Merge pull request #2336 from colinleroy/optimize-strdup

Optimize strdup
This commit is contained in:
Bob Andrews 2024-01-08 22:13:43 +01:00 committed by GitHub
commit 2127778239
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 111 additions and 73 deletions

View File

@ -7,13 +7,13 @@
.export _strcspn
.import popptr1, _strlen
.importzp ptr1, ptr2, tmp1, tmp2
.importzp ptr1, ptr4, tmp1, tmp2
_strcspn:
jsr _strlen ; get length in a/x and transfer s2 to ptr2
jsr _strlen ; get length in a/x and transfer s2 to ptr4
; Note: It does not make sense to
; have more than 255 test chars, so
; we don't support a high byte here! (ptr2+1 is
; we don't support a high byte here! (ptr4+1 is
; also unchanged in strlen then (important!))
; -> the original implementation also
; ignored this case
@ -38,7 +38,7 @@ checkNext:
iny
check: cpy tmp1 ; compare with length of test character string
beq endOfTestChars
cmp (ptr2),y ; found matching char?
cmp (ptr4),y ; found matching char?
bne checkNext
leave: txa ; restore position of finding

View File

@ -1,85 +1,62 @@
;
; Ullrich von Bassewitz, 18.07.2000
; Colin Leroy-Mira, 05.01.2024
;
; char* __fastcall__ strdup (const char* S);
;
; Note: The code knowns which zero page locations are used by malloc.
; Note: The code knowns which zero page locations are used by malloc,
; memcpy and strlen.
;
.importzp sp, tmp1, ptr4
.import pushax, decsp4, incsp4
.import _strlen, _malloc, _memcpy
.importzp ptr2, ptr3, ptr4, tmp1, tmp2, tmp3
.import _strlen_ptr4, _malloc, _memcpy, pushax
.export _strdup
.macpack cpu
.macpack generic
_strdup:
; Get length (and store source in ptr4)
sta ptr4
stx ptr4+1
stx tmp1 ; Backup high byte, which
jsr _strlen_ptr4 ; strlen may increment
; Since we need some place to store the intermediate results, allocate a
; stack frame. To make this somewhat more efficient, create the stackframe
; as needed for the final call to the memcpy function.
pha ; decsp will destroy A (but not X)
jsr decsp4 ; Target/source
; Store the pointer into the source slot
ldy #1
txa
sta (sp),y
pla
.if (.cpu .bitand CPU_ISET_65SC02)
sta (sp)
; Add null byte for terminator
.if (.cpu .bitand ::CPU_ISET_65SC02)
inc a
.else
dey
sta (sp),y
clc
adc #1
.endif
; Get length of S (which is still in a/x)
jsr _strlen
; Calculate strlen(S)+1 (the space needed)
add #1
bcc @L1
bne :+
inx
; Save the space we're about to allocate in ptr4
@L1: sta ptr4
stx ptr4+1
; Allocate memory. _malloc will not use ptr4
; Store length
: sta tmp2
stx tmp3
; Allocate memory
jsr _malloc
; Store the result into the target stack slot
ldy #2
sta (sp),y ; Store low byte
sta tmp1
txa ; Get high byte
iny
sta (sp),y ; Store high byte
; Check for a NULL pointer
ora tmp1
; Check for NULL
bne :+
cpx #$00
beq OutOfMemory
; Copy the string. memcpy will return the target string which is exactly
; what we need here. It will also drop the allocated stack frame.
; Push dest
: jsr pushax
; Push source
lda ptr4
ldx ptr4+1 ; Load size
jmp _memcpy ; Copy string, drop stackframe
ldx tmp1
jsr pushax
; Out of memory, return NULL (A = 0)
; Push length
lda tmp2
ldx tmp3
; Copy and return the dest pointer
jmp _memcpy
OutOfMemory:
tax
jmp incsp4 ; Drop stack frame
rts

View File

