x86-64-Maschinencode, 44 Byte
(Der gleiche Maschinencode funktioniert auch im 32-Bit-Modus.)
@Daniel Scheplers Antwort war ein Ausgangspunkt dafür, aber dies hat mindestens eine neue algorithmische Idee (nicht nur besseres Golfen derselben Idee): Die ASCII-Codes für 'B'
( 1000010
) und 'X'
( 1011000
) ergeben nach dem Maskieren mit 16 und 20b0010010
.
Nachdem wir also Dezimalstellen (führende Ziffer ungleich Null) und Oktalstellen (Zeichen danach '0'
ist kleiner als 'B'
) ausgeschlossen haben, können wir einfach base = setzen c & 0b0010010
und in die Ziffernschleife springen.
Aufrufbar mit x86-64 System V als unsigned __int128 parse_cxx14_int(int dummy, const char*rsi);
Extrahieren Sie den EDX-Rückgabewert aus der oberen Hälfte des unsigned __int128
Ergebnisses mit 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
Die geänderten Blöcke gegenüber Daniels Version sind (meistens) weniger eingerückt als andere Anweisungen. Auch die Hauptschleife hat unten ihren bedingten Zweig. Es stellte sich heraus, dass dies eine neutrale Änderung war, da keiner der Pfade in den oberen Bereich fallen konntedec ecx / loop .Lentry
Idee, in die Schleife einzutreten, sich nicht als Gewinn herausstellte, nachdem man das Oktal anders gehandhabt hatte. Aber es gibt weniger Anweisungen in der Schleife, wobei die Schleife in der idiomatischen Form do {} while-Struktur ist, also habe ich sie beibehalten.
Daniels C ++ - Test-Harness funktioniert mit diesem Code unverändert im 64-Bit-Modus. Dabei wird dieselbe Aufrufkonvention wie bei seiner 32-Bit-Antwort verwendet.
g++ -Og parse-cxx14.cpp parse-cxx14.s &&
./a.out < tests | diff -u -w - tests.good
Demontage, einschließlich der Maschinencode-Bytes, die die eigentliche Antwort sind
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
Andere Änderungen von Daniels Version beinhalten das Speichern des Codes sub $16, %al
aus der Ziffernschleife heraus, indem mehr sub
anstelle von verwendet wirdtest
als Teil der Erfassungs Separatoren und Ziffern vs. alphabetischen Zeichen.
Im Gegensatz zu Daniel wird jedes Zeichen '0'
als Trennzeichen behandelt, nicht nur '\''
. (Außer ' '
: and $~32, %al
/ jnz
in beiden Schleifen wird das Leerzeichen als Abschlusszeichen behandelt, was möglicherweise zum Testen mit einer Ganzzahl am Zeilenanfang nützlich ist.)
Jede Operation, die %al
innerhalb der Schleife geändert wird, weist Verzweigungsverbrauchsflags auf, die vom Ergebnis gesetzt werden, und jede Verzweigung wird an eine andere Position verschoben (oder fällt durch).