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); ; 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

View File

@@ -1,4 +1,5 @@
#include <string.h> #include <string.h>
#include <errno.h>
#include "unittest.h" #include "unittest.h"
#define SHORT_STR "abcdefghijklmnopqrstuvwxyz" #define SHORT_STR "abcdefghijklmnopqrstuvwxyz"
@@ -56,5 +57,17 @@ TEST
free(dst); 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 ENDTEST