1use std::{error::Error, fmt};
2
3pub const NCS_HEADER: &str = "NCS V1.0";
5pub const NCS_BINARY_HEADER_SIZE: usize = 13;
7pub const NCS_OPERATION_BASE_SIZE: usize = 2;
9pub const NCS_OPCODE_OFFSET: usize = 0;
11pub const NCS_AUXCODE_OFFSET: usize = 1;
13pub const NCS_EXTRA_DATA_OFFSET: usize = 2;
15
16#[derive(#[automatically_derived]
impl ::core::fmt::Debug for NcsHeader {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field1_finish(f, "NcsHeader",
"code_size", &&self.code_size)
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for NcsHeader {
#[inline]
fn clone(&self) -> NcsHeader {
let _: ::core::clone::AssertParamIsClone<u32>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for NcsHeader { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for NcsHeader {
#[inline]
fn eq(&self, other: &NcsHeader) -> bool {
self.code_size == other.code_size
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for NcsHeader {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<u32>;
}
}Eq, #[automatically_derived]
impl ::core::hash::Hash for NcsHeader {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&self.code_size, state)
}
}Hash)]
18pub struct NcsHeader {
19 pub code_size: u32,
21}
22
23#[derive(#[automatically_derived]
impl ::core::fmt::Debug for NcsHeaderError {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
NcsHeaderError::TooShort(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"TooShort", &__self_0),
NcsHeaderError::InvalidMagic =>
::core::fmt::Formatter::write_str(f, "InvalidMagic"),
NcsHeaderError::InvalidMarker(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"InvalidMarker", &__self_0),
}
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for NcsHeaderError {
#[inline]
fn clone(&self) -> NcsHeaderError {
match self {
NcsHeaderError::TooShort(__self_0) =>
NcsHeaderError::TooShort(::core::clone::Clone::clone(__self_0)),
NcsHeaderError::InvalidMagic => NcsHeaderError::InvalidMagic,
NcsHeaderError::InvalidMarker(__self_0) =>
NcsHeaderError::InvalidMarker(::core::clone::Clone::clone(__self_0)),
}
}
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for NcsHeaderError {
#[inline]
fn eq(&self, other: &NcsHeaderError) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr &&
match (self, other) {
(NcsHeaderError::TooShort(__self_0),
NcsHeaderError::TooShort(__arg1_0)) => __self_0 == __arg1_0,
(NcsHeaderError::InvalidMarker(__self_0),
NcsHeaderError::InvalidMarker(__arg1_0)) =>
__self_0 == __arg1_0,
_ => true,
}
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for NcsHeaderError {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<usize>;
let _: ::core::cmp::AssertParamIsEq<u8>;
}
}Eq)]
25pub enum NcsHeaderError {
26 TooShort(usize),
28 InvalidMagic,
30 InvalidMarker(u8),
32}
33
34impl fmt::Display for NcsHeaderError {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 match self {
37 Self::TooShort(len) => f.write_fmt(format_args!("NCS header too short: expected 13 bytes, got {0}",
len))write!(f, "NCS header too short: expected 13 bytes, got {len}"),
38 Self::InvalidMagic => f.write_str("invalid NCS header magic"),
39 Self::InvalidMarker(marker) => f.write_fmt(format_args!("invalid NCS binary marker: {0:#04x}", marker))write!(f, "invalid NCS binary marker: {marker:#04x}"),
40 }
41 }
42}
43
44impl Error for NcsHeaderError {}
45
46pub fn decode_ncs_header(bytes: &[u8]) -> Result<NcsHeader, NcsHeaderError> {
53 if bytes.len() < NCS_BINARY_HEADER_SIZE {
54 return Err(NcsHeaderError::TooShort(bytes.len()));
55 }
56 if bytes.get(..NCS_HEADER.len()) != Some(NCS_HEADER.as_bytes()) {
57 return Err(NcsHeaderError::InvalidMagic);
58 }
59 let Some(marker) = bytes.get(8).copied() else {
60 return Err(NcsHeaderError::TooShort(bytes.len()));
61 };
62 if marker != b'B' {
63 return Err(NcsHeaderError::InvalidMarker(marker));
64 }
65 let Some(code_size_bytes) = bytes.get(9..13) else {
66 return Err(NcsHeaderError::TooShort(bytes.len()));
67 };
68
69 let code_size = <[u8; 4]>::try_from(code_size_bytes)
70 .map(u32::from_be_bytes)
71 .map_err(|_error| NcsHeaderError::TooShort(bytes.len()))?;
72 Ok(NcsHeader {
73 code_size,
74 })
75}
76
77#[derive(#[automatically_derived]
impl ::core::fmt::Debug for NcsInstruction {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field3_finish(f,
"NcsInstruction", "opcode", &self.opcode, "auxcode",
&self.auxcode, "extra", &&self.extra)
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for NcsInstruction {
#[inline]
fn clone(&self) -> NcsInstruction {
NcsInstruction {
opcode: ::core::clone::Clone::clone(&self.opcode),
auxcode: ::core::clone::Clone::clone(&self.auxcode),
extra: ::core::clone::Clone::clone(&self.extra),
}
}
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for NcsInstruction {
#[inline]
fn eq(&self, other: &NcsInstruction) -> bool {
self.opcode == other.opcode && self.auxcode == other.auxcode &&
self.extra == other.extra
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for NcsInstruction {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<NcsOpcode>;
let _: ::core::cmp::AssertParamIsEq<NcsAuxCode>;
let _: ::core::cmp::AssertParamIsEq<Vec<u8>>;
}
}Eq, #[automatically_derived]
impl ::core::hash::Hash for NcsInstruction {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&self.opcode, state);
::core::hash::Hash::hash(&self.auxcode, state);
::core::hash::Hash::hash(&self.extra, state)
}
}Hash)]
79pub struct NcsInstruction {
80 pub opcode: NcsOpcode,
82 pub auxcode: NcsAuxCode,
84 pub extra: Vec<u8>,
86}
87
88impl NcsInstruction {
89 #[must_use]
91 pub fn encoded_len(&self) -> usize {
92 NCS_OPERATION_BASE_SIZE + self.extra.len()
93 }
94}
95
96#[derive(#[automatically_derived]
impl ::core::fmt::Debug for NcsOpcode {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
NcsOpcode::Assignment => "Assignment",
NcsOpcode::RunstackAdd => "RunstackAdd",
NcsOpcode::RunstackCopy => "RunstackCopy",
NcsOpcode::Constant => "Constant",
NcsOpcode::ExecuteCommand => "ExecuteCommand",
NcsOpcode::LogicalAnd => "LogicalAnd",
NcsOpcode::LogicalOr => "LogicalOr",
NcsOpcode::InclusiveOr => "InclusiveOr",
NcsOpcode::ExclusiveOr => "ExclusiveOr",
NcsOpcode::BooleanAnd => "BooleanAnd",
NcsOpcode::Equal => "Equal",
NcsOpcode::NotEqual => "NotEqual",
NcsOpcode::Geq => "Geq",
NcsOpcode::Gt => "Gt",
NcsOpcode::Lt => "Lt",
NcsOpcode::Leq => "Leq",
NcsOpcode::ShiftLeft => "ShiftLeft",
NcsOpcode::ShiftRight => "ShiftRight",
NcsOpcode::UShiftRight => "UShiftRight",
NcsOpcode::Add => "Add",
NcsOpcode::Sub => "Sub",
NcsOpcode::Mul => "Mul",
NcsOpcode::Div => "Div",
NcsOpcode::Modulus => "Modulus",
NcsOpcode::Negation => "Negation",
NcsOpcode::OnesComplement => "OnesComplement",
NcsOpcode::ModifyStackPointer => "ModifyStackPointer",
NcsOpcode::StoreIp => "StoreIp",
NcsOpcode::Jmp => "Jmp",
NcsOpcode::Jsr => "Jsr",
NcsOpcode::Jz => "Jz",
NcsOpcode::Ret => "Ret",
NcsOpcode::DeStruct => "DeStruct",
NcsOpcode::BooleanNot => "BooleanNot",
NcsOpcode::Decrement => "Decrement",
NcsOpcode::Increment => "Increment",
NcsOpcode::Jnz => "Jnz",
NcsOpcode::AssignmentBase => "AssignmentBase",
NcsOpcode::RunstackCopyBase => "RunstackCopyBase",
NcsOpcode::DecrementBase => "DecrementBase",
NcsOpcode::IncrementBase => "IncrementBase",
NcsOpcode::SaveBasePointer => "SaveBasePointer",
NcsOpcode::RestoreBasePointer => "RestoreBasePointer",
NcsOpcode::StoreState => "StoreState",
NcsOpcode::NoOperation => "NoOperation",
})
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for NcsOpcode {
#[inline]
fn clone(&self) -> NcsOpcode { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for NcsOpcode { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for NcsOpcode {
#[inline]
fn eq(&self, other: &NcsOpcode) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for NcsOpcode {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {}
}Eq, #[automatically_derived]
impl ::core::hash::Hash for NcsOpcode {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
let __self_discr = ::core::intrinsics::discriminant_value(self);
::core::hash::Hash::hash(&__self_discr, state)
}
}Hash)]
98#[repr(u8)]
99pub enum NcsOpcode {
100 Assignment = 0x01,
102 RunstackAdd = 0x02,
104 RunstackCopy = 0x03,
106 Constant = 0x04,
108 ExecuteCommand = 0x05,
110 LogicalAnd = 0x06,
112 LogicalOr = 0x07,
114 InclusiveOr = 0x08,
116 ExclusiveOr = 0x09,
118 BooleanAnd = 0x0a,
120 Equal = 0x0b,
122 NotEqual = 0x0c,
124 Geq = 0x0d,
126 Gt = 0x0e,
128 Lt = 0x0f,
130 Leq = 0x10,
132 ShiftLeft = 0x11,
134 ShiftRight = 0x12,
136 UShiftRight = 0x13,
138 Add = 0x14,
140 Sub = 0x15,
142 Mul = 0x16,
144 Div = 0x17,
146 Modulus = 0x18,
148 Negation = 0x19,
150 OnesComplement = 0x1a,
152 ModifyStackPointer = 0x1b,
154 StoreIp = 0x1c,
156 Jmp = 0x1d,
158 Jsr = 0x1e,
160 Jz = 0x1f,
162 Ret = 0x20,
164 DeStruct = 0x21,
166 BooleanNot = 0x22,
168 Decrement = 0x23,
170 Increment = 0x24,
172 Jnz = 0x25,
174 AssignmentBase = 0x26,
176 RunstackCopyBase = 0x27,
178 DecrementBase = 0x28,
180 IncrementBase = 0x29,
182 SaveBasePointer = 0x2a,
184 RestoreBasePointer = 0x2b,
186 StoreState = 0x2c,
188 NoOperation = 0x2d,
190}
191
192impl NcsOpcode {
193 #[must_use]
195 pub fn canonical_name(self) -> &'static str {
196 match self {
197 Self::Assignment => "CPDOWNSP",
198 Self::RunstackAdd => "RSADD",
199 Self::RunstackCopy => "CPTOPSP",
200 Self::Constant => "CONST",
201 Self::ExecuteCommand => "ACTION",
202 Self::LogicalAnd => "LOGAND",
203 Self::LogicalOr => "LOGOR",
204 Self::InclusiveOr => "INCOR",
205 Self::ExclusiveOr => "EXCOR",
206 Self::BooleanAnd => "BOOLAND",
207 Self::Equal => "EQUAL",
208 Self::NotEqual => "NEQUAL",
209 Self::Geq => "GEQ",
210 Self::Gt => "GT",
211 Self::Lt => "LT",
212 Self::Leq => "LEQ",
213 Self::ShiftLeft => "SHLEFT",
214 Self::ShiftRight => "SHRIGHT",
215 Self::UShiftRight => "USHRIGHT",
216 Self::Add => "ADD",
217 Self::Sub => "SUB",
218 Self::Mul => "MUL",
219 Self::Div => "DIV",
220 Self::Modulus => "MOD",
221 Self::Negation => "NEG",
222 Self::OnesComplement => "COMP",
223 Self::ModifyStackPointer => "MOVSP",
224 Self::StoreIp => "STOREIP",
225 Self::Jmp => "JMP",
226 Self::Jsr => "JSR",
227 Self::Jz => "JZ",
228 Self::Ret => "RET",
229 Self::DeStruct => "DESTRUCT",
230 Self::BooleanNot => "NOT",
231 Self::Decrement => "DECSP",
232 Self::Increment => "INCSP",
233 Self::Jnz => "JNZ",
234 Self::AssignmentBase => "CPDOWNBP",
235 Self::RunstackCopyBase => "CPTOPBP",
236 Self::DecrementBase => "DECBP",
237 Self::IncrementBase => "INCBP",
238 Self::SaveBasePointer => "SAVEBP",
239 Self::RestoreBasePointer => "RESTOREBP",
240 Self::StoreState => "STORESTATE",
241 Self::NoOperation => "NOP",
242 }
243 }
244}
245
246impl fmt::Display for NcsOpcode {
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248 f.write_str(self.canonical_name())
249 }
250}
251
252#[derive(#[automatically_derived]
impl ::core::fmt::Debug for NcsAuxCode {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
NcsAuxCode::None => "None",
NcsAuxCode::TypeVoid => "TypeVoid",
NcsAuxCode::TypeCommand => "TypeCommand",
NcsAuxCode::TypeInteger => "TypeInteger",
NcsAuxCode::TypeFloat => "TypeFloat",
NcsAuxCode::TypeString => "TypeString",
NcsAuxCode::TypeObject => "TypeObject",
NcsAuxCode::TypeEngst0 => "TypeEngst0",
NcsAuxCode::TypeEngst1 => "TypeEngst1",
NcsAuxCode::TypeEngst2 => "TypeEngst2",
NcsAuxCode::TypeEngst3 => "TypeEngst3",
NcsAuxCode::TypeEngst4 => "TypeEngst4",
NcsAuxCode::TypeEngst5 => "TypeEngst5",
NcsAuxCode::TypeEngst6 => "TypeEngst6",
NcsAuxCode::TypeEngst7 => "TypeEngst7",
NcsAuxCode::TypeEngst8 => "TypeEngst8",
NcsAuxCode::TypeEngst9 => "TypeEngst9",
NcsAuxCode::TypeTypeIntegerInteger =>
"TypeTypeIntegerInteger",
NcsAuxCode::TypeTypeFloatFloat => "TypeTypeFloatFloat",
NcsAuxCode::TypeTypeObjectObject => "TypeTypeObjectObject",
NcsAuxCode::TypeTypeStringString => "TypeTypeStringString",
NcsAuxCode::TypeTypeStructStruct => "TypeTypeStructStruct",
NcsAuxCode::TypeTypeIntegerFloat => "TypeTypeIntegerFloat",
NcsAuxCode::TypeTypeFloatInteger => "TypeTypeFloatInteger",
NcsAuxCode::TypeTypeEngst0Engst0 => "TypeTypeEngst0Engst0",
NcsAuxCode::TypeTypeEngst1Engst1 => "TypeTypeEngst1Engst1",
NcsAuxCode::TypeTypeEngst2Engst2 => "TypeTypeEngst2Engst2",
NcsAuxCode::TypeTypeEngst3Engst3 => "TypeTypeEngst3Engst3",
NcsAuxCode::TypeTypeEngst4Engst4 => "TypeTypeEngst4Engst4",
NcsAuxCode::TypeTypeEngst5Engst5 => "TypeTypeEngst5Engst5",
NcsAuxCode::TypeTypeEngst6Engst6 => "TypeTypeEngst6Engst6",
NcsAuxCode::TypeTypeEngst7Engst7 => "TypeTypeEngst7Engst7",
NcsAuxCode::TypeTypeEngst8Engst8 => "TypeTypeEngst8Engst8",
NcsAuxCode::TypeTypeEngst9Engst9 => "TypeTypeEngst9Engst9",
NcsAuxCode::TypeTypeVectorVector => "TypeTypeVectorVector",
NcsAuxCode::TypeTypeVectorFloat => "TypeTypeVectorFloat",
NcsAuxCode::TypeTypeFloatVector => "TypeTypeFloatVector",
NcsAuxCode::EvalInplace => "EvalInplace",
NcsAuxCode::EvalPostplace => "EvalPostplace",
})
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for NcsAuxCode {
#[inline]
fn clone(&self) -> NcsAuxCode { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for NcsAuxCode { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for NcsAuxCode {
#[inline]
fn eq(&self, other: &NcsAuxCode) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for NcsAuxCode {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {}
}Eq, #[automatically_derived]
impl ::core::hash::Hash for NcsAuxCode {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
let __self_discr = ::core::intrinsics::discriminant_value(self);
::core::hash::Hash::hash(&__self_discr, state)
}
}Hash)]
254#[repr(u8)]
255pub enum NcsAuxCode {
256 None = 0x00,
258 TypeVoid = 0x01,
260 TypeCommand = 0x02,
262 TypeInteger = 0x03,
264 TypeFloat = 0x04,
266 TypeString = 0x05,
268 TypeObject = 0x06,
270 TypeEngst0 = 0x10,
272 TypeEngst1 = 0x11,
274 TypeEngst2 = 0x12,
276 TypeEngst3 = 0x13,
278 TypeEngst4 = 0x14,
280 TypeEngst5 = 0x15,
282 TypeEngst6 = 0x16,
284 TypeEngst7 = 0x17,
286 TypeEngst8 = 0x18,
288 TypeEngst9 = 0x19,
290 TypeTypeIntegerInteger = 0x20,
292 TypeTypeFloatFloat = 0x21,
294 TypeTypeObjectObject = 0x22,
296 TypeTypeStringString = 0x23,
298 TypeTypeStructStruct = 0x24,
300 TypeTypeIntegerFloat = 0x25,
302 TypeTypeFloatInteger = 0x26,
304 TypeTypeEngst0Engst0 = 0x30,
306 TypeTypeEngst1Engst1 = 0x31,
308 TypeTypeEngst2Engst2 = 0x32,
310 TypeTypeEngst3Engst3 = 0x33,
312 TypeTypeEngst4Engst4 = 0x34,
314 TypeTypeEngst5Engst5 = 0x35,
316 TypeTypeEngst6Engst6 = 0x36,
318 TypeTypeEngst7Engst7 = 0x37,
320 TypeTypeEngst8Engst8 = 0x38,
322 TypeTypeEngst9Engst9 = 0x39,
324 TypeTypeVectorVector = 0x3a,
326 TypeTypeVectorFloat = 0x3b,
328 TypeTypeFloatVector = 0x3c,
330 EvalInplace = 0x70,
332 EvalPostplace = 0x71,
334}
335
336impl NcsAuxCode {
337 #[must_use]
340 pub fn canonical_name(self) -> Option<&'static str> {
341 match self {
342 Self::None
343 | Self::TypeVoid
344 | Self::TypeCommand
345 | Self::EvalInplace
346 | Self::EvalPostplace => None,
347 Self::TypeInteger => Some("I"),
348 Self::TypeFloat => Some("F"),
349 Self::TypeString => Some("S"),
350 Self::TypeObject => Some("O"),
351 Self::TypeEngst0 => Some("E0"),
352 Self::TypeEngst1 => Some("E1"),
353 Self::TypeEngst2 => Some("E2"),
354 Self::TypeEngst3 => Some("E3"),
355 Self::TypeEngst4 => Some("E4"),
356 Self::TypeEngst5 => Some("E5"),
357 Self::TypeEngst6 => Some("E6"),
358 Self::TypeEngst7 => Some("E7"),
359 Self::TypeEngst8 => Some("E8"),
360 Self::TypeEngst9 => Some("E9"),
361 Self::TypeTypeIntegerInteger => Some("II"),
362 Self::TypeTypeFloatFloat => Some("FF"),
363 Self::TypeTypeObjectObject => Some("OO"),
364 Self::TypeTypeStringString => Some("SS"),
365 Self::TypeTypeStructStruct => Some("TT"),
366 Self::TypeTypeIntegerFloat => Some("IF"),
367 Self::TypeTypeFloatInteger => Some("FI"),
368 Self::TypeTypeEngst0Engst0 => Some("E0E0"),
369 Self::TypeTypeEngst1Engst1 => Some("E1E1"),
370 Self::TypeTypeEngst2Engst2 => Some("E2E2"),
371 Self::TypeTypeEngst3Engst3 => Some("E3E3"),
372 Self::TypeTypeEngst4Engst4 => Some("E4E4"),
373 Self::TypeTypeEngst5Engst5 => Some("E5E5"),
374 Self::TypeTypeEngst6Engst6 => Some("E6E6"),
375 Self::TypeTypeEngst7Engst7 => Some("E7E7"),
376 Self::TypeTypeEngst8Engst8 => Some("E8E8"),
377 Self::TypeTypeEngst9Engst9 => Some("E9E9"),
378 Self::TypeTypeVectorVector => Some("VV"),
379 Self::TypeTypeVectorFloat => Some("VF"),
380 Self::TypeTypeFloatVector => Some("FV"),
381 }
382 }
383}
384
385impl fmt::Display for NcsAuxCode {
386 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
387 if let Some(name) = self.canonical_name() {
388 f.write_str(name)
389 } else {
390 f.write_fmt(format_args!("{0:#04x}", *self as u8))write!(f, "{:#04x}", *self as u8)
391 }
392 }
393}
394
395#[derive(#[automatically_derived]
impl ::core::fmt::Debug for UnknownNcsOpcode {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"UnknownNcsOpcode", &&self.0)
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for UnknownNcsOpcode {
#[inline]
fn clone(&self) -> UnknownNcsOpcode {
let _: ::core::clone::AssertParamIsClone<u8>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for UnknownNcsOpcode { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for UnknownNcsOpcode {
#[inline]
fn eq(&self, other: &UnknownNcsOpcode) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for UnknownNcsOpcode {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<u8>;
}
}Eq)]
397pub struct UnknownNcsOpcode(pub u8);
398
399impl fmt::Display for UnknownNcsOpcode {
400 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
401 f.write_fmt(format_args!("unknown NCS opcode: {0:#04x}", self.0))write!(f, "unknown NCS opcode: {:#04x}", self.0)
402 }
403}
404
405impl Error for UnknownNcsOpcode {}
406
407#[derive(#[automatically_derived]
impl ::core::fmt::Debug for UnknownNcsAuxCode {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"UnknownNcsAuxCode", &&self.0)
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for UnknownNcsAuxCode {
#[inline]
fn clone(&self) -> UnknownNcsAuxCode {
let _: ::core::clone::AssertParamIsClone<u8>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for UnknownNcsAuxCode { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for UnknownNcsAuxCode {
#[inline]
fn eq(&self, other: &UnknownNcsAuxCode) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for UnknownNcsAuxCode {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<u8>;
}
}Eq)]
409pub struct UnknownNcsAuxCode(pub u8);
410
411impl fmt::Display for UnknownNcsAuxCode {
412 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
413 f.write_fmt(format_args!("unknown NCS aux code: {0:#04x}", self.0))write!(f, "unknown NCS aux code: {:#04x}", self.0)
414 }
415}
416
417impl Error for UnknownNcsAuxCode {}
418
419#[derive(#[automatically_derived]
impl ::core::fmt::Debug for NcsReadError {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
NcsReadError::Header(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Header",
&__self_0),
NcsReadError::Opcode(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Opcode",
&__self_0),
NcsReadError::AuxCode(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"AuxCode", &__self_0),
NcsReadError::TruncatedInstruction {
offset: __self_0,
expected_extra: __self_1,
actual_extra: __self_2 } =>
::core::fmt::Formatter::debug_struct_field3_finish(f,
"TruncatedInstruction", "offset", __self_0,
"expected_extra", __self_1, "actual_extra", &__self_2),
}
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for NcsReadError {
#[inline]
fn clone(&self) -> NcsReadError {
match self {
NcsReadError::Header(__self_0) =>
NcsReadError::Header(::core::clone::Clone::clone(__self_0)),
NcsReadError::Opcode(__self_0) =>
NcsReadError::Opcode(::core::clone::Clone::clone(__self_0)),
NcsReadError::AuxCode(__self_0) =>
NcsReadError::AuxCode(::core::clone::Clone::clone(__self_0)),
NcsReadError::TruncatedInstruction {
offset: __self_0,
expected_extra: __self_1,
actual_extra: __self_2 } =>
NcsReadError::TruncatedInstruction {
offset: ::core::clone::Clone::clone(__self_0),
expected_extra: ::core::clone::Clone::clone(__self_1),
actual_extra: ::core::clone::Clone::clone(__self_2),
},
}
}
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for NcsReadError {
#[inline]
fn eq(&self, other: &NcsReadError) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr &&
match (self, other) {
(NcsReadError::Header(__self_0),
NcsReadError::Header(__arg1_0)) => __self_0 == __arg1_0,
(NcsReadError::Opcode(__self_0),
NcsReadError::Opcode(__arg1_0)) => __self_0 == __arg1_0,
(NcsReadError::AuxCode(__self_0),
NcsReadError::AuxCode(__arg1_0)) => __self_0 == __arg1_0,
(NcsReadError::TruncatedInstruction {
offset: __self_0,
expected_extra: __self_1,
actual_extra: __self_2 },
NcsReadError::TruncatedInstruction {
offset: __arg1_0,
expected_extra: __arg1_1,
actual_extra: __arg1_2 }) =>
__self_0 == __arg1_0 && __self_1 == __arg1_1 &&
__self_2 == __arg1_2,
_ => unsafe { ::core::intrinsics::unreachable() }
}
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for NcsReadError {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<NcsHeaderError>;
let _: ::core::cmp::AssertParamIsEq<UnknownNcsOpcode>;
let _: ::core::cmp::AssertParamIsEq<UnknownNcsAuxCode>;
let _: ::core::cmp::AssertParamIsEq<usize>;
}
}Eq)]
421pub enum NcsReadError {
422 Header(NcsHeaderError),
424 Opcode(UnknownNcsOpcode),
426 AuxCode(UnknownNcsAuxCode),
428 TruncatedInstruction {
430 offset: usize,
432 expected_extra: usize,
434 actual_extra: usize,
436 },
437}
438
439impl fmt::Display for NcsReadError {
440 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
441 match self {
442 Self::Header(error) => error.fmt(f),
443 Self::Opcode(error) => error.fmt(f),
444 Self::AuxCode(error) => error.fmt(f),
445 Self::TruncatedInstruction {
446 offset,
447 expected_extra,
448 actual_extra,
449 } => f.write_fmt(format_args!("truncated NCS instruction at byte {0}: expected {1} payload bytes, got {2}",
offset, expected_extra, actual_extra))write!(
450 f,
451 "truncated NCS instruction at byte {offset}: expected {expected_extra} payload \
452 bytes, got {actual_extra}"
453 ),
454 }
455 }
456}
457
458impl Error for NcsReadError {}
459
460impl From<NcsHeaderError> for NcsReadError {
461 fn from(value: NcsHeaderError) -> Self {
462 Self::Header(value)
463 }
464}
465
466impl From<UnknownNcsOpcode> for NcsReadError {
467 fn from(value: UnknownNcsOpcode) -> Self {
468 Self::Opcode(value)
469 }
470}
471
472impl From<UnknownNcsAuxCode> for NcsReadError {
473 fn from(value: UnknownNcsAuxCode) -> Self {
474 Self::AuxCode(value)
475 }
476}
477
478impl TryFrom<u8> for NcsOpcode {
479 type Error = UnknownNcsOpcode;
480
481 fn try_from(value: u8) -> Result<Self, Self::Error> {
482 let opcode = match value {
483 0x01 => Self::Assignment,
484 0x02 => Self::RunstackAdd,
485 0x03 => Self::RunstackCopy,
486 0x04 => Self::Constant,
487 0x05 => Self::ExecuteCommand,
488 0x06 => Self::LogicalAnd,
489 0x07 => Self::LogicalOr,
490 0x08 => Self::InclusiveOr,
491 0x09 => Self::ExclusiveOr,
492 0x0a => Self::BooleanAnd,
493 0x0b => Self::Equal,
494 0x0c => Self::NotEqual,
495 0x0d => Self::Geq,
496 0x0e => Self::Gt,
497 0x0f => Self::Lt,
498 0x10 => Self::Leq,
499 0x11 => Self::ShiftLeft,
500 0x12 => Self::ShiftRight,
501 0x13 => Self::UShiftRight,
502 0x14 => Self::Add,
503 0x15 => Self::Sub,
504 0x16 => Self::Mul,
505 0x17 => Self::Div,
506 0x18 => Self::Modulus,
507 0x19 => Self::Negation,
508 0x1a => Self::OnesComplement,
509 0x1b => Self::ModifyStackPointer,
510 0x1c => Self::StoreIp,
511 0x1d => Self::Jmp,
512 0x1e => Self::Jsr,
513 0x1f => Self::Jz,
514 0x20 => Self::Ret,
515 0x21 => Self::DeStruct,
516 0x22 => Self::BooleanNot,
517 0x23 => Self::Decrement,
518 0x24 => Self::Increment,
519 0x25 => Self::Jnz,
520 0x26 => Self::AssignmentBase,
521 0x27 => Self::RunstackCopyBase,
522 0x28 => Self::DecrementBase,
523 0x29 => Self::IncrementBase,
524 0x2a => Self::SaveBasePointer,
525 0x2b => Self::RestoreBasePointer,
526 0x2c => Self::StoreState,
527 0x2d => Self::NoOperation,
528 _ => return Err(UnknownNcsOpcode(value)),
529 };
530 Ok(opcode)
531 }
532}
533
534impl TryFrom<u8> for NcsAuxCode {
535 type Error = UnknownNcsAuxCode;
536
537 fn try_from(value: u8) -> Result<Self, Self::Error> {
538 let aux = match value {
539 0x00 => Self::None,
540 0x01 => Self::TypeVoid,
541 0x02 => Self::TypeCommand,
542 0x03 => Self::TypeInteger,
543 0x04 => Self::TypeFloat,
544 0x05 => Self::TypeString,
545 0x06 => Self::TypeObject,
546 0x10 => Self::TypeEngst0,
547 0x11 => Self::TypeEngst1,
548 0x12 => Self::TypeEngst2,
549 0x13 => Self::TypeEngst3,
550 0x14 => Self::TypeEngst4,
551 0x15 => Self::TypeEngst5,
552 0x16 => Self::TypeEngst6,
553 0x17 => Self::TypeEngst7,
554 0x18 => Self::TypeEngst8,
555 0x19 => Self::TypeEngst9,
556 0x20 => Self::TypeTypeIntegerInteger,
557 0x21 => Self::TypeTypeFloatFloat,
558 0x22 => Self::TypeTypeObjectObject,
559 0x23 => Self::TypeTypeStringString,
560 0x24 => Self::TypeTypeStructStruct,
561 0x25 => Self::TypeTypeIntegerFloat,
562 0x26 => Self::TypeTypeFloatInteger,
563 0x30 => Self::TypeTypeEngst0Engst0,
564 0x31 => Self::TypeTypeEngst1Engst1,
565 0x32 => Self::TypeTypeEngst2Engst2,
566 0x33 => Self::TypeTypeEngst3Engst3,
567 0x34 => Self::TypeTypeEngst4Engst4,
568 0x35 => Self::TypeTypeEngst5Engst5,
569 0x36 => Self::TypeTypeEngst6Engst6,
570 0x37 => Self::TypeTypeEngst7Engst7,
571 0x38 => Self::TypeTypeEngst8Engst8,
572 0x39 => Self::TypeTypeEngst9Engst9,
573 0x3a => Self::TypeTypeVectorVector,
574 0x3b => Self::TypeTypeVectorFloat,
575 0x3c => Self::TypeTypeFloatVector,
576 0x70 => Self::EvalInplace,
577 0x71 => Self::EvalPostplace,
578 _ => return Err(UnknownNcsAuxCode(value)),
579 };
580 Ok(aux)
581 }
582}
583
584fn instruction_extra_size(opcode: NcsOpcode, auxcode: NcsAuxCode, bytes: &[u8]) -> usize {
585 match opcode {
586 NcsOpcode::Constant => match auxcode {
587 NcsAuxCode::TypeInteger
588 | NcsAuxCode::TypeFloat
589 | NcsAuxCode::TypeObject
590 | NcsAuxCode::TypeEngst2 => 4,
591 NcsAuxCode::TypeString | NcsAuxCode::TypeEngst7 => {
592 match bytes
593 .get(..2)
594 .and_then(|prefix| <[u8; 2]>::try_from(prefix).ok())
595 {
596 Some(prefix) => 2 + usize::from(u16::from_be_bytes(prefix)),
597 None => 2,
598 }
599 }
600 _ => 0,
601 },
602 NcsOpcode::Jmp
603 | NcsOpcode::Jsr
604 | NcsOpcode::Jz
605 | NcsOpcode::Jnz
606 | NcsOpcode::ModifyStackPointer
607 | NcsOpcode::Decrement
608 | NcsOpcode::Increment
609 | NcsOpcode::DecrementBase
610 | NcsOpcode::IncrementBase => 4,
611 NcsOpcode::StoreState => 8,
612 NcsOpcode::ExecuteCommand => 3,
613 NcsOpcode::RunstackCopy
614 | NcsOpcode::RunstackCopyBase
615 | NcsOpcode::Assignment
616 | NcsOpcode::AssignmentBase
617 | NcsOpcode::DeStruct => 6,
618 NcsOpcode::Equal | NcsOpcode::NotEqual if auxcode == NcsAuxCode::TypeTypeStructStruct => 2,
619 _ => 0,
620 }
621}
622
623pub fn decode_ncs_instructions(bytes: &[u8]) -> Result<Vec<NcsInstruction>, NcsReadError> {
630 let header = decode_ncs_header(bytes)?;
631 let mut offset = NCS_BINARY_HEADER_SIZE;
632 let code_end = NCS_BINARY_HEADER_SIZE + header.code_size as usize;
633 if bytes.len() < code_end {
634 return Err(NcsReadError::TruncatedInstruction {
635 offset,
636 expected_extra: header.code_size as usize,
637 actual_extra: bytes.len().saturating_sub(offset),
638 });
639 }
640
641 let mut instructions = Vec::new();
642 while offset < code_end {
643 let opcode = NcsOpcode::try_from(*bytes.get(offset).ok_or(
644 NcsReadError::TruncatedInstruction {
645 offset,
646 expected_extra: 1,
647 actual_extra: bytes.len().saturating_sub(offset),
648 },
649 )?)?;
650 let auxcode = NcsAuxCode::try_from(*bytes.get(offset + 1).ok_or(
651 NcsReadError::TruncatedInstruction {
652 offset,
653 expected_extra: 2,
654 actual_extra: bytes.len().saturating_sub(offset),
655 },
656 )?)?;
657 let extra_window = bytes.get(offset + 2..code_end).unwrap_or(&[]);
658 let extra_size = instruction_extra_size(opcode, auxcode, extra_window);
659 let remaining = code_end.saturating_sub(offset + 2);
660 if remaining < extra_size {
661 return Err(NcsReadError::TruncatedInstruction {
662 offset,
663 expected_extra: extra_size,
664 actual_extra: remaining,
665 });
666 }
667 let extra = bytes
668 .get(offset + 2..offset + 2 + extra_size)
669 .unwrap_or(&[])
670 .to_vec();
671 instructions.push(NcsInstruction {
672 opcode,
673 auxcode,
674 extra,
675 });
676 offset += NCS_OPERATION_BASE_SIZE + extra_size;
677 }
678
679 Ok(instructions)
680}
681
682pub fn encode_ncs_instructions(instructions: &[NcsInstruction]) -> Vec<u8> {
684 let code_size = u32::try_from(
685 instructions
686 .iter()
687 .map(NcsInstruction::encoded_len)
688 .sum::<usize>(),
689 )
690 .ok()
691 .unwrap_or(u32::MAX);
692 let mut bytes = Vec::with_capacity(
693 NCS_BINARY_HEADER_SIZE + usize::try_from(code_size).ok().unwrap_or(usize::MAX),
694 );
695 bytes.extend_from_slice(NCS_HEADER.as_bytes());
696 bytes.push(b'B');
697 bytes.extend_from_slice(&code_size.to_be_bytes());
698 for instruction in instructions {
699 bytes.push(instruction.opcode as u8);
700 bytes.push(instruction.auxcode as u8);
701 bytes.extend_from_slice(&instruction.extra);
702 }
703 bytes
704}
705
706#[cfg(test)]
707mod tests {
708 use super::*;
709
710 #[test]
711 fn decode_header_accepts_valid_prefix() -> Result<(), Box<dyn std::error::Error>> {
712 let bytes = [
713 b'N', b'C', b'S', b' ', b'V', b'1', b'.', b'0', b'B', 0x00, 0x00, 0x00, 0x2a,
714 ];
715 let header = decode_ncs_header(&bytes)?;
716 assert_eq!(header.code_size, 42);
717 Ok(())
718 }
719
720 #[test]
721 fn decode_header_rejects_bad_marker() {
722 let bytes = [
723 b'N', b'C', b'S', b' ', b'V', b'1', b'.', b'0', b'X', 0x00, 0x00, 0x00, 0x2a,
724 ];
725 assert_eq!(
726 decode_ncs_header(&bytes),
727 Err(NcsHeaderError::InvalidMarker(b'X'))
728 );
729 }
730
731 #[test]
732 fn encode_and_decode_roundtrip_instruction_stream() -> Result<(), Box<dyn std::error::Error>> {
733 let instructions = vec![
734 NcsInstruction {
735 opcode: NcsOpcode::Constant,
736 auxcode: NcsAuxCode::TypeInteger,
737 extra: 42_i32.to_be_bytes().to_vec(),
738 },
739 NcsInstruction {
740 opcode: NcsOpcode::Ret,
741 auxcode: NcsAuxCode::None,
742 extra: Vec::new(),
743 },
744 ];
745
746 let bytes = encode_ncs_instructions(&instructions);
747 let decoded = decode_ncs_instructions(&bytes)?;
748 assert_eq!(decoded, instructions);
749 Ok(())
750 }
751}