Make strndup smaller, safer, faster

First implementation was doing:
- strdup (without checking result!)
- strlen
- terminate
- realloc

New one does:
- malloc to shortest +1
- strncpy
- terminate
This commit is contained in:
Colin Leroy-Mira
2025-07-05 12:11:40 +02:00
parent 7ed1f0c7ea
commit 6604c5ce92

View File

@@ -4,53 +4,50 @@
; char* __fastcall__ strndup (const char* S, size_t maxlen); ; char* __fastcall__ strndup (const char* S, size_t maxlen);
; ;
.importzp ptr4, c_sp .importzp tmp1, tmp2, ptr2
.import _strdup, _strlen, pushax, popax, _realloc .import _strncpy, _strlen, _malloc
.import pushax, popax, incsp2, incax1, swapstk
.import ___errno
.export _strndup .export _strndup
_strndup: .include "errno.inc"
sta maxlen ; Remember maxlen
stx maxlen+1
jsr popax ; Duplicate string .proc _strndup
jsr _strdup sta tmp1 ; Remember maxlen
jsr pushax ; Remember result stx tmp1+1
jsr _strlen ; Check length, jsr popax ; Get string
cpx maxlen+1 jsr pushax ; Keep it in TOS
bcc out ; Return directly if < maxlen
jsr _strlen ; Get string length,
cpx tmp1+1 ; Compare to max,
bcc alloc
bne :+ bne :+
cmp maxlen cmp tmp1
bcc out bcc alloc
: ldy #$00 ; Otherwise, point to end of string, : lda tmp1 ; Use maxlen if shorter
lda maxlen ldx tmp1+1
clc
adc (c_sp),y
sta ptr4
lda maxlen+1
iny
adc (c_sp),y
sta ptr4+1
dey ; Cut it short, alloc: jsr incax1 ; Add 1 for terminator
tya jsr _malloc ; Allocate output
sta (ptr4),y cpx #$00 ; Check allocation
beq errmem
ldx maxlen+1 ; And finally, realloc to maxlen+1 jsr swapstk ; Put dest in TOS and get string back
ldy maxlen jsr pushax ; Put src in TOS
iny lda tmp1 ; Get length for strncpy
tya ldx tmp1+1
bne :+
inx
: jsr _realloc ; TOS still contains result
; We consider realloc will not fail,
; as the block shrinks.
jsr pushax ; push/pop for size optimisation
out:
jmp popax
.bss jsr _strncpy ; Copy
pha ; Terminate
lda #$00
sta (ptr2),y
pla
rts
maxlen: .res 2 errmem: ldy #ENOMEM
sty ___errno
jmp incsp2 ; Pop string and return
.endproc