x86-64 makine kodu, 44 bayt
(Aynı makine kodu, 32 bit modunda da çalışır.)
@Daniel Schepler'in cevabı bunun için bir başlangıç noktasıydı, ancak bunun en az bir yeni algoritmik fikri var (sadece aynı fikrin daha iyi kullanılması değil): ASCII 'B'
( 1000010
) ve 'X'
( 1011000
) kodları, maskelemeden sonra 16 ve 2'yi veriyor0b0010010
.
Ondalık (sıfır olmayan baştaki haneyi) ve sekizli (sonradan '0'
küçük olan 'B'
) dışladıktan sonra , yalnızca base = ayarlayabilir c & 0b0010010
ve sayı döngüsüne atlayabiliriz.
X86-64 System V as ile Calble ile unsigned __int128 parse_cxx14_int(int dummy, const char*rsi);
EDX dönüş değerini unsigned __int128
sonucun yüksek yarısından çıkarın tmp>>64
.
.globl parse_cxx14_int
## Input: pointer to 0-terminated string in RSI
## output: integer in EDX
## clobbers: RAX, RCX (base), RSI (points to terminator on return)
parse_cxx14_int:
xor %eax,%eax # initialize high bits of digit reader
cdq # also initialize result accumulator edx to 0
lea 10(%rax), %ecx # base 10 default
lodsb # fetch first character
cmp $'0', %al
jne .Lentry2
# leading zero. Legal 2nd characters are b/B (base 2), x/X (base 16)
# Or NUL terminator = 0 in base 10
# or any digit or ' separator (octal). These have ASCII codes below the alphabetic ranges
lodsb
mov $8, %cl # after '0' have either digit, apostrophe, or terminator,
cmp $'B', %al # or 'b'/'B' or 'x'/'X' (set a new base)
jb .Lentry2 # enter the parse loop with base=8 and an already-loaded character
# else hex or binary. The bit patterns for those letters are very convenient
and $0b0010010, %al # b/B -> 2, x/X -> 16
xchg %eax, %ecx
jmp .Lentry
.Lprocessdigit:
sub $'0' & (~32), %al
jb .Lentry # chars below '0' are treated as a separator, including '
cmp $10, %al
jb .Lnum
add $('0'&~32) - 'A' + 10, %al # digit value = c-'A' + 10. we have al = c - '0'&~32.
# c = al + '0'&~32. val = m+'0'&~32 - 'A' + 10
.Lnum:
imul %ecx, %edx
add %eax, %edx # accum = accum * base + newdigit
.Lentry:
lodsb # fetch next character
.Lentry2:
and $~32, %al # uppercase letters (and as side effect,
# digits are translated to N+16)
jnz .Lprocessdigit # space also counts as a terminator
.Lend:
ret
Daniel'in versiyonuna göre değişen bloklar (çoğunlukla) diğer talimatlardan daha az girintilidir. Ayrıca ana döngünün alt kısmında koşullu dalı vardır. Bu, nötr bir değişim olarak ortaya çıktı çünkü hiçbir yol bunun üstüne dec ecx / loop .Lentry
düşemedi ve döngüye girme fikri, sekizlik oklusu farklı bir şekilde kullandıktan sonra bir kazanç olmadığı ortaya çıktı. Ama döngü içinde deyimsel formda döngü ile daha az talimat var, yapıdayken {} yapıyorum, o yüzden sakladım.
Daniel'in C ++ test donanımı, 32 bitlik yanıtıyla aynı çağrı kuralını kullanan bu kodla 64 bit modunda değişmeden çalışıyor.
g++ -Og parse-cxx14.cpp parse-cxx14.s &&
./a.out < tests | diff -u -w - tests.good
Gerçek cevap olan makine kodu baytları da dahil olmak üzere sökme
0000000000000000 <parse_cxx14_int>:
0: 31 c0 xor %eax,%eax
2: 99 cltd
3: 8d 48 0a lea 0xa(%rax),%ecx
6: ac lods %ds:(%rsi),%al
7: 3c 30 cmp $0x30,%al
9: 75 1c jne 27 <parse_cxx14_int+0x27>
b: ac lods %ds:(%rsi),%al
c: b1 08 mov $0x8,%cl
e: 3c 42 cmp $0x42,%al
10: 72 15 jb 27 <parse_cxx14_int+0x27>
12: 24 12 and $0x12,%al
14: 91 xchg %eax,%ecx
15: eb 0f jmp 26 <parse_cxx14_int+0x26>
17: 2c 10 sub $0x10,%al
19: 72 0b jb 26 <parse_cxx14_int+0x26>
1b: 3c 0a cmp $0xa,%al
1d: 72 02 jb 21 <parse_cxx14_int+0x21>
1f: 04 d9 add $0xd9,%al
21: 0f af d1 imul %ecx,%edx
24: 01 c2 add %eax,%edx
26: ac lods %ds:(%rsi),%al
27: 24 df and $0xdf,%al
29: 75 ec jne 17 <parse_cxx14_int+0x17>
2b: c3 retq
Daniel'in sürümündeki diğer değişiklikler arasında sub $16, %al
, ayırıcıları algılamanın bir parçası olarak kullanmak sub
yerine rakam döngüsünün içinden tasarruf test
etmek ve rakamlara karşılık alfabetik karakterleri kullanmak sayılabilir .
Daniel'in aksine , aşağıdaki her karakter '0'
sadece bir ayırıcı olarak değerlendirilir '\''
. (Hariç ' '
: and $~32, %al
/ jnz
her iki döngüde de boşluğu bir satırın başında bir tamsayı ile test etmek için uygun olan bir sonlandırıcı olarak görür.)
%al
Döngünün içinde değişiklik yapan her işlem , sonuç tarafından ayarlanmış dal alıcı bayraklara sahiptir ve her dal farklı bir yere gider (veya aşağıya düşer).