I may also add a version with explicit lengths which accepts null bytes, but the solution would be significantly less elegant.
I may also add a version with explicit lengths which accepts null bytes, but the solution would be significantly less elegant.
x86 machine code, 29 bytes
Machine code:
00000034: 31 d2 e3 08 8a 11 41 41 84 d2 e1 fe ac 84 c0 75 1.....AA.......u
00000044: 06 e3 09 89 ce 31 c9 30 d0 aa eb e4 c3 .....1.0.....
Commented assembly:
.intel_syntax noprefix
.globl .strxor
// Doesn't follow standard calling convention.
// Input:
// EDI: Output buffer large enough for longest string
// ESI, ECX: Null terminated strings to XOR
// Output:
// NON-null terminated string stored in EDI.
.strxor:
.Lloop:
// Always clear EDX.
xor edx, edx
// We mark the end of the shorter string
// by setting ECX to null.
jecxz .Lskip_ecx
.Lno_skip_ecx:
// Read a byte from ECX into DL.
mov dl, byte ptr [ecx]
// Increment twice because LOOPZ decrements.
inc ecx
inc ecx
// Was it '0円'?
test dl, dl
// If so, set ECX to NULL by using LOOPZ in place.
// CMOVZ ECX, EAX also works, but needs P6.
// This works with i386 and is the same size.
.Lclear_ecx:
loopz .Lclear_ecx
.Lskip_ecx:
// Load from ESI and autoincrement
lods al, byte ptr [esi]
// Test for '0円'
test al, al
jnz .Lno_swap
.Lswap: // '0円' found
// If ECX is null, we are at the end of both strings.
jecxz .Lend
// Otherwise, we swap ESI and ECX, and then clear ECX.
// Set ESI to ECX.
mov esi, ecx
// And set ECX to NULL.
xor ecx, ecx
// fallthrough
.Lno_swap:
// XOR the two bytes together
xor al, dl
// Store to EDI and autoincrement
stos byte ptr [edi], al
// Loop unconditionally.
jmp .Lloop
.Lend:
ret
The code reads two null terminated strings from esi and ecx, and stores the NON null terminated string in edi.
The logic is probably easier to see in the equivalent C code.
void strxor(char *dst, const char *src1, const char *src2)
{
for (;;) {
char x = '0円';
if (src2 != NULL) {
x = *src2++;
if (x == '0円') { // eos
src2 = NULL;
}
}
char y = *src1++;
if (y == '0円') {
if (src2 == NULL) { // both eos
return;
} else { // src2 is longer
src1 = src2;
src2 = NULL;
}
}
*dst++ = x ^ y;
}
}
I don't null terminate because null bytes naturally show up in the output and it saves a byte.