Changeset 29e7cc7 in mainline for uspace/lib/c


Ignore:
Timestamp:
2025-04-18T15:14:10Z (8 months ago)
Author:
Miroslav Cimerman <mc@…>
Children:
e77c3ed
Parents:
800d188 (diff), 25fdb2d (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge branch 'upstream/master' into helenraid

Location:
uspace/lib/c
Files:
1 added
2 deleted
13 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/c/arch/arm32/src/atomic.c

    r800d188 r29e7cc7  
    3838volatile unsigned *ras_page;
    3939
    40 bool __atomic_compare_exchange_4(volatile void *mem0, void *expected0,
    41     unsigned desired, bool weak, int success, int failure)
     40unsigned long long __atomic_load_8(const volatile void *mem0, int model)
     41{
     42        const volatile unsigned *mem = mem0;
     43
     44        (void) model;
     45
     46        union {
     47                unsigned long long a;
     48                unsigned b[2];
     49        } ret;
     50
     51        /*
     52         * The following instructions between labels 1 and 2 constitute a
     53         * Restartable Atomic Seqeunce. Should the sequence be non-atomic,
     54         * the kernel will restart it.
     55         */
     56        asm volatile (
     57            "1:\n"
     58            "   adr %[ret0], 1b\n"
     59            "   str %[ret0], %[rp0]\n"
     60            "   adr %[ret0], 2f\n"
     61            "   str %[ret0], %[rp1]\n"
     62
     63            "   ldr %[ret0], %[addr0]\n"
     64            "   ldr %[ret1], %[addr1]\n"
     65            "2:\n"
     66            : [ret0] "=&r" (ret.b[0]),
     67              [ret1] "=&r" (ret.b[1]),
     68              [rp0] "=m" (ras_page[0]),
     69              [rp1] "=m" (ras_page[1])
     70            : [addr0] "m" (mem[0]),
     71              [addr1] "m" (mem[1])
     72        );
     73
     74        ras_page[0] = 0;
     75        ras_page[1] = 0xffffffff;
     76
     77        return ret.a;
     78}
     79
     80void __atomic_store_8(volatile void *mem0, unsigned long long val, int model)
    4281{
    4382        volatile unsigned *mem = mem0;
    44         unsigned *expected = expected0;
     83
     84        (void) model;
     85
     86        union {
     87                unsigned long long a;
     88                unsigned b[2];
     89        } v;
     90
     91        v.a = val;
     92
     93        /* scratch register */
     94        unsigned tmp;
     95
     96        /*
     97         * The following instructions between labels 1 and 2 constitute a
     98         * Restartable Atomic Seqeunce. Should the sequence be non-atomic,
     99         * the kernel will restart it.
     100         */
     101        asm volatile (
     102            "1:\n"
     103            "   adr %[tmp], 1b\n"
     104            "   str %[tmp], %[rp0]\n"
     105            "   adr %[tmp], 2f\n"
     106            "   str %[tmp], %[rp1]\n"
     107
     108            "   str %[val0], %[addr0]\n"
     109            "   str %[val1], %[addr1]\n"
     110            "2:\n"
     111            : [tmp] "=&r" (tmp),
     112              [rp0] "=m" (ras_page[0]),
     113              [rp1] "=m" (ras_page[1]),
     114              [addr0] "=m" (mem[0]),
     115              [addr1] "=m" (mem[1])
     116            : [val0] "r" (v.b[0]),
     117              [val1] "r" (v.b[1])
     118        );
     119
     120        ras_page[0] = 0;
     121        ras_page[1] = 0xffffffff;
     122}
     123
     124bool __atomic_compare_exchange_1(volatile void *mem0, void *expected0,
     125    unsigned char desired, bool weak, int success, int failure)
     126{
     127        volatile unsigned char *mem = mem0;
     128        unsigned char *expected = expected0;
    45129
    46130        (void) success;
     
    48132        (void) weak;
    49133
     134        unsigned char ov = *expected;
     135        unsigned ret;
     136
     137        /*
     138         * The following instructions between labels 1 and 2 constitute a
     139         * Restartable Atomic Sequence. Should the sequence be non-atomic,
     140         * the kernel will restart it.
     141         */
     142        asm volatile (
     143            "1:\n"
     144            "   adr %[ret], 1b\n"
     145            "   str %[ret], %[rp0]\n"
     146            "   adr %[ret], 2f\n"
     147            "   str %[ret], %[rp1]\n"
     148
     149            "   ldrb %[ret], %[addr]\n"
     150            "   cmp %[ret], %[ov]\n"
     151            "   streqb %[nv], %[addr]\n"
     152            "2:\n"
     153            : [ret] "=&r" (ret),
     154              [rp0] "=m" (ras_page[0]),
     155              [rp1] "=m" (ras_page[1]),
     156              [addr] "+m" (*mem)
     157            : [ov] "r" (ov),
     158              [nv] "r" (desired)
     159        );
     160
     161        ras_page[0] = 0;
     162        ras_page[1] = 0xffffffff;
     163
     164        if (ret == ov)
     165                return true;
     166
     167        *expected = ret;
     168        return false;
     169}
     170
     171bool __atomic_compare_exchange_4(volatile void *mem0, void *expected0,
     172    unsigned desired, bool weak, int success, int failure)
     173{
     174        volatile unsigned *mem = mem0;
     175        unsigned *expected = expected0;
     176
     177        (void) success;
     178        (void) failure;
     179        (void) weak;
     180
    50181        unsigned ov = *expected;
    51182        unsigned ret;
  • uspace/lib/c/generic/async/ports.c

    r800d188 r29e7cc7  
    5050#include <as.h>
    5151#include <abi/mm/as.h>
    52 #include "../private/libc.h"
    5352#include "../private/fibril.h"
    5453
     
    115114}
    116115
    117 static bool interface_key_equal(const void *key, const ht_link_t *item)
     116static bool interface_key_equal(const void *key, size_t hash, const ht_link_t *item)
    118117{
    119118        const iface_t *iface = key;
     
    143142}
    144143
    145 static bool port_key_equal(const void *key, const ht_link_t *item)
     144static bool port_key_equal(const void *key, size_t hash, const ht_link_t *item)
    146145{
    147146        const port_id_t *port_id = key;
  • uspace/lib/c/generic/async/server.c

    r800d188 r29e7cc7  
    242242}
    243243
    244 static bool client_key_equal(const void *key, const ht_link_t *item)
     244static bool client_key_equal(const void *key, size_t, const ht_link_t *item)
    245245{
    246246        const task_id_t *in_task_id = key;
     
    502502}
    503503
    504 static bool notification_key_equal(const void *key, const ht_link_t *item)
     504static bool notification_key_equal(const void *key, size_t hash, const ht_link_t *item)
    505505{
    506506        const sysarg_t *id = key;
  • uspace/lib/c/generic/io/asprintf.c

    r800d188 r29e7cc7  
    4040#include <str.h>
    4141#include <printf_core.h>
    42 
    43 static int asprintf_str_write(const char *str, size_t count, void *unused)
    44 {
    45         return str_nlength(str, count);
    46 }
    47 
    48 static int asprintf_wstr_write(const char32_t *str, size_t count, void *unused)
    49 {
    50         return wstr_nlength(str, count);
    51 }
    52 
    53 int vprintf_length(const char *fmt, va_list args)
    54 {
    55         printf_spec_t ps = {
    56                 asprintf_str_write,
    57                 asprintf_wstr_write,
    58                 NULL
    59         };
    60 
    61         return printf_core(fmt, &ps, args);
    62 }
    63 
    64 int printf_length(const char *fmt, ...)
    65 {
    66         va_list args;
    67         va_start(args, fmt);
    68         int ret = vprintf_length(fmt, args);
    69         va_end(args);
    70 
    71         return ret;
    72 }
    7342
    7443/** Allocate and print to string.
     
    11584        int ret = vasprintf(strp, fmt, args);
    11685        va_end(args);
    117 
    11886        return ret;
    11987}
  • uspace/lib/c/generic/io/kio.c

    r800d188 r29e7cc7  
    3434 */
    3535
     36#include <abi/syscall.h>
    3637#include <stddef.h>
    3738#include <libc.h>
     
    112113}
    113114
     115size_t kio_read(char *buf, size_t n, size_t at)
     116{
     117        return __SYSCALL3(SYS_KIO_READ, (sysarg_t) buf, n, at);
     118}
     119
    114120/** Print formatted text to kio.
    115121 *
     
    131137}
    132138
    133 static int kio_vprintf_str_write(const char *str, size_t size, void *data)
     139static errno_t kio_vprintf_str_write(const char *str, size_t size, void *data)
    134140{
    135         size_t wr;
    136 
    137         wr = 0;
    138         (void) kio_write(str, size, &wr);
    139         return str_nlength(str, wr);
    140 }
    141 
    142 static int kio_vprintf_wstr_write(const char32_t *str, size_t size, void *data)
    143 {
    144         size_t offset = 0;
    145         size_t chars = 0;
    146         size_t wr;
    147 
    148         while (offset < size) {
    149                 char buf[STR_BOUNDS(1)];
    150                 size_t sz = 0;
    151 
    152                 if (chr_encode(str[chars], buf, &sz, STR_BOUNDS(1)) == EOK)
    153                         kio_write(buf, sz, &wr);
    154 
    155                 chars++;
    156                 offset += sizeof(char32_t);
    157         }
    158 
    159         return chars;
     141        size_t wr = 0;
     142        return kio_write(str, size, &wr);
    160143}
    161144
     
    172155        printf_spec_t ps = {
    173156                kio_vprintf_str_write,
    174                 kio_vprintf_wstr_write,
    175157                NULL
    176158        };
  • uspace/lib/c/generic/io/vprintf.c

    r800d188 r29e7cc7  
    4242static FIBRIL_MUTEX_INITIALIZE(printf_mutex);
    4343
    44 static int vprintf_str_write(const char *str, size_t size, void *stream)
     44static errno_t vprintf_str_write(const char *str, size_t size, void *stream)
    4545{
    46         size_t wr = fwrite(str, 1, size, (FILE *) stream);
    47         return str_nlength(str, wr);
    48 }
     46        errno_t old_errno = errno;
    4947
    50 static int vprintf_wstr_write(const char32_t *str, size_t size, void *stream)
    51 {
    52         size_t offset = 0;
    53         size_t chars = 0;
     48        errno = EOK;
     49        size_t written = fwrite(str, 1, size, (FILE *) stream);
    5450
    55         while (offset < size) {
    56                 if (fputuc(str[chars], (FILE *) stream) <= 0)
    57                         break;
     51        if (errno == EOK && written != size)
     52                errno = EIO;
    5853
    59                 chars++;
    60                 offset += sizeof(char32_t);
    61         }
     54        if (errno != EOK)
     55                return errno;
    6256
    63         return chars;
     57        errno = old_errno;
     58        return EOK;
    6459}
    6560
     
    7772        printf_spec_t ps = {
    7873                vprintf_str_write,
    79                 vprintf_wstr_write,
    8074                stream
    8175        };
  • uspace/lib/c/include/io/kio.h

    r800d188 r29e7cc7  
    3636#define _LIBC_IO_KIO_H_
    3737
    38 #include <stddef.h>
    39 #include <stdarg.h>
    40 #include <io/verify.h>
    4138#include <_bits/errno.h>
    4239#include <_bits/size_t.h>
     40#include <io/verify.h>
     41#include <stdarg.h>
     42#include <stddef.h>
     43#include <uchar.h>
    4344
    4445extern void __kio_init(void);
     
    5051    _HELENOS_PRINTF_ATTRIBUTE(1, 2);
    5152extern int kio_vprintf(const char *, va_list);
     53
     54extern size_t kio_read(char *buf, size_t n, size_t at);
    5255
    5356/*
  • uspace/lib/c/include/stdio.h

    r800d188 r29e7cc7  
    209209};
    210210
    211 extern int vprintf_length(const char *, va_list);
    212 extern int printf_length(const char *, ...)
    213     _HELENOS_PRINTF_ATTRIBUTE(1, 2);
    214211extern FILE *fdopen(int, const char *);
    215212extern int fileno(FILE *);
  • uspace/lib/c/meson.build

    r800d188 r29e7cc7  
    6262        'common/adt/checksum.c',
    6363        'common/adt/circ_buf.c',
     64        'common/adt/hash_table.c',
    6465        'common/adt/list.c',
    65         'common/adt/hash_table.c',
    6666        'common/adt/odict.c',
     67        'common/gsort.c',
    6768        'common/printf/printf_core.c',
     69        'common/stdc/bsearch.c',
     70        'common/stdc/calloc.c',
    6871        'common/stdc/ctype.c',
    6972        'common/stdc/mem.c',
    70         'common/stdc/bsearch.c',
    7173        'common/stdc/qsort.c',
    72         'common/stdc/calloc.c',
    73         'common/gsort.c',
     74        'common/stdc/snprintf.c',
     75        'common/stdc/uchar.c',
     76        'common/stdc/vsnprintf.c',
     77        'common/stdc/wchar.c',
    7478        'common/str.c',
    7579        'common/str_error.c',
    7680        'common/strtol.c',
    7781
    78         'generic/libc.c',
    7982        'generic/adt/prodcons.c',
     83        'generic/arg_parse.c',
    8084        'generic/as.c',
    81         'generic/ddi.c',
    82         'generic/perm.c',
     85        'generic/assert.c',
     86        'generic/async/client.c',
     87        'generic/async/ports.c',
     88        'generic/async/server.c',
    8389        'generic/capa.c',
    8490        'generic/config.c',
    8591        'generic/context.c',
    8692        'generic/dbgcon.c',
     93        'generic/ddi.c',
    8794        'generic/device/clock_dev.c',
    8895        'generic/device/hw_res.c',
     
    9198        'generic/dirent.c',
    9299        'generic/dlfcn.c',
     100        'generic/double_to_str.c',
    93101        'generic/elf/elf.c',
    94102        'generic/elf/elf_load.c',
    95103        'generic/elf/elf_mod.c',
     104        'generic/errno.c',
    96105        'generic/event.c',
    97         'generic/errno.c',
     106        'generic/getopt.c',
     107        'generic/ieee_double.c',
     108        'generic/imath.c',
    98109        'generic/inttypes.c',
    99         'generic/loc.c',
    100         'generic/string.c',
    101         'generic/l18n/langs.c',
    102         'generic/pcb.c',
    103         'generic/pio_trace.c',
    104         'generic/smc.c',
    105         'generic/task.c',
    106         'generic/imath.c',
    107110        'generic/io/asprintf.c',
    108111        'generic/io/io.c',
    109         'generic/io/printf.c',
     112        'generic/io/kio.c',
     113        'generic/io/klog.c',
    110114        'generic/io/log.c',
    111115        'generic/io/logctl.c',
    112         'generic/io/kio.c',
    113         'generic/io/klog.c',
    114         'generic/io/snprintf.c',
     116        'generic/io/printf.c',
     117        'generic/io/table.c',
    115118        'generic/io/vprintf.c',
    116         'generic/io/vsnprintf.c',
    117         'generic/io/table.c',
     119        'generic/ipc.c',
    118120        'generic/irq.c',
    119         'generic/ieee_double.c',
     121        'generic/l18n/langs.c',
     122        'generic/libc.c',
     123        'generic/loader.c',
     124        'generic/loc.c',
     125        'generic/malloc.c',
     126        'generic/ns.c',
     127        'generic/pcb.c',
     128        'generic/perm.c',
     129        'generic/pio_trace.c',
    120130        'generic/power_of_ten.c',
    121         'generic/double_to_str.c',
    122         'generic/malloc.c',
    123131        'generic/rndgen.c',
     132        'generic/setjmp.c',
    124133        'generic/shutdown.c',
     134        'generic/smc.c',
     135        'generic/stack.c',
     136        'generic/stacktrace.c',
     137        'generic/stats.c',
     138        'generic/stdio.c',
    125139        'generic/stdio/scanf.c',
    126140        'generic/stdio/sprintf.c',
     
    128142        'generic/stdio/sstream.c',
    129143        'generic/stdio/vsprintf.c',
     144        'generic/stdlib.c',
     145        'generic/string.c',
     146        'generic/sysinfo.c',
     147        'generic/task.c',
    130148        'generic/thread/fibril.c',
    131149        'generic/thread/fibril_synch.c',
     150        'generic/thread/futex.c',
     151        'generic/thread/mpsc.c',
    132152        'generic/thread/thread.c',
    133153        'generic/thread/tls.c',
    134         'generic/thread/futex.c',
    135         'generic/thread/mpsc.c',
    136         'generic/sysinfo.c',
    137         'generic/ipc.c',
    138         'generic/ns.c',
    139         'generic/async/client.c',
    140         'generic/async/server.c',
    141         'generic/async/ports.c',
    142         'generic/loader.c',
    143         'generic/getopt.c',
    144154        'generic/time.c',
    145155        'generic/tmpfile.c',
    146         'generic/stdio.c',
    147         'generic/stdlib.c',
     156        'generic/ubsan.c',
    148157        'generic/udebug.c',
     158        'generic/uuid.c',
    149159        'generic/vfs/canonify.c',
    150160        'generic/vfs/inbox.c',
    151161        'generic/vfs/mtab.c',
    152162        'generic/vfs/vfs.c',
    153         'generic/setjmp.c',
    154         'generic/stack.c',
    155         'generic/stacktrace.c',
    156         'generic/arg_parse.c',
    157         'generic/stats.c',
    158         'generic/assert.c',
    159         'generic/ubsan.c',
    160         'generic/uuid.c',
    161163)
    162164
    163165if CONFIG_RTLD
    164166        src += files(
    165                 'generic/rtld/rtld.c',
    166167                'generic/rtld/dynamic.c',
    167168                'generic/rtld/module.c',
     169                'generic/rtld/rtld.c',
    168170                'generic/rtld/symbol.c',
    169171        )
     
    190192        'test/qsort.c',
    191193        'test/sprintf.c',
     194        'test/stdio.c',
    192195        'test/stdio/scanf.c',
    193         'test/stdio.c',
    194196        'test/stdlib.c',
    195197        'test/str.c',
    196198        'test/string.c',
    197199        'test/strtol.c',
     200        'test/uchar.c',
    198201        'test/uuid.c',
    199202)
  • uspace/lib/c/test/adt/odict.c

    r800d188 r29e7cc7  
    233233
    234234                e->key = v;
    235                 odict_insert(&e->odict, &odict, &ep->odict);
     235                odict_insert(&e->odict, &odict, ep ? &ep->odict : NULL);
    236236                PCUT_ASSERT_ERRNO_VAL(EOK, odict_validate(&odict));
    237237                v = seq_next(v);
  • uspace/lib/c/test/main.c

    r800d188 r29e7cc7  
    5757PCUT_IMPORT(strtol);
    5858PCUT_IMPORT(table);
     59PCUT_IMPORT(uchar);
    5960PCUT_IMPORT(uuid);
    6061
  • uspace/lib/c/test/sprintf.c

    r800d188 r29e7cc7  
    11/*
    22 * Copyright (c) 2014 Vojtech Horky
     3 * Copyright (c) 2025 Jiří Zárevúcky
    34 * All rights reserved.
    45 *
     
    3132#include <pcut/pcut.h>
    3233
     34#pragma GCC diagnostic ignored "-Wformat"
     35
    3336#define BUFFER_SIZE 8192
    3437#define TEQ(expected, actual) PCUT_ASSERT_STR_EQUALS(expected, actual)
     
    3639
    3740#define SPRINTF_TEST(test_name, expected_string, actual_format, ...) \
    38         PCUT_TEST(test_name) { \
     41        PCUT_TEST(printf_##test_name) { \
    3942                snprintf(buffer, BUFFER_SIZE, actual_format, ##__VA_ARGS__); \
    4043                PCUT_ASSERT_STR_EQUALS(expected_string, buffer); \
     
    7982    (long long) -5);
    8083
    81 SPRINTF_TEST(int_as_hex, "[0x11] [0x012] [0x013] [0x00014] [0x00015]",
    82     "[%#x] [%#5.3x] [%#-5.3x] [%#3.5x] [%#-3.5x]",
    83     17, 18, 19, 20, 21);
     84SPRINTF_TEST(int_as_hex, "[1a] [  02b] [03c  ] [    04d] [05e    ] [0006f] [00080]",
     85    "[%x] [%5.3x] [%-5.3x] [%7.3x] [%-7.3x] [%3.5x] [%-3.5x]",
     86    26, 43, 60, 77, 94, 111, 128);
     87
     88SPRINTF_TEST(int_as_hex_alt, "[0x1a] [0x02b] [0x03c] [  0x04d] [0x05e  ] [0x0006f] [0x00080]",
     89    "[%#x] [%#5.3x] [%#-5.3x] [%#7.3x] [%#-7.3x] [%#3.5x] [%#-3.5x]",
     90    26, 43, 60, 77, 94, 111, 128);
     91
     92SPRINTF_TEST(int_as_hex_uc, "[1A] [  02B] [03C  ] [    04D] [05E    ] [0006F] [00080]",
     93    "[%X] [%5.3X] [%-5.3X] [%7.3X] [%-7.3X] [%3.5X] [%-3.5X]",
     94    26, 43, 60, 77, 94, 111, 128);
     95
     96SPRINTF_TEST(int_as_hex_alt_uc, "[0X1A] [0X02B] [0X03C] [  0X04D] [0X05E  ] [0X0006F] [0X00080]",
     97    "[%#X] [%#5.3X] [%#-5.3X] [%#7.3X] [%#-7.3X] [%#3.5X] [%#-3.5X]",
     98    26, 43, 60, 77, 94, 111, 128);
     99
     100SPRINTF_TEST(max_negative, "-9223372036854775808", "%" PRId64, INT64_MIN);
     101
     102SPRINTF_TEST(sign1, "[12] [ 12] [+12] [+12] [+12] [+12]",   "[%d] [% d] [%+d] [% +d] [%+ d] [%++ ++    +  ++++d]", 12, 12, 12, 12, 12, 12);
     103SPRINTF_TEST(sign2, "[-12] [-12] [-12] [-12] [-12] [-12]", "[%d] [% d] [%+d] [% +d] [%+ d] [%++ ++    +  ++++d]", -12, -12, -12, -12, -12, -12);
     104
     105/* When zero padding and precision and/or left justification are both specified, zero padding is ignored. */
     106SPRINTF_TEST(zero_left_padding, "[    0012] [0034    ] [56      ]", "[%08.4d] [%-08.4d] [%-08d]", 12, 34, 56);
     107
     108/* Zero padding comes after the sign, but space padding doesn't. */
     109SPRINTF_TEST(sign_padding, "[00012] [   12] [ 0012] [   12] [+0012] [  +12]", "[%05d] [%5d] [%0 5d] [% 5d] [%0+5d] [%+5d]", 12, 12, 12, 12, 12, 12);
     110SPRINTF_TEST(sign_padding2, "[-0012] [  -12] [-0012] [  -12] [-0012] [  -12]", "[%05d] [%5d] [%0 5d] [% 5d] [%0+5d] [%+5d]", -12, -12, -12, -12, -12, -12);
     111
     112SPRINTF_TEST(all_zero, "[00000] [0] [0] [0] [0] [0] [0] [0] [0]", "[%05d] [%d] [%x] [%#x] [%o] [%#o] [%b] [%#b] [%u]", 0, 0, 0, 0, 0, 0, 0, 0, 0);
    84113
    85114PCUT_EXPORT(sprintf);
  • uspace/lib/c/test/str.c

    r800d188 r29e7cc7  
    2727 */
    2828
     29#include "pcut/asserts.h"
     30#include <assert.h>
     31#include <stdint.h>
    2932#include <stdio.h>
    3033#include <str.h>
     
    4750}
    4851
     52/* Helper to display string contents for debugging */
     53static void print_string_hex(char *out, const char *s, size_t len)
     54{
     55        *out++ = '"';
     56        for (size_t i = 0; i < len && s[i]; i++) {
     57                if (s[i] >= 32 && s[i] <= 126)
     58                        *out++ = s[i];
     59                else
     60                        out += snprintf(out, 5, "\\x%02x", (uint8_t) s[i]);
     61        }
     62        *out++ = '"';
     63        *out++ = 0;
     64}
     65
    4966PCUT_TEST(rtrim)
    5067{
     
    115132}
    116133
     134PCUT_TEST(str_non_shortest)
     135{
     136        /* Overlong zero. */
     137        const char overlong1[] = "\xC0\x80";
     138        const char overlong2[] = "\xE0\x80\x80";
     139        const char overlong3[] = "\xF0\x80\x80\x80";
     140
     141        const char overlong4[] = "\xC1\xBF";
     142        const char overlong5[] = "\xE0\x9F\xBF";
     143        const char overlong6[] = "\xF0\x8F\xBF\xBF";
     144
     145        size_t offset = 0;
     146        PCUT_ASSERT_INT_EQUALS(U_SPECIAL, str_decode(overlong1, &offset, sizeof(overlong1)));
     147        offset = 0;
     148        PCUT_ASSERT_INT_EQUALS(U_SPECIAL, str_decode(overlong2, &offset, sizeof(overlong2)));
     149        offset = 0;
     150        PCUT_ASSERT_INT_EQUALS(U_SPECIAL, str_decode(overlong3, &offset, sizeof(overlong3)));
     151        offset = 0;
     152        PCUT_ASSERT_INT_EQUALS(U_SPECIAL, str_decode(overlong4, &offset, sizeof(overlong4)));
     153        offset = 0;
     154        PCUT_ASSERT_INT_EQUALS(U_SPECIAL, str_decode(overlong5, &offset, sizeof(overlong5)));
     155        offset = 0;
     156        PCUT_ASSERT_INT_EQUALS(U_SPECIAL, str_decode(overlong6, &offset, sizeof(overlong6)));
     157}
     158
     159struct sanitize_test {
     160        const char *input;
     161        const char *output;
     162};
     163
     164static const struct sanitize_test sanitize_tests[] = {
     165        // Empty string
     166        { "", "" },
     167        // ASCII only
     168        { "Hello, world!", "Hello, world!" },
     169        // Valid multi-byte sequences
     170        { "Aπ你🐱", "Aπ你🐱" },
     171        // U+D7FF is last valid before surrogates
     172        { "A\xED\x9F\xBFZ", "A\xED\x9F\xBFZ" },
     173        // 0x10FFFF is the highest legal code point
     174        { "A\xF4\x8F\xBF\xBFZ", "A\xF4\x8F\xBF\xBFZ" },
     175
     176        // Missing continuation byte
     177        { "A\xC2Z", "A?Z" },
     178        // Truncated multi-byte at buffer end
     179        { "A\xE2\x82", "A??" },
     180        // Continuation byte without leading byte (0x80-0xBF are never valid first bytes)
     181        { "A\x80Y\xBFZ", "A?Y?Z" },
     182
     183        // 'A' (U+0041) normally encoded as 0x41
     184        // Overlong 2-byte encoding: 0xC1 0x81
     185        { "\xC1\x81X", "??X" },
     186
     187        // ¢ (U+00A2) normally encoded as 0xC2 0xA2
     188        // Overlong 3-byte encoding: 0xE0 0x82 0xA2
     189        { "\xE0\x82\xA2X", "???X" },
     190
     191        // ¢ (U+00A2) normally encoded as 0xC2 0xA2
     192        // Overlong 4-byte encoding: 0xF0 0x80 0x82 0xA2
     193        { "\xF0\x80\x82\xA2X", "????X" },
     194
     195        // € (U+20AC) normally encoded as 0xE2 0x82 0xAC
     196        // Overlong 4-byte encoding: 0xF0 0x82 0x82 0xAC
     197        { "\xF0\x82\x82\xACX", "????X" },
     198
     199        // Using 0xC0 0x80 as overlong encoding for NUL (which should be just 0x00)
     200        { "\xC0\x80X", "??X" },
     201
     202        // 0xED 0xA0 0x80 encodes a surrogate half (U+D800), not allowed in UTF-8
     203        { "A\xED\xA0\x80Z", "A???Z" },
     204
     205        // 0x110000 is not a legal code point
     206        { "A\xF4\x90\x80\x80Z", "A????Z" },
     207
     208        // Mix of valid and invalid sequences
     209        { "A\xC2\xA9\xE2\x28\xA1\xF0\x9F\x98\x81\x80Z", "A©?(?😁?Z" },
     210};
     211
     212static size_t count_diff(const char *a, const char *b, size_t n)
     213{
     214        size_t count = 0;
     215
     216        for (size_t i = 0; i < n; i++) {
     217                if (a[i] != b[i])
     218                        count++;
     219        }
     220
     221        return count;
     222}
     223
     224PCUT_TEST(str_sanitize)
     225{
     226        char replacement = '?';
     227        char buffer2[255];
     228
     229        for (size_t i = 0; i < sizeof(sanitize_tests) / sizeof(sanitize_tests[0]); i++) {
     230                const char *in = sanitize_tests[i].input;
     231                const char *out = sanitize_tests[i].output;
     232                size_t n = str_size(in) + 1;
     233                assert(str_size(out) + 1 == n);
     234
     235                memcpy(buffer, in, n);
     236                size_t replaced = str_sanitize(buffer, n, replacement);
     237                if (memcmp(buffer, out, n) != 0) {
     238                        print_string_hex(buffer2, buffer, n);
     239                        print_string_hex(buffer, out, n);
     240                        PCUT_ASSERTION_FAILED("Expected %s, got %s", buffer, buffer2);
     241                }
     242
     243                size_t expect_replaced = count_diff(buffer, in, n);
     244                PCUT_ASSERT_INT_EQUALS(expect_replaced, replaced);
     245        }
     246
     247        // Test with n smaller than string length - truncated valid encoding for €
     248        const char *in = "ABC€";
     249        const char *out = "ABC??\xAC";
     250        size_t n = str_size(in) + 1;
     251        memcpy(buffer, in, n);
     252        size_t replaced = str_sanitize(buffer, 5, replacement);
     253        if (memcmp(buffer, out, n) != 0) {
     254                print_string_hex(buffer2, buffer, n);
     255                print_string_hex(buffer, out, n);
     256                PCUT_ASSERTION_FAILED("Expected %s, got %s", buffer, buffer2);
     257        }
     258
     259        PCUT_ASSERT_INT_EQUALS(2, replaced);
     260}
     261
    117262PCUT_EXPORT(str);
Note: See TracChangeset for help on using the changeset viewer.