x86 / x86-64 machine code, 22 bytes
Takes pointer (RSI), length (RCX) in registers, modifies the string in-place. Callable from C with the x86-64 SysV calling convention as void vowel_alt_caseflip(int dummy, char *RSI, int dummy, size_t RCX)
. Same machine code works in 32-bit mode, although none of the standard 32-bit C calling conventions match.
NASM listing, with address, machine-code bytes, and source the machine-code answer was generated from.
1 ;;; char *str /* RSI */, size_t len /* RCX */
2 vowel_alt_caseflip:
3 00000000 BF22822000 mov edi, 0x0208222 ; ASCII vowel bitmap, 1-indexed like ASCII codes are with 'a' = 0x61 not 0x60
4 00000005 31D2 xor edx, edx ; first vowel is not flipped
5 .loop:
6 00000007 AC lodsb ; al = *str++
7 00000008 0FA3C7 bt edi, eax ; if (bitmap & (1<<(al&31)))
8 0000000B 7306 jnc .non_vowel
9 0000000D 3056FF xor [rsi-1], dl ; caseflip or not the current character
10 00000010 80F220 xor dl, 0x20 ; toggle state for the next
11 .non_vowel:
12 00000013 E2F2 loop .loop
13 00000015 C3 ret
; next address is 0x16, size is 0x16 = 22 bytes
Since the input string is guaranteed to be all lowercase alphabetic, I just used XOR instead of AND with ~0
/ ~0x20
to flip instead of clear the ASCII lower-case bit. It would work out to the same size with mov dl, 0xFF
/ and [rsi-1], dl
.
Try it online! with a Linux _start
caller that passes it the alphabet twice and makes write system calls before/after, so we can see that it correctly treats u
as a vowel.
For more about the immediate bitmap strategy, and variations like using it branchlessly, see SO and two of my previous x86 answers:
Alternate version, same size
I had hoped to be able to use xor al, 0x20
to save a byte vs. DL, but we need AL for efficient string-reading via lodsb
. (We need a char from the string in a register to use as a source for bt
.) We could start with mov
there and use stosb
to store, but that would have to be separate from a reg-reg XOR or AND, and still doesn't free up AL.
We could use scasb
as a 1-byte inc rdi
. Using mov dl, [rdi]
to start, and saving the -1
in the memory-dest addressing mode, and another byte from xor al, 0x20
short-form, that's break-even for size and worse for efficiency (useless scasb
reload). Or similar efficiency in 32-bit mode since we could use inc edi
there.
;;; char *str /* RDI */, size_t len /* RCX */
vowel_alt_caseflip_scasb:
mov esi, 0x0208222 ; ASCII vowel bitmap, 1-indexed like ASCII codes are with 'a' = 0x61 not 0x60
xor eax, eax ; first vowel is not flipped
.loop:
mov dl, [rdi]
bt esi, edx ; if (bitmap & (1<<(c&31)))
jnc .non_vowel
xor [rdi], al ; caseflip or not the current character
xor al, 0x20 ; toggle state for the next
.non_vowel:
scasb ; inc rdi in one byte even on x86-64
loop .loop
ret
aeiou
and noty
? \$\endgroup\$i
. The examples you give would give it a different pronunciation. The International phonetic alphabet explicitly makes a distinction between the two sounds./j/
as inyes
and/i/
as infeet
. The former is a consonant while the latter is a vowel \$\endgroup\$