Added a --extended-libc option and some headers to allow porting external programs.

This commit is contained in:
lephe 2017-01-22 18:35:02 +01:00
parent 765bb97ee7
commit 7a888fc90a
15 changed files with 412 additions and 58 deletions

2
.gitignore vendored
View file

@ -18,3 +18,5 @@ gintdemo.g1a
# Configuration files # Configuration files
gcc.cfg gcc.cfg
Makefile.cfg

View file

@ -5,7 +5,7 @@
# #
#--- #---
include Makefile.cfg
#--- #---
# Project variables. # Project variables.
@ -48,7 +48,10 @@ obj-lib-spec = build/display_font_system.bmp.o
obj-std-spec = obj-std-spec =
# Configuration files # Configuration files
config = gcc.cfg config = gcc.cfg
ifeq ($(wildcard $(config)),)
$(error "Configuration files are missing. Did you ./configure ?")
endif
# Target folder # Target folder
folder := $(shell fxsdk --folder) folder := $(shell fxsdk --folder)
@ -127,16 +130,13 @@ endef
all: $(config) $(target-std) $(target-lib) $(target-g1a) all: $(config) $(target-std) $(target-lib) $(target-g1a)
@ printf '\e[32;1mmsg \u00bb\e[0m All done!\n' @ printf '\e[32;1mmsg \u00bb\e[0m All done!\n'
$(config):
$(error "Configuration files are missing. Did you ./configure?")
build: build:
$(if $(VERBOSE),,@ printf '\e[35;1mdir \u00bb\e[0m mkdir $@\n') $(if $(VERBOSE),,@ printf '\e[35;1mdir \u00bb\e[0m mkdir $@\n')
$(if $(VERBOSE),,@) mkdir -p $@ $(if $(VERBOSE),,@) mkdir -p $@
$(obj-std) $(obj-lib) $(demo-obj): | build $(obj-std) $(obj-lib) $(demo-obj): | build
$(target-std): $(config) $(obj-std) $(target-std): $(obj-std)
$(if $(VERBOSE),,@ printf '\e[35;1mlib \u00bb\e[0m ar $@\n') $(if $(VERBOSE),,@ printf '\e[35;1mlib \u00bb\e[0m ar $@\n')
$(if $(VERBOSE),,@) $(ar) rcs $@ $(obj-std) $(if $(VERBOSE),,@) $(ar) rcs $@ $(obj-std)
@ printf '\e[32;1mmsg \u00bb\e[0m Succesfully built libc (' @ printf '\e[32;1mmsg \u00bb\e[0m Succesfully built libc ('
@ -217,6 +217,9 @@ install: $(target-std) $(target-lib)
install -m 644 -T demo/gintdemo.ld $(folder)/linker.ld install -m 644 -T demo/gintdemo.ld $(folder)/linker.ld
mkdir -p $(folder)/gint mkdir -p $(folder)/gint
install -m 644 include/*.h $(folder)/gint install -m 644 include/*.h $(folder)/gint
ifdef config_ext
install -m 644 include/extended/*.h $(folder)/gint
endif
@ printf '\e[32;1mmsg \u00bb\e[0m All installed!\n' @ printf '\e[32;1mmsg \u00bb\e[0m All installed!\n'
install_demo: install_demo:

2
TODO
View file

@ -12,6 +12,8 @@ Simple improvements:
- core: Add VBR handlers debugging information (if possible) - core: Add VBR handlers debugging information (if possible)
- events: Introduce KeyRepeat events - events: Introduce KeyRepeat events
- library: Implement C99's inttypes.h for Cake's UpdateExe - library: Implement C99's inttypes.h for Cake's UpdateExe
- string: Use cmp/str to implement memchr() (assembler examples)
- string: Do some tests for memcmp()
Larger improvements: Larger improvements:
- errno: Introduce errno and use it more or less everywhere - errno: Introduce errno and use it more or less everywhere
- bopti: Monochrome bitmaps blending modes - bopti: Monochrome bitmaps blending modes

50
configure vendored
View file

@ -5,8 +5,16 @@ conf[ATEXIT_MAX]=16
conf[RTC_CB_ARRAY_SIZE]=5 conf[RTC_CB_ARRAY_SIZE]=5
conf[EVENTS_QUEUE_SIZE]=64 conf[EVENTS_QUEUE_SIZE]=64
conf[GINT_NO_SYSCALLS]= conf[GINT_NO_SYSCALLS]=
conf[GINT_EXTENDED_LIBC]=
fail=false fail=false
output="gcc.cfg" output_gcc="gcc.cfg"
output_make="Makefile.cfg"
error="\e[31;1merror:\e[0m"
Cr="$(tput setaf 1)$(tput bold)"
Cg="$(tput setaf 2)$(tput bold)"
Cp="$(tput setaf 5)$(tput setaf 62)$(tput bold)"
C0="$(tput setaf 0)$(tput sgr 0)"
help() help()
{ {
@ -14,16 +22,19 @@ help()
Configuration script for the gint library. Configuration script for the gint library.
Options that affect the behavior of the library: Options that affect the behavior of the library:
--no-syscalls [default: false] $Cr--no-syscalls$C0 [default:$Cp false$C0]
Never use syscalls. Expect some trouble with the malloc() function... Never use syscalls. Expect some trouble with the malloc() function...
Do not trigger this option unless you know what you are doing. Do not enable this option unless you know what you are doing.
$Cr--extended-libc$C0 [default:$Cp false$C0]
Enable specific C99 headers/features that are normally not required by
calculator programs. May allow porting programs from other platforms.
Options that customize size limits: Options that customize size limits:
--atexit-max=<integer> [default: 16] $Cr--atexit-max=$C0$Cg<integer>$C0 [default:$Cp 16$C0]
Number of exit handlers that can be registered by atexit(). Number of exit handlers that can be registered by atexit().
--rtc-callbacks=<integer> [default: 5] $Cr--rtc-callbacks=$C0$Cg<integer>$C0 [default:$Cp 5$C0]
Number of RTC callbacks that can be registered. Number of RTC callbacks that can be registered.
--events-queue-size=<integer> [default: 64] $Cr--events-queue-size=$C0$Cg<integer>$C0 [default:$Cp 64$C0]
Number of events simultaneously stored in the event queue. Number of events simultaneously stored in the event queue.
EOF EOF
@ -33,34 +44,36 @@ EOF
for arg; do case "$arg" in for arg; do case "$arg" in
-h | --help) help;; -h | --help) help;;
--no-syscalls) conf[GINT_NO_SYSCALLS]=true;; --no-syscalls) conf[GINT_NO_SYSCALLS]=true;;
--extended-libc) conf[GINT_EXTENDED_LIBC]=true;;
--atexit-max=*) --atexit-max=*)
size=${arg#*=} size=${arg#*=}
if [[ $size == +([0-9]) ]]; then if [[ $size == +([0-9]) ]]; then
conf[ATEXIT_MAX]=$size conf[ATEXIT_MAX]=$size
else echo "error: --atexit-max expects an integer value" else echo -e "$error --atexit-max expects an integer value"
fail=true; fi;; fail=true; fi;;
--rtc-callbacks=*) --rtc-callbacks=*)
size=${arg#*=} size=${arg#*=}
if [[ $size == +([0-9]) ]]; then if [[ $size == +([0-9]) ]]; then
conf[RTC_CB_ARRAY_SIZE]=$size conf[RTC_CB_ARRAY_SIZE]=$size
else echo "error: --rtc-callbacks expects an integer value" else echo -e "$error --rtc-callbacks expects an integer value"
fail=true; fi;; fail=true; fi;;
--events-queue-size=*) --events-queue-size=*)
size=${arg#*=} size=${arg#*=}
if [[ $size == +([0-9]) ]]; then if [[ $size == +([0-9]) ]]; then
conf[EVENTS_QUEUE_SIZE]=$size conf[EVENTS_QUEUE_SIZE]=$size
else echo "error: --events-queue-size expects an integer value" else echo -e "$error --events-queue-size expects an integer"\
"value"
fail=true; fi;; fail=true; fi;;
--atexit-max | --rtc-callbacks | --events-queue-size) --atexit-max | --rtc-callbacks | --events-queue-size)
echo "error: syntax for $arg is $arg=<integer-value>";; echo -e "$error syntax for $arg is $arg=<integer-value>";;
*) *)
echo "error: unrecognized argument '$arg'"; fail=true;; echo -e "$error unrecognized argument '$arg'"; fail=true;;
esac; done esac; done
output_config() output_config_gcc()
{ {
echo "-D ATEXIT_MAX=${conf[ATEXIT_MAX]}" echo "-D ATEXIT_MAX=${conf[ATEXIT_MAX]}"
echo "-D RTC_CB_ARRAY_SIZE=${conf[RTC_CB_ARRAY_SIZE]}" echo "-D RTC_CB_ARRAY_SIZE=${conf[RTC_CB_ARRAY_SIZE]}"
@ -68,11 +81,20 @@ output_config()
if [ "${conf[GINT_NO_SYSCALLS]}" != "" ]; then if [ "${conf[GINT_NO_SYSCALLS]}" != "" ]; then
echo "-D GINT_NO_SYSCALLS" echo "-D GINT_NO_SYSCALLS"
fi fi
if [ "${conf[GINT_EXTENDED_LIBC]}" != "" ]; then
echo "-D GINT_EXTENDED_LIBC"
fi
}
output_config_make()
{
[ "${conf[GINT_EXTENDED_LIBC]}" != "" ] && echo "config_ext=true"
} }
if $fail; then if $fail; then
echo "Configuration has not been modified." echo "Configuration has not been modified."
else else
output_config > $output output_config_gcc > $output_gcc
echo "Configuration details have been output to file $output." output_config_make > $output_make
echo "Configuration saved in $output_gcc and $output_make."
fi fi

View file

@ -9,27 +9,76 @@
#ifndef _CTYPE_H #ifndef _CTYPE_H
#define _CTYPE_H 1 #define _CTYPE_H 1
// Character definition macros. #include <stdint.h>
#define isalnum(c) (isdigit(c) || isalpha(c))
#define isalpha(c) (islower(c) || isupper(c))
#define iscntrl(c) ((c) <= 0x1f || (c) == 0x7f) //---
#define isdigit(c) ((c) >= '0' && (c) <= '9') // Character classes.
#define isgraph(c) ((c) > ' ' && (c) < 0x7f) //---
#define islower(c) ((c) >= 'a' && (c) <= 'z')
#define isprint(c) ((c) >= ' ' && (c) < 0x7f)
#define ispunct(c) (((c) >= '!' && (c) <= '/') || \
((c) >= ':' && (c) <= '@') || \
((c) >= '[' && (c) <= '`') || \
((c) >= '{' && (c) <= '~'))
#define isspace(c) (((c) >= '\t' && (c) <= '\r') || (c) == ' ')
#define isupper(c) ((c) >= 'A' && (c) <= 'Z')
#define isxdigit(c) (((c) >= '0' && (c) <= '9') || \
((c) >= 'A' && (c) <= 'F') || \
((c) >= 'a' && (c) <= 'f'))
// Character manipulation macros. extern uint8_t ctype_classes[0x80];
#define tolower(c) (isupper(c) ? (c) | 32 : (c))
#define toupper(c) (islower(c) ? (c) & ~32 : (c)) __attribute__((always_inline)) static inline int isalnum(int c) {
return ctype_classes[c] & 0xf0;
}
__attribute__((always_inline)) static inline int isalpha(int c) {
return ctype_classes[c] & 0x30;
}
__attribute__((always_inline)) static inline int iscntrl(int c) {
return ctype_classes[c] & 0x01;
}
__attribute__((always_inline)) static inline int isdigit(int c) {
return ctype_classes[c] & 0x40;
}
__attribute__((always_inline)) static inline int isgraph(int c) {
return ctype_classes[c] & 0xf4;
}
__attribute__((always_inline)) static inline int islower(int c) {
return ctype_classes[c] & 0x10;
}
__attribute__((always_inline)) static inline int isprint(int c) {
return ctype_classes[c] & 0x08;
}
__attribute__((always_inline)) static inline int ispunct(int c) {
return ctype_classes[c] & 0x04;
}
__attribute__((always_inline)) static inline int isspace(int c) {
return ctype_classes[c] & 0x02;
}
__attribute__((always_inline)) static inline int isupper(int c) {
return ctype_classes[c] & 0x20;
}
__attribute__((always_inline)) static inline int isxdigit(int c) {
return ctype_classes[c] & 0x80;
}
__attribute__((always_inline)) static inline int isascii(int c) {
return (c >= 0 && c <= 0x7f);
}
__attribute__((always_inline)) static inline int isblank(int c) {
return (c == '\t' || c == ' ');
}
//---
// Character manipulation.
//---
__attribute__((always_inline)) static inline int tolower(int c) {
return c | isupper(c);
}
__attribute__((always_inline)) static inline int toupper(int c) {
return c & ~(islower(c) << 1);
}
#endif // _CTYPE_H #endif // _CTYPE_H

46
include/extended/endian.h Normal file
View file

@ -0,0 +1,46 @@
#ifndef _ENDIAN_H
#define _ENDIAN_H
#include <stdint.h>
//---
// Assembler-optimized byte-ordering functions.
//---
__attribute__((always_inline)) static inline uint16_t swap16(uint16_t word)
{
uint16_t result;
__asm__("swap.b %1, %0": "=r"(result): "r"(word));
return result;
}
__attribute__((always_inline)) static inline uint32_t swap32(uint32_t longw)
{
uint32_t result;
__asm__(
"swap.b %1, r0 \n\t"
"swap.w r0, r0 \n\t"
"swap.b r0, %0 \n\t"
: "=r"(result)
: "r"(longw)
);
return result;
}
//---
// Conversion of values of different endianness.
//---
#define htobe16(host16) (host16)
#define htole16(host16) (swap16(host16))
#define be16toh(be16) (be16)
#define le16toh(le16) (swap16(le16))
#define htobe32(host32) (host32)
#define htole32(host32) (swap32(host32))
#define be32toh(be32) (be32)
#define le32toh(le32) (swap32(le32))
#endif // _ENDIAN_H

View file

@ -17,18 +17,34 @@
//--- //---
/* /*
memcpy() O(byte_number) memcpy() O(byte_count)
Copies a memory area. The two areas must not overlap (if they do, use Copies a memory area. The two areas must not overlap (if they do, use
memmove()). A smart copy is performed when possible. To enhance memmove()). A smart copy is performed when possible. To enhance
performance, make sure than destination and source are both 4-aligned. performance, make sure than destination and source are both 4-aligned.
*/ */
void *memcpy(void *destination, const void *source, size_t byte_number); void *memcpy(void *destination, const void *source, size_t byte_count);
/* /*
memset() O(byte_number) memset() O(byte_count)
Sets the contents of a memory area. A smart copy is performed. Sets the contents of a memory area. A smart copy is performed.
*/ */
void *memset(void *destination, int byte, size_t byte_number); void *memset(void *destination, int byte, size_t byte_count);
/*
memchr() O(byte_count)
Looks for a byte in a memory area. Returns the address of the first
occurrence if found, NULL otherwise.
*/
void *memchr(const void *area, int byte, size_t byte_count);
/*
memcmp() O(byte_count)
Compares two memory areas. Returns 0 if all bytes are equal in both
areas, a negative number if the first unequal byte is lower in the
first area, and a positive number otherwise.
A smart comparison is performed when possible.
*/
int memcmp(const void *area1, const void *area2, size_t byte_count);
@ -42,12 +58,25 @@ void *memset(void *destination, int byte, size_t byte_number);
*/ */
size_t strlen(const char *str); size_t strlen(const char *str);
/*
strnlen() O(len(str))
Returns the minimum of the length of the string and n. This function
never access more than n bytes at the beginning of the string.
*/
size_t strnlen(const char *str, size_t n);
/* /*
strcpy() O(len(source)) strcpy() O(len(source))
Copies a string to another. Copies a string to another.
*/ */
char *strcpy(char *destination, const char *source); char *strcpy(char *destination, const char *source);
/*
strncpy() O(min(len(source), size))
Copies part of a string to another.
*/
char *strncpy(char *destination, const char *source, size_t size);
/* /*
strchr() O(len(str)) strchr() O(len(str))
Searches a character in a string. Searches a character in a string.
@ -55,9 +84,11 @@ char *strcpy(char *destination, const char *source);
const char *strchr(const char *str, int value); const char *strchr(const char *str, int value);
/* /*
strncpy() O(min(len(source), size)) strcmp() O(max(len(str1), len(str2)))
Copies part of a string to another. Compares two strings. Returns 0 if they are identical, a negative
number if the first unequal byte is lower in str1 than in str2, and a
positive number otherwise.
*/ */
char *strncpy(char *destination, const char *source, size_t size); int strcmp(const char *str1, const char *str2);
#endif // _STRING_H #endif // _STRING_H

