diff --git a/dev/src/SmartC/__tests__/assembly.a.spec.ts b/dev/src/SmartC/__tests__/assembly.a.spec.ts
index 8e2a4c1..04594c5 100644
--- a/dev/src/SmartC/__tests__/assembly.a.spec.ts
+++ b/dev/src/SmartC/__tests__/assembly.a.spec.ts
@@ -127,6 +127,14 @@ describe('Assembly compilation:', () => {
const result = new SmartC({ language: 'Assembly', sourceCode: code }).compile().getMachineCode()
expect(result.MachineCodeHashId).toBe('17223659044509638052')
})
+ it('should compile: atv3 opCodes and apiCodes', () => {
+ const code = '^comment new opCodes SIP-37\nSLP\nPOW @base $exp\nMDV @x $y $den\n^comment new APICodes SIP-37\nFUN @ret Check_Sig_B_With_A\nFUN @ret Get_Code_Hash_Id\nFUN @ret Get_Activation_Fee\nFUN Put_Last_Block_GSig_In_A\n^comment new APICodes SIP-38\nFUN @ret Get_Map_Value_Keys_In_A\nFUN Set_Map_Value_Keys_In_A\n^comment new APICodes SIP-39\nFUN @ret Issue_Asset\nFUN Mint_Asset\nFUN Distribute_To_Asset_Holders\nFUN @ret Get_Asset_Holders_Count\n'
+ const MachineCode = '2a1900000000010000002c02000000030000000400000035060205000000350c0305000000350d0405000000320e043507040500000032080435090405000000320a04320b04350c0405000000'
+ const MachineData = ''
+ const result = new SmartC({ language: 'Assembly', sourceCode: code }).compile().getMachineCode()
+ expect(result.ByteCode).toBe(MachineCode)
+ expect(result.ByteData).toBe(MachineData)
+ })
})
describe('Assembly wrong code', () => {
diff --git a/dev/src/SmartC/assembler/assembler.ts b/dev/src/SmartC/assembler/assembler.ts
index 247e836..198b8e0 100644
--- a/dev/src/SmartC/assembler/assembler.ts
+++ b/dev/src/SmartC/assembler/assembler.ts
@@ -122,6 +122,7 @@ export default function assembler (assemblyCode: string): MACHINE_OBJECT {
{ opCode: 0x16, name: 'MOD_DAT', size: 9, argsType: ['I', 'I'], regex: /^\s*MOD\s+@(\w+)\s+\$(\w+)\s*$/ },
{ opCode: 0x17, name: 'SHL_DAT', size: 9, argsType: ['I', 'I'], regex: /^\s*SHL\s+@(\w+)\s+\$(\w+)\s*$/ },
{ opCode: 0x18, name: 'SHR_DAT', size: 9, argsType: ['I', 'I'], regex: /^\s*SHR\s+@(\w+)\s+\$(\w+)\s*$/ },
+ { opCode: 0x19, name: 'POW_DAT', size: 9, argsType: ['I', 'I'], regex: /^\s*POW\s+@(\w+)\s+\$(\w+)\s*$/ }, // POW @var $var
{ opCode: 0x1a, name: 'JMP_ADR', size: 5, argsType: ['J'], regex: /^\s*JMP\s+:(\w+)\s*$/ },
{ opCode: 0x1b, name: 'BZR_DAT', size: 6, argsType: ['I', 'B'], regex: /^\s*BZR\s+\$(\w+)\s+:(\w+)\s*$/ },
{ opCode: 0x1e, name: 'BNZ_DAT', size: 6, argsType: ['I', 'B'], regex: /^\s*BNZ\s+\$(\w+)\s+:(\w+)\s*$/ },
@@ -136,7 +137,9 @@ export default function assembler (assemblyCode: string): MACHINE_OBJECT {
{ opCode: 0x27, name: 'STZ_DAT', size: 5, argsType: ['I'], regex: /^\s*STZ\s+\$(\w+)\s*$/ },
{ opCode: 0x28, name: 'FIN_IMD', size: 1, argsType: [], regex: /^\s*FIN\s*$/ },
{ opCode: 0x29, name: 'STP_IMD', size: 1, argsType: [], regex: /^\s*STP\s*$/ },
+ { opCode: 0x2a, name: 'SLP_IMD', size: 1, argsType: [], regex: /^\s*SLP\s*$/ },
{ opCode: 0x2b, name: 'ERR_ADR', size: 5, argsType: ['J'], regex: /^\s*ERR\s+:(\w+)\s*$/ },
+ { opCode: 0x2c, name: 'MDV_DAT', size: 13, argsType: ['I', 'I', 'I'], regex: /^\s*MDV\s+@(\w+)\s+\$(\w+)\s+\$(\w+)\s*$/ }, // MDV @var $var $var
{ opCode: 0x30, name: 'SET_PCS', size: 1, argsType: [], regex: /^\s*PCS\s*$/ },
{ opCode: 0x32, name: 'EXT_FUN', size: 3, argsType: ['F'], regex: /^\s*FUN\s+(\w+)\s*$/ },
{ opCode: 0x33, name: 'EXT_FUN_DAT', size: 7, argsType: ['F', 'I'], regex: /^\s*FUN\s+(\w+)\s+\$(\w+)\s*$/ },
@@ -196,6 +199,7 @@ export default function assembler (assemblyCode: string): MACHINE_OBJECT {
{ name: 'check_HASH160_A_with_B', apiCode: 0x0203, opCode: 0x35 },
{ name: 'SHA256_A_to_B', apiCode: 0x0204, opCode: 0x32 },
{ name: 'check_SHA256_A_with_B', apiCode: 0x0205, opCode: 0x35 },
+ { name: 'Check_Sig_B_With_A', apiCode: 0x0206, opCode: 0x35 },
{ name: 'get_Block_Timestamp', apiCode: 0x0300, opCode: 0x35 },
{ name: 'get_Creation_Timestamp', apiCode: 0x0301, opCode: 0x35 },
{ name: 'get_Last_Block_Timestamp', apiCode: 0x0302, opCode: 0x35 },
@@ -208,13 +212,22 @@ export default function assembler (assemblyCode: string): MACHINE_OBJECT {
{ name: 'message_from_Tx_in_A_to_B', apiCode: 0x0309, opCode: 0x32 },
{ name: 'B_to_Address_of_Tx_in_A', apiCode: 0x030a, opCode: 0x32 },
{ name: 'B_to_Address_of_Creator', apiCode: 0x030b, opCode: 0x32 },
+ { name: 'Get_Code_Hash_Id', apiCode: 0x030c, opCode: 0x35 },
{ name: 'get_Current_Balance', apiCode: 0x0400, opCode: 0x35 },
{ name: 'get_Previous_Balance', apiCode: 0x0401, opCode: 0x35 },
{ name: 'send_to_Address_in_B', apiCode: 0x0402, opCode: 0x33 },
{ name: 'send_All_to_Address_in_B', apiCode: 0x0403, opCode: 0x32 },
{ name: 'send_Old_to_Address_in_B', apiCode: 0x0404, opCode: 0x32 },
{ name: 'send_A_to_Address_in_B', apiCode: 0x0405, opCode: 0x32 },
- { name: 'add_Minutes_to_Timestamp', apiCode: 0x0406, opCode: 0x37 }
+ { name: 'add_Minutes_to_Timestamp', apiCode: 0x0406, opCode: 0x37 },
+ { name: 'Get_Map_Value_Keys_In_A', apiCode: 0x0407, opCode: 0x35 },
+ { name: 'Set_Map_Value_Keys_In_A', apiCode: 0x0408, opCode: 0x32 },
+ { name: 'Issue_Asset', apiCode: 0x0409, opCode: 0x35 },
+ { name: 'Mint_Asset', apiCode: 0x040a, opCode: 0x32 },
+ { name: 'Distribute_To_Asset_Holders', apiCode: 0x040b, opCode: 0x32 },
+ { name: 'Get_Asset_Holders_Count', apiCode: 0x040c, opCode: 0x35 },
+ { name: 'Get_Activation_Fee', apiCode: 0x040d, opCode: 0x35 },
+ { name: 'Put_Last_Block_GSig_In_A', apiCode: 0x040e, opCode: 0x32 }
]
const AsmObj: ASM_OBJECT = {
memory: [],
@@ -491,7 +504,7 @@ export default function assembler (assemblyCode: string): MACHINE_OBJECT {
}
const datapages = Math.ceil(AsmObj.memory.length / 32)
const codepages = Math.ceil(AsmObj.bytecode.length / (32 * 16))
- const minimumfee = (cspages + uspages + datapages + codepages) * 7350000
+ const minimumfee = (cspages + uspages + datapages + codepages) * 10000000
return {
DataPages: datapages,
CodeStackPages: cspages,
diff --git a/dev/src/__tests__/asmHighlight.spec.ts b/dev/src/__tests__/asmHighlight.spec.ts
index 26d0d29..513fe18 100644
--- a/dev/src/__tests__/asmHighlight.spec.ts
+++ b/dev/src/__tests__/asmHighlight.spec.ts
@@ -43,6 +43,12 @@ describe('Assembly compilation:', () => {
const result = asmHighlight(code, true)
expect(result).toBe(highlighted)
})
+ it('should highlight atv3 new codes', () => {
+ const code = '^comment new opCodes SIP-37\nSLP\nPOW @base $exp\nMDV @x $y $den\n^comment new APICodes SIP-37\nFUN @ret Check_Sig_B_With_A\nFUN @ret Get_Code_Hash_Id\nFUN @ret Get_Activation_Fee\nFUN Put_Last_Block_GSig_In_A\n^comment new APICodes SIP-38\nFUN @ret Get_Map_Value_Keys_In_A\nFUN Set_Map_Value_Keys_In_A\n^comment new APICodes SIP-39\nFUN @ret Issue_Asset\nFUN Mint_Asset\nFUN Distribute_To_Asset_Holders\nFUN @ret Get_Asset_Holders_Count\n'
+ const highlighted = "^comment
SLP
POW @base $exp
MDV @x $y $den
^comment
FUN @ret Check_Sig_B_With_A
FUN @ret Get_Code_Hash_Id
FUN @ret Get_Activation_Fee
FUN Put_Last_Block_GSig_In_A
^comment
FUN @ret Get_Map_Value_Keys_In_A
FUN Set_Map_Value_Keys_In_A
^comment
FUN @ret Issue_Asset
FUN Mint_Asset
FUN Distribute_To_Asset_Holders
FUN @ret Get_Asset_Holders_Count
"
+ const result = asmHighlight(code, false)
+ expect(result).toBe(highlighted)
+ })
it('should highlight error: ^comment and multi spaces', () => {
const code = 'SET @a #0000000000000100\nC @a\n\nFUN Xclear_A_B\nFUN Xset_A1 $a\nFUN Xset_A1_A2 $a $b\nFUN @a Xcheck_A_equals_B\nFUN @a Xadd_Minutes_to_Timestamp $b $c'
const highlighted = "SET @a #0000000000000100
C @a
FUN Xclear_A_B
FUN Xset_A1 $a
FUN Xset_A1_A2 $a $b
FUN @a Xcheck_A_equals_B
FUN @a Xadd_Minutes_to_Timestamp $b $c
"
diff --git a/dev/src/asmHighlight.ts b/dev/src/asmHighlight.ts
index fb030b2..3509e64 100644
--- a/dev/src/asmHighlight.ts
+++ b/dev/src/asmHighlight.ts
@@ -57,6 +57,7 @@ export function asmHighlight (asmSourceCode: string, addLineNumber: boolean) {
{ opCode: 0x16, size: 9, regex: /^(\s*MOD\s+)(@\w+\s+)(\$\w+\s*)$/ },
{ opCode: 0x17, size: 9, regex: /^(\s*SHL\s+)(@\w+\s+)(\$\w+\s*)$/ },
{ opCode: 0x18, size: 9, regex: /^(\s*SHR\s+)(@\w+\s+)(\$\w+\s*)$/ },
+ { opCode: 0x19, size: 9, regex: /^(\s*POW\s+)(@\w+\s+)(\$\w+\s*)$/ }, // POW @var $var
{ opCode: 0x1a, size: 5, regex: /^(\s*JMP\s+)(:\w+\s*)$/ }, // JMP :label
{ opCode: 0x1b, size: 6, regex: /^(\s*BZR\s+)(\$\w+\s+)(:\w+\s*)$/ }, // BZR $var :label
{ opCode: 0x1e, size: 6, regex: /^(\s*BNZ\s+)(\$\w+\s+)(:\w+\s*)$/ }, // BZR $var :label
@@ -71,7 +72,9 @@ export function asmHighlight (asmSourceCode: string, addLineNumber: boolean) {
{ opCode: 0x27, size: 5, regex: /^(\s*STZ\s+)(\$\w+\s*)$/ },
{ opCode: 0x28, size: 1, regex: /^\s*FIN\s*$/ },
{ opCode: 0x29, size: 1, regex: /^\s*STP\s*$/ },
+ { opCode: 0x2a, size: 1, regex: /^\s*SLP\s*$/ },
{ opCode: 0x2b, size: 5, regex: /^(\s*ERR\s+)(:\w+\s*)$/ }, // ERR :label
+ { opCode: 0x2c, size: 13, regex: /^(\s*MDV\s+)(@\w+\s+)(\$\w+\s+)(\$\w+\s*)$/ }, // MDV @var $var $var
{ opCode: 0x30, size: 1, regex: /^\s*PCS\s*$/ },
{ opCode: 0x32, size: 3, regex: /^(\s*FUN\s+)(\w+\s*)$/ },
{ opCode: 0x33, size: 7, regex: /^(\s*FUN\s+)(\w+\s+)(\$\w+\s*)$/ },
@@ -132,6 +135,7 @@ export function asmHighlight (asmSourceCode: string, addLineNumber: boolean) {
{ fnCode: 0x0203, fnName: 'check_HASH160_A_with_B' },
{ fnCode: 0x0204, fnName: 'SHA256_A_to_B' },
{ fnCode: 0x0205, fnName: 'check_SHA256_A_with_B' },
+ { fnCode: 0x0206, fnName: 'Check_Sig_B_With_A' },
{ fnCode: 0x0300, fnName: 'get_Block_Timestamp' },
{ fnCode: 0x0301, fnName: 'get_Creation_Timestamp' },
{ fnCode: 0x0302, fnName: 'get_Last_Block_Timestamp' },
@@ -144,13 +148,22 @@ export function asmHighlight (asmSourceCode: string, addLineNumber: boolean) {
{ fnCode: 0x0309, fnName: 'message_from_Tx_in_A_to_B' },
{ fnCode: 0x030a, fnName: 'B_to_Address_of_Tx_in_A' },
{ fnCode: 0x030b, fnName: 'B_to_Address_of_Creator' },
+ { fnCode: 0x030c, fnName: 'Get_Code_Hash_Id' },
{ fnCode: 0x0400, fnName: 'get_Current_Balance' },
{ fnCode: 0x0401, fnName: 'get_Previous_Balance' },
{ fnCode: 0x0402, fnName: 'send_to_Address_in_B' },
{ fnCode: 0x0403, fnName: 'send_All_to_Address_in_B' },
{ fnCode: 0x0404, fnName: 'send_Old_to_Address_in_B' },
{ fnCode: 0x0405, fnName: 'send_A_to_Address_in_B' },
- { fnCode: 0x0406, fnName: 'add_Minutes_to_Timestamp' }
+ { fnCode: 0x0406, fnName: 'add_Minutes_to_Timestamp' },
+ { fnCode: 0x0407, fnName: 'Get_Map_Value_Keys_In_A' },
+ { fnCode: 0x0408, fnName: 'Set_Map_Value_Keys_In_A' },
+ { fnCode: 0x0409, fnName: 'Issue_Asset' },
+ { fnCode: 0x040a, fnName: 'Mint_Asset' },
+ { fnCode: 0x040b, fnName: 'Distribute_To_Asset_Holders' },
+ { fnCode: 0x040c, fnName: 'Get_Asset_Holders_Count' },
+ { fnCode: 0x040d, fnName: 'Get_Activation_Fee' },
+ { fnCode: 0x040e, fnName: 'Put_Last_Block_GSig_In_A' }
]
/**
@@ -228,6 +241,7 @@ export function asmHighlight (asmSourceCode: string, addLineNumber: boolean) {
case 0x16:
case 0x17:
case 0x18:
+ case 0x19:
return toSpan(parts[1], Config.spanInstructionClass) +
toSpan(parts[2], Config.spanVariableClass) +
toSpan(parts[3], Config.spanVariableClass)
@@ -245,6 +259,7 @@ export function asmHighlight (asmSourceCode: string, addLineNumber: boolean) {
case 0x13:
case 0x28:
case 0x29:
+ case 0x2a:
case 0x30:
case 0x7f:
return toSpan(parts[0], Config.spanInstructionClass)
@@ -296,6 +311,11 @@ export function asmHighlight (asmSourceCode: string, addLineNumber: boolean) {
toSpan(parts[2], Config.spanVariableClass) +
toSpan(parts[3], Config.spanVariableClass) +
toSpan(parts[4], Config.spanLabelClass)
+ case 0x2c:
+ return toSpan(parts[1], Config.spanInstructionClass) +
+ toSpan(parts[2], Config.spanVariableClass) +
+ toSpan(parts[3], Config.spanVariableClass) +
+ toSpan(parts[4], Config.spanVariableClass)
case 0x32:
apiName = parts[2].trim()
if (allowedFunctions.findIndex(Obj => Obj.fnName === apiName) === -1) {
diff --git a/samples/EchoAnySize.md b/samples/EchoAnySize.md
new file mode 100644
index 0000000..5b02859
--- /dev/null
+++ b/samples/EchoAnySize.md
@@ -0,0 +1,94 @@
+# Echo Any Size
+Simple contract that reads the incoming text message until a zero byte is found on last byte of last page read. Clears the rest of message buffer and then send the "same message" to sender. Smart contracts only can send 32 bytes each time, so expect the received message to be multiple of 32, padded with zero bytes. Online at tesnet `TS-LZYH-PE75-JZTB-FJ88Y`. Note that there is no API to get the message size, so the program must handle the input end in some way. Activation amount is huge because the fees to read/send info for a smart contract are much higher than sending manually and the contract must handle input text up the 1000 bytes, the current blockchain limit.
+
+## Source code
+Note that this contract is currently not working on stable. It is for reference only and to be updated when the hard fork is online.
+```c
+#include APIFunctions
+
+#program name EchoAnySize
+#program description Reads the incoming message until a zero byte\
+ is found on last byte of last page read. Clears the rest of buffer\
+ and then send the same message to sender. Expect text messages.
+#program activationAmount 5_0000_0000
+
+#pragma maxConstVars 1
+
+long zero;
+B_To_Address_Of_Creator();
+long CREATOR = Get_B1();
+
+while (true)
+{
+ while (getNextTxDetails()) {
+ processTX();
+ }
+ Set_B1(CREATOR);
+ Send_All_To_Address_In_B();
+}
+
+// just echoes a received message back to sender.
+void processTX(void) {
+
+ long messagePage, currentLong;
+
+ // Last read on getNextTxDetails
+ currentLong = 4;
+ while (currentLong < currentTX.message.length) {
+ if (((currentTX.message[currentLong - 1]) >> 56) == 0) {
+ // Found a null byte at last byte of last page that was read.
+ break;
+ }
+ messagePage = currentLong / 4;
+ Set_A1_A2(currentTX.txId, messagePage);
+ Message_From_Tx_In_A_To_B();
+ currentTX.message[currentLong++] = Get_B1();
+ currentTX.message[currentLong++] = Get_B2();
+ currentTX.message[currentLong++] = Get_B3();
+ currentTX.message[currentLong++] = Get_B4();
+ }
+ while (currentLong < currentTX.message.length) {
+ // clear the rest of buffer.
+ currentTX.message[currentLong++] = zero;
+ currentTX.message[currentLong++] = zero;
+ currentTX.message[currentLong++] = zero;
+ currentTX.message[currentLong++] = zero;
+ }
+ Set_B1(currentTX.sender);
+ currentLong = 0;
+ do {
+ Set_A1_A2(currentTX.message[currentLong], currentTX.message[currentLong + 1]);
+ currentLong += 2;
+ Set_A3_A4(currentTX.message[currentLong], currentTX.message[currentLong + 1]);
+ currentLong += 2;
+ Send_A_To_Address_In_B();
+ } while (((currentTX.message[currentLong - 1]) >> 56) != 0 && currentLong < currentTX.message.length);
+}
+
+struct TXINFO {
+ long txId;
+ long timestamp;
+ long sender;
+ long amount;
+ long message[132];
+} currentTX;
+
+long getNextTxDetails(void)
+{
+ A_To_Tx_After_Timestamp(currentTX.timestamp);
+ currentTX.txId = Get_A1();
+ if (currentTX.txId == 0) {
+ return false;
+ }
+ currentTX.amount = Get_Amount_For_Tx_In_A();
+ currentTX.timestamp = Get_Timestamp_For_Tx_In_A();
+ Message_From_Tx_In_A_To_B();
+ currentTX.message[0] = Get_B1();
+ currentTX.message[1] = Get_B2();
+ currentTX.message[2] = Get_B3();
+ currentTX.message[3] = Get_B4();
+ B_To_Address_Of_Tx_In_A();
+ currentTX.sender = Get_B1();
+ return true;
+}
+```
diff --git a/samples/GetATCreatorID.md b/samples/GetATCreatorID.md
new file mode 100644
index 0000000..1af3cd1
--- /dev/null
+++ b/samples/GetATCreatorID.md
@@ -0,0 +1,113 @@
+# Get AT Creator ID
+Simple contract that receives a message with some AT ID (text message in decimal representation) and return to sender a message with the creator's ID of that AT (also in text unsigned decimal representation). Online at tesnet `TS-7MUA-SSZ8-W6QR-6M892`
+
+## Source code
+Note that this contract is currently not working on stable. It is for reference only and to be updated when the hard fork is online.
+```c
+#include APIFunctions
+
+#program name GetATCreator
+#program description Receives a message with some AT ID and return to sender a\
+ message with the creator`s ID of that AT.
+#program activationAmount 1_5000_0000
+
+#pragma maxConstVars 1
+
+B_To_Address_Of_Creator();
+long CREATOR = Get_B1();
+
+while (true)
+{
+ while (getNextTxDetails()) {
+ processTX();
+ }
+ Set_B1(CREATOR);
+ Send_All_To_Address_In_B();
+}
+
+// Return to sender the creator of a given AT.
+void processTX(void) {
+ long atId = messageToId();
+ Set_B2(atId);
+ B_To_Address_Of_Creator();
+ long creatorID = Get_B1();
+ IdToMessage(creatorID);
+ Set_B1_B2(currentTX.sender, 0);
+ Set_A1_A2(currentTX.message[0], currentTX.message[1]);
+ Set_A3_A4(currentTX.message[2], currentTX.message[3]);
+ Send_A_To_Address_In_B();
+}
+
+struct TXINFO {
+ long txId;
+ long timestamp;
+ long sender;
+ long amount;
+ long message[4];
+} currentTX;
+
+long getNextTxDetails(void)
+{
+ A_To_Tx_After_Timestamp(currentTX.timestamp);
+ currentTX.txId = Get_A1();
+ if (currentTX.txId == 0) {
+ return false;
+ }
+ currentTX.amount = Get_Amount_For_Tx_In_A();
+ currentTX.timestamp = Get_Timestamp_For_Tx_In_A();
+ Message_From_Tx_In_A_To_B();
+ currentTX.message[0] = Get_B1();
+ currentTX.message[1] = Get_B2();
+ currentTX.message[2] = Get_B3();
+ currentTX.message[3] = Get_B4();
+ B_To_Address_Of_Tx_In_A();
+ currentTX.sender = Get_B1();
+ return true;
+}
+
+long i, auxDiv, auxShift, auxMask, auxNum;
+const long n8 = 8, n10 = 10, n15 = 15, n48 = 48, n57 = 57, n255 = 255;
+void IdToMessage(long id){
+ long currDiv = 10;
+ currentTX.message[] = "00000000000000000000 ";
+ // using i as temp var;
+ i = (id >> 1) / 5;
+ currentTX.message[2] |= (id - (i * 10)) << 24;
+ id = i;
+
+ for (i = 18; id != 0; i--) {
+ auxNum = id % currDiv;
+ id /= 10;
+ auxDiv = i/8;
+ auxShift = (i % 8) * 8;
+ auxMask = 0xff << auxShift;
+ currentTX.message[i/8] |= auxNum << auxShift;
+ }
+}
+
+// Expects a numeric ID in currentTX.message[0] to [3]
+// return its long representation
+long messageToId(void) {
+ long currMul = 1;
+ long ret=0;
+
+ for (i = 19; i>=0; i--) {
+ auxDiv = i/8;
+ auxShift = (i % 8) * 8;
+ auxMask = 0xff << auxShift;
+ auxNum = (currentTX.message[i/8] & auxMask) >> auxShift;
+ if (auxNum == 0) {
+ continue;
+ }
+ if (auxNum < '0' || auxNum > '9' ) {
+ // invalid char
+ return 0;
+ }
+ auxNum &= 0xF;
+ auxNum *= currMul;
+ ret += auxNum;
+ currMul *= 10;
+ }
+ return ret;
+}
+```