NOTE: Since many/most of the answers seem to ignore the requirement in the question that the input must be taken from stdin
and the output must be sent to stdout
, I suppose it is reasonable if I do so, too. I think that's a silly and arbitrary requirement, because it makes many languages non-competitive. The standard on Code Golf is to allow functions, and to allow functions taking input via arguments and outputting via their return value(s), so this is what I will follow.
x86-64 Assembly, standard Linux calling convention, 17 bytes
64 31 C0 0F B6 0F E3 09 48 FF C7 8D 44 01 D0 EB F2 C3
Try it online!
This is a minor, iterative improvement on qwr's answer, which is 18 bytes.
It saves 1 byte by using the relatively-obscure JECXZ
instruction (actually JRCXZ
, since this is 64-bit mode), which jumps if the value of the rcx
register is zero.
It also improves on qwr's original (arguably fixes a bug?) in that it supports the case where the input is an empty string, returning a sum of zero. (To be fair, the challenge doesn't specify whether it is required to handle this, so maybe it cannot be termed a "bug", and, in fact, my next attempt will have this same "bug", so I can hardly ding qwr for taking advantage of it, too!)
SumDigits:
31 C0 xor eax, eax
Loop:
0F B6 0F movzx ecx, BYTE PTR [rdi]
E3 09 jrcxz End
48 FF C7 inc rdi
8D 44 01 D0 lea eax, [rcx + rax - '0']
EB F2 jmp Loop
End:
C3 ret ; result is in eax
x86-64 Assembly, fully custom calling convention, 14 bytes
64 31 C0 31 D2 AC 8D 54 02 D0 38 26 75 F7 C3
Try it online!
This is a variation on the same theme (it still takes the input as a pointer to the beginning of a NUL-terminated ASCII string), but it pulls out all the stops to really optimize for size:
It uses the ultra-compact x86 string instructions. In this case, lods
, which loads a byte into al
from the address in rsi
, and also increments rsi
to point to the next byte, using a single-byte encoding.
- In order to know whether to increment or decrement the source pointer, the
rsi
instruction implicitly depends on the direction flag (DF
). Normally, you'd need to explicitly clear that flag with the cld
instruction, but, in this case, since we're running on a *nix-like operating system, per the challenge, we can assume it is already clear. (Also, when you're defining a custom calling convention, the state of the flags can be part of that; see below.)
It uses a custom calling convention so that we can (A) take the input string in the rsi
register, which is what the lods
instruction expects, saving us from having to copy it from one register to another, and (B) build the result in a custom register (edx
), freeing up eax
to use as a scratch register (because this is what the lods
instruction uses as an implicit destination operand).
The lods
instruction is only loading a single byte, so it only modified the low-order byte of the eax
register (al
). But the lea
instruction that we use to perform multiple arithmetic operations in a single instruction only works on the full 64-bit register, so we need to make sure that the high values are clear. We could do this with an explicit movzx
after the lods
instruction, but that would consume a lot of bytes. So, instead, we do it once, outside of the loop, with a single xor
instruction. This clears the entire rax
register, which works because we only ever touch the low-order byte (al
).
- Another neat trick falls out of this: because we don't touch the high-order byte (
ah
), it's guaranteed to be zero through all iterations of our loop, so we can compare (cmp
) the source pointer (string) to ah
instead of comparing it to a literal 0, which saves us one byte in the encoding.
SumDigits:
-- ; cld ; can assume DF is always clear on Linux)
31 C0 xor eax, eax ; eax = 0 (al = 0, ah = 0)
31 D2 xor edx, edx ; edx = 0 (holds result)
Loop:
AC lods ; al = BYTE PTR [rsi]
8D 54 02 D0 lea edx, [rdx + rax - '0'] ; edx += (eax - '0')
38 26 cmp BYTE PTR [rsi], ah ; is BYTE PTR [rsi] == ah (which is 0)?
75 F7 jne Loop ; loop back if not equal (non-zero)
C3 ret ; result is in edx
The custom calling convention is a pretty major "cheat" here, but it's not unusual in the "real world" to use a custom calling convention when writing code in assembly. If you're going to call it from C, you need to take steps to match the standard C calling convention, but who says we're calling it from C?! We don't need no stinkin' C! :-)