ARM Thumb-2 (manual rothuvsein
, no div, no libc) 70 bytes
Machine code:
b5f0 2100 2420 f000 f816 0032 18c5 3b03
bf18 f1c3 0303 5cc4 a004 f000 f80c 240a
fb01 3104 1c68 2a00 d1ec bdf0 6f72 6874
7675 6573 6e69 f07f 0300 1c5b 5cc6 b10e
42a6 d1fa 4770
Assembly:
.syntax unified
.arch armv6t2
.thumb
.globl _strtoint
.thumb_func
_strtoint:
push {r4-r7, lr}
movs r1, #0
.Lstrtoint_loop:
movs r4, #' '
bl _strfind
movs r2, r6
adds r5, r0, r3
subs r3, #3
it ne
rsbne r3, #3
ldrb r4, [r0, r3]
adr r0, .Lrothuvsein
bl _strfind
movs r4, #10
mla r1, r1, r4, r3
adds r0, r5, #1
cmp r2, #0
bne .Lstrtoint_loop
.Lstrtoint_loop_end:
pop {r4-r7, pc}
@ naturally 4-byte aligned
@ note: this is not null terminated - we blindly assume a match
.Lrothuvsein:
.ascii "rothuvsein"
@ naturally 2-byte aligned
.thumb_func
_strfind:
movs r3, #-1
.Lstrfind_loop:
adds r3, r3, #1
ldrb r6, [r0, r3]
cbz r6, .Lstrfind_loop_end
cmp r6, r4
bne .Lstrfind_loop
.Lstrfind_loop_end:
bx lr
Explanation
Yet another port of the rothuvsein
method, but this seems to be the first one in assembly. It is also done manually without the support of libc or fancy useless CISC instructions like repne scasb
. Just plain, barebones RISC. That means that it is longer than other assembly languages.
Unlike my previous entries, I am a little more flexible with the calling convention.
While the input string (null terminated) is in r0
just like it would be in C, the output is a 32-bit integer in r1
instead of the traditional r0
.
It can still be wrapped in C like so, since r1
is still used for 64-bit integers:
extern uint64_t _strtoint(const char *str);
uint32_t strtoint(const char *str)
{
return (uint32_t)(_strtoint(str) >> 32);
}
Regardless of whether I care about the calling convention, I am going to have to do something with the link register so I can call my subroutine, so might as well push
the callee-saved registers and remain mostly in line with the calling convention.
.globl _strtoint
.thumb_func
_strtoint:
push {r4-r7, lr}
Initialize our accumulator/output variable to 0.
movs r1, #0
Call our internal subroutine to find the end of the word.
r3
will have the length of the word, and r6
will have the last byte read. I will explain _strfind
later.
.Lstrtoint_loop:
movs r4, #' '
bl _strfind
We save a copy of the last byte read for later to tell if we reached the null terminator.
movs r2, r6
Put the pointer to the string (advanced to the space) in r5
.
adds r5, r0, r3
Here is the trickiest part: One does not simply str+str+str
in assembly. We need to find the index.
Specifically, if we plot it out:
oneoneone
^
fourfourfour
^
sevensevenseven
^
We can turn it into this:
switch (strlen(str)) {
case 3: return str[0];
case 4: return str[2];
case 5: return str[1];
}
It can be done as 6 % strlen(str)
, but that is division. I can do better.
I could do a lookup table, but there is a better solution than that:
Remember that r3
is essentially strlen(str)
.
We can do this:
subs r3, #3
it ne
rsbne r3, #3 @ note: rsb = Reverse SuBtract
The equivalent C:
if ((len -= 3) != 0)
{
len = 3 - len;
}
This works perfectly:
3 - 3 = 0
3 - (4 - 3) = 2
3 - (5 - 3) = 1
Now, we load the byte from the string at our offset...
ldrb r4, [r0, r3]
...then call _strfind
with the rothuvsein
lookup table to find it.
adr r0, .Lrothuvsein
bl _strfind
Now, we add the length to our accumulator times 10;
movs r4, #10
mla r1, r1, r4, r3 @ r1 = r1 * 10 + len
Take the copy of the string we made in r5
, advance it past the space, and put it in r0
for the next iteration.
adds r0, r5, #1
Then, loop if we are not at a null terminator:
cmp r2, #0
bne .Lstrtoint_loop
Finally, return in r1
by popping the link register holding the return address into the program counter (along with restoring the other registers I saved).
.Lstrtoint_loop_end:
pop {r4-r7, pc}
_strfind subroutine
_strfind
is the real workhorse here.
C users will find it similar to strcspn
or strchr
.
It is called with the null terminated string to search in r0
and the byte to find in r4
.
It will return the index of the match (or null terminator) in r3
and the last byte read in r6
.
First, initialize our counter at -1.
_strfind:
movs r3, #-1
On each iteration, increment the counter..
.Lstrfind_loop:
adds r3, r3, #1
Then, load str[counter]
into r6
ldrb r6, [r0, r3]
Then loop unless r6
is \0
or if it matches r4
.
cbz r6, .Lstrfind_loop_end
cmp r6, r4
bne .Lstrfind_loop
When we find it, return back to _strtoint
.
.Lstrfind_loop_end:
bx lr
range "zero".."nine"
is not fully specified. \$\endgroup\$Interpreter@"SemanticNumber"
does exactly this in Mathematica—except that it fails on strings starting withzero zero
. \$\endgroup\$