@ -2,19 +2,20 @@
; Ullrich von Bassewitz, 31.05.1998
;
; Note: strspn & strcspn call internally this function and rely on
; the usage of only ptr2 here! Keep in mind when appling changes
; the usage of only ptr4 here! Keep in mind when appling changes
; and check the other implementations too!
;
; size_t __fastcall__ strlen (const char* s);
;
.export _strlen
.importzp ptr2
.export _strlen, _strlen_ptr4
.importzp ptr4
.macpack cpu
_strlen:
sta ptr2 ; Save s
stx ptr2+1
sta ptr4 ; Save s
stx ptr4+1
_strlen_ptr4:
.if (.cpu .bitand ::CPU_ISET_HUC6280)
clx
cly
@ -27,11 +28,11 @@ _strlen:
.endif
.endif
L1: lda (ptr2),y
L1: lda (ptr4),y
beq L9
iny
bne L1
inc ptr2+1
inc ptr4+1
inx
bne L1

View File

@ -7,13 +7,13 @@
.export _strspn
.import popptr1, _strlen
.importzp ptr1, ptr2, tmp1, tmp2
.importzp ptr1, ptr4, tmp1, tmp2
_strspn:
jsr _strlen ; get length in a/x and transfer s2 to ptr2
jsr _strlen ; get length in a/x and transfer s2 to ptr4
; Note: It does not make sense to
; have more than 255 test chars, so
; we don't support a high byte here! (ptr2+1 is
; we don't support a high byte here! (ptr4+1 is
; also unchanged in strlen then (important!))
; -> the original implementation also
; ignored this case
@ -38,7 +38,7 @@ checkNext:
iny
check: cpy tmp1 ; compare with length of test character string
beq leave
cmp (ptr2),y ; found matching char?
cmp (ptr4),y ; found matching char?
bne checkNext
foundTestChar:

View File

@ -0,0 +1,60 @@
#include <string.h>
#include "unittest.h"
#define SHORT_STR "abcdefghijklmnopqrstuvwxyz"
#define MID_STR_LEN 700 /* Two pages and something */
#define LONG_STR_LEN 40000UL /* Two long to duplicate */
TEST
{
char *dst;
char *src;
int i;
dst = strdup("");
ASSERT_IsTrue(dst != NULL, "strdup returned NULL")
ASSERT_IsTrue(!strcmp(dst, ""), "strings differ");
free(dst);
dst = strdup(SHORT_STR);
ASSERT_IsTrue(dst != NULL, "strdup returned NULL");
ASSERT_IsTrue(strlen(dst) == strlen(SHORT_STR), "string lengths differ");
ASSERT_IsTrue(!strcmp(dst, SHORT_STR), "strings differ");
free(dst);
src = malloc(MID_STR_LEN+1);
ASSERT_IsTrue(src != NULL, "Could not allocate source string");
memset(src, 'a', MID_STR_LEN-1);
src[MID_STR_LEN] = '\0';
dst = strdup(src);
ASSERT_IsTrue(dst != NULL, "strdup returned NULL");
printf("strlens %zu %zu\n", strlen(src), strlen(dst));
ASSERT_IsTrue(strlen(dst) == strlen(src), "string lengths differ");
ASSERT_IsTrue(!strcmp(dst, src), "strings differ");
free(dst);
free(src);
src = malloc(LONG_STR_LEN+1);
ASSERT_IsTrue(src != NULL, "Could not allocate source string");
memset(src, 'a', LONG_STR_LEN-1);
src[LONG_STR_LEN] = '\0';
dst = strdup(src);
ASSERT_IsTrue(dst == NULL, "strdup did not return NULL");
free(src);
for (i = 254; i < 258; i++) {
src = malloc(i+1);
memset(src, 'a', i-1);
src[i] = '\0';
dst = strdup(src);
ASSERT_IsTrue(dst != NULL, "strdup returned NULL");
ASSERT_IsTrue(strlen(dst) == strlen(src), "string lengths differ");
ASSERT_IsTrue(!strcmp(dst, src), "strings differ");
free (dst);
free(src);
}
}
ENDTEST