64
src/string/ctype.c Normal file
View file

@ -0,0 +1,64 @@
#include <ctype.h>
enum {
cntrl = 0x01,
space = 0x02,
punct = 0x04,
print = 0x08,
upper = 0x20,
lower = 0x10,
digit = 0x40,
xdigt = 0x80,
};
uint8_t ctype_classes[0x80] = {
// Control characters.
cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl,
cntrl | space, // Tabulation
cntrl | space, cntrl | space, cntrl | space, cntrl | space,
cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl,
cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl,
// Space and some punctuation.
space | print,
punct | print, punct | print, punct | print, punct | print,
punct | print, punct | print, punct | print, punct | print,
punct | print, punct | print, punct | print, punct | print,
punct | print, punct | print, punct | print,
// Decimal digits.
digit | xdigt | print, digit | xdigt | print, digit | xdigt | print,
digit | xdigt | print, digit | xdigt | print, digit | xdigt | print,
digit | xdigt | print, digit | xdigt | print, digit | xdigt | print,
digit | xdigt | print,
// Some punctuation.
punct | print, punct | print, punct | print, punct | print,
punct | print, punct | print, punct | print,
// Uppercase alphabet.
upper | xdigt | print, upper | xdigt | print, upper | xdigt | print,
upper | xdigt | print, upper | xdigt | print, upper | xdigt | print,
upper | print, upper | print, upper | print, upper | print,
upper | print, upper | print, upper | print, upper | print,
upper | print, upper | print, upper | print, upper | print,
upper | print, upper | print, upper | print, upper | print,
upper | print, upper | print, upper | print, upper | print,
// Other punctuation symbols.
punct | print, punct | print, punct | print, punct | print,
punct | print, punct | print,
// Lowercase alphabet.
lower | xdigt | print, lower | xdigt | print, lower | xdigt | print,
lower | xdigt | print, lower | xdigt | print, lower | xdigt | print,
lower | print, lower | print, lower | print, lower | print,
lower | print, lower | print, lower | print, lower | print,
lower | print, lower | print, lower | print, lower | print,
lower | print, lower | print, lower | print, lower | print,
lower | print, lower | print, lower | print, lower | print,
// Last punctuation characters and DEL.
punct | print, punct | print, punct | print, punct | print,
cntrl
};

