Merge pull request #2771 from colinleroy/much-smaller-strndup

Make strndup smaller, safer, faster
This commit is contained in:
Bob Andrews
2025-07-09 00:55:04 +02:00
committed by GitHub
2 changed files with 49 additions and 39 deletions

View File

@@ -4,53 +4,50 @@
; char* __fastcall__ strndup (const char* S, size_t maxlen);
;
.importzp ptr4, c_sp
.import _strdup, _strlen, pushax, popax, _realloc
.importzp tmp1, tmp2, ptr2
.import _strncpy, _strlen, _malloc
.import pushax, popax, incsp2, incax1, swapstk
.import ___errno
.export _strndup
_strndup:
sta maxlen ; Remember maxlen
stx maxlen+1
.include "errno.inc"
jsr popax ; Duplicate string
jsr _strdup
jsr pushax ; Remember result
.proc _strndup
sta tmp1 ; Remember maxlen
stx tmp1+1
jsr _strlen ; Check length,
cpx maxlen+1
bcc out ; Return directly if < maxlen
jsr popax ; Get string
jsr pushax ; Keep it in TOS
jsr _strlen ; Get string length,
cpx tmp1+1 ; Compare to max,
bcc alloc
bne :+
cmp maxlen
bcc out
cmp tmp1
bcc alloc
: ldy #$00 ; Otherwise, point to end of string,
lda maxlen
clc
adc (c_sp),y
sta ptr4
lda maxlen+1
iny
adc (c_sp),y
sta ptr4+1
: lda tmp1 ; Use maxlen if shorter
ldx tmp1+1
dey ; Cut it short,
tya
sta (ptr4),y
alloc: jsr incax1 ; Add 1 for terminator
jsr _malloc ; Allocate output
cpx #$00 ; Check allocation
beq errmem
ldx maxlen+1 ; And finally, realloc to maxlen+1
ldy maxlen
iny
tya
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
jsr swapstk ; Put dest in TOS and get string back
jsr pushax ; Put src in TOS
lda tmp1 ; Get length for strncpy
ldx tmp1+1
.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

View File

@@ -1,4 +1,5 @@
#include <string.h>
#include <errno.h>
#include "unittest.h"
#define SHORT_STR "abcdefghijklmnopqrstuvwxyz"
@@ -56,5 +57,17 @@ TEST
free(dst);
}
src = malloc(LONG_STR_LEN+1);
ASSERT_IsTrue(src != NULL, "Could not allocate source string");
memset(src, 'a', LONG_STR_LEN);
src[LONG_STR_LEN] = '\0';
dst = strndup(src, 30);
ASSERT_IsTrue(dst != NULL, "strndup returned NULL");
free(dst);
dst = strndup(src, LONG_STR_LEN);
ASSERT_IsTrue(errno == ENOMEM, "error is not ENOMEM");
ASSERT_IsTrue(dst == NULL, "strndup did not return NULL");
}
ENDTEST