20
src/string/memchr.c Normal file
View file

@ -0,0 +1,20 @@
#include <string.h>
#include <stdint.h>
/*
memchr() O(byte_count)
Looks for a byte in a memory area. Returns the address of the first
occurrence if found, NULL otherwise.
*/
void *memchr(const void *area, int byte, size_t byte_count)
{
const uint8_t *mem = area;
while(byte_count--)
{
if(*mem == byte) return (void *)mem;
mem++;
}
return NULL;
}

84
src/string/memcmp.c Normal file
View file

@ -0,0 +1,84 @@
#include <string.h>
#include <stdint.h>
int raw_cmp(const uint8_t *mem1, const uint8_t *mem2, size_t byte_count);
/*
memcmp() O(byte_count)
Compares two memory areas. Returns 0 if all bytes are equal in both
areas, a negative number if the first unequal byte is lower in the
first area, and a positive number otherwise.
A smart comparison is performed when possible.
*/
int memcmp(const void *area1, const void *area2, size_t byte_count)
{
const uint8_t *mem1 = (const uint8_t *)area1;
const uint8_t *mem2 = (const uint8_t *)area2;
// Trying to do long-based comparisons.
if(((intptr_t)area1 & 3) == ((intptr_t)area2 & 3))
{
// Getting to a 4-byte offset.
while((intptr_t)mem1 & 3)
{
if(*mem1 != *mem2) return *mem1 - *mem2;
mem1++, mem2++;
byte_count--;
}
// Testing groups of four bytes.
while(byte_count >= 4)
{
uint32_t long1 = *((uint32_t *)mem1);
uint32_t long2 = *((uint32_t *)mem2);
if(long1 != long2) return raw_cmp(mem1, mem2, 4);
mem1 += 4, mem2 += 4;
byte_count -= 4;
}
// Testing the last bytes.
return raw_cmp(mem1, mem2, byte_count);
}
// Well, maybe we can do a word-based operation?
else if(((intptr_t)area1 & 1) == ((intptr_t)area2 & 3))
{
// Getting to the word offset.
if((intptr_t)mem1 & 1)
{
if(*mem1 != *mem2) return *mem1 - *mem2;
mem1++, mem2++;
byte_count--;
}
// Testing groups of two bytes.
while(byte_count >= 2)
{
uint16_t word1 = *((uint16_t *)mem1);
uint16_t word2 = *((uint16_t *)mem2);
if(word1 != word2) return raw_cmp(mem1, mem2, 2);
mem1 += 2, mem2 += 2;
byte_count -= 2;
}
// Testing the last byte.
if(!byte_count) return 0;
return *mem1 - *mem2;
}
// That's too bad, we'll have to compare everything manually.
else return raw_cmp(mem1, mem2, byte_count);
}
int raw_cmp(const uint8_t *mem1, const uint8_t *mem2, size_t byte_count)
{
while(byte_count)
{
if(*mem1 != *mem2) return *mem1 - *mem2;
mem1++;
mem2++;
byte_count--;
}
return 0;
}

View file

@ -2,7 +2,7 @@
#include <stdint.h> #include <stdint.h>
/* /*
memcpy() memcpy() O(n)
Copies a memory area. A smart copy is performed if possible. Copies a memory area. A smart copy is performed if possible.
*/ */
void *memcpy(void *d, const void *s, size_t n) void *memcpy(void *d, const void *s, size_t n)

View file

@ -2,10 +2,10 @@
#include <stdint.h> #include <stdint.h>
/* /*
memset() memset() O(byte_count)
Sets the contents of a memory area. A smart copy is performed. Sets the contents of a memory area. A smart copy is performed.
*/ */
void *memset(void *d, int byte, size_t byte_number) void *memset(void *d, int byte, size_t byte_count)
{ {
uint8_t *dest = (uint8_t *)d; uint8_t *dest = (uint8_t *)d;
unsigned short word = (byte << 8) | byte; unsigned short word = (byte << 8) | byte;
@ -13,12 +13,12 @@ void *memset(void *d, int byte, size_t byte_number)
// When the area is small, simply copying using byte operations. The // When the area is small, simply copying using byte operations. The
// minimum length used for long operations must be at least 3. // minimum length used for long operations must be at least 3.
if(byte_number < 8) if(byte_count < 8)
{ {
while(byte_number) while(byte_count)
{ {
*dest++ = byte; *dest++ = byte;
byte_number--; byte_count--;
} }
return d; return d;
@ -28,20 +28,20 @@ void *memset(void *d, int byte, size_t byte_number)
while((intptr_t)dest & 3) while((intptr_t)dest & 3)
{ {
*dest++ = byte; *dest++ = byte;
byte_number--; byte_count--;
} }
// Copying using long operations. // Copying using long operations.
while(byte_number >= 4) while(byte_count >= 4)
{ {
*((uint32_t *)dest) = longword; *((uint32_t *)dest) = longword;
dest += 4; dest += 4;
byte_number -= 4; byte_count -= 4;
} }
// Ending the copy. // Ending the copy.
while(byte_number) while(byte_count)
{ {
*dest++ = byte; *dest++ = byte;
byte_number--; byte_count--;
} }
return d; return d;

18
src/string/strcmp.c Normal file
View file

@ -0,0 +1,18 @@
#include <string.h>
/*
strcmp() O(max(len(str1), len(str2)))
Compares two strings. Returns 0 if they are identical, a negative
number if the first unequal byte is lower in str1 than in str2, and a
positive number otherwise.
*/
int strcmp(const char *str1, const char *str2)
{
while(*str1 && *str2 && *str1 == *str2)
{
str1++;
str2++;
}
return *str1 - *str2;
}

13
src/string/strnlen.c Normal file
View file

@ -0,0 +1,13 @@
#include <string.h>
/*
strnlen() O(len(str))
Returns the minimum of the length of the string and n. This function
never access more than n bytes at the beginning of the string.
*/
size_t strnlen(const char *str, size_t n)
{
size_t len = 0;
while(len < n && str[len]) len++;
return len;
}