aboutsummaryrefslogtreecommitdiff
path: root/cstring/include/cstring.h
blob: 991d7424ded4809c68b19ec28ca206d8eae2382d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/// Fixed-size strings with value semantics.
#pragma once

#include <assert.h>
#include <bsd/string.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>

/// A fixed-size string.
/// The string is null-terminated so that it can be used with the usual C APIs.
//
// TODO: The asserts on length should be hard asserts, not just asserts in debug
// builds.
#define DEF_STRING(STRING, SIZE)                                          \
  typedef struct STRING {                                                 \
    size_t length;                                                        \
    char   str[SIZE];                                                     \
  } STRING;                                                               \
                                                                          \
  static const size_t STRING##_size = SIZE;                               \
                                                                          \
  static inline const char* STRING##_cstr(const STRING* str) {            \
    return str->str;                                                      \
  }                                                                       \
                                                                          \
  static inline size_t STRING##_length(STRING str) { return str.length; } \
                                                                          \
  static inline STRING STRING##_make(const char* cstr) {                  \
    if (!cstr) {                                                          \
      return (STRING){0};                                                 \
    } else {                                                              \
      STRING str = (STRING){0};                                           \
      str.length = strlcpy(str.str, cstr, SIZE);                          \
      return str;                                                         \
    }                                                                     \
  }                                                                       \
                                                                          \
  static inline STRING STRING##_dirname(STRING path) {                    \
    STRING str = path;                                                    \
    for (int i = str.length - 1; i >= 0; --i) {                           \
      if (str.str[i] == '/' || str.str[i] == '\\') {                      \
        str.str[i] = 0;                                                   \
        str.length = i;                                                   \
        return str;                                                       \
      } else {                                                            \
        str.str[i] = 0;                                                   \
      }                                                                   \
    }                                                                     \
    str        = (STRING){0};                                             \
    str.str[0] = '.';                                                     \
    str.length = 1;                                                       \
    return str;                                                           \
  }                                                                       \
                                                                          \
  static inline void STRING##_append_cstr(STRING* a, const char* b) {     \
    size_t b_length = strlen(b);                                          \
    assert(a->length + b_length + 1 < SIZE);                              \
    strlcpy(a->str + a->length, b, SIZE);                                 \
    a->length = a->length + b_length;                                     \
  }                                                                       \
                                                                          \
  static inline void STRING##_append(STRING* a, STRING b) {               \
    assert(a->length + b.length + 1 < SIZE);                              \
    strlcpy(a->str + a->length, b.str, SIZE);                             \
    a->length = a->length + b.length;                                     \
  }                                                                       \
                                                                          \
  static inline STRING STRING##_concat(STRING a, STRING b) {              \
    assert(a.length + b.length + 1 < SIZE);                               \
    STRING str = {0};                                                     \
    strlcpy(str.str, a.str, SIZE);                                        \
    strlcpy(str.str + a.length, b.str, SIZE);                             \
    str.length = a.length + b.length;                                     \
    return str;                                                           \
  }                                                                       \
                                                                          \
  static inline STRING STRING##_concat_cstr(STRING a, const char* b) {    \
    return STRING##_concat(a, STRING##_make(b));                          \
  }                                                                       \
                                                                          \
  static inline STRING STRING##_concat_path(STRING a, STRING b) {         \
    return STRING##_concat(STRING##_concat(a, STRING##_make("/")), b);    \
  }                                                                       \
                                                                          \
  static inline bool STRING##_eq(STRING a, STRING b) {                    \
    if (a.length != b.length) {                                           \
      return false;                                                       \
    }                                                                     \
    return strncmp(a.str, b.str, a.length) == 0;                          \
  }                                                                       \
                                                                          \
  static inline bool STRING##_eq_cstr(STRING a, const char* b) {          \
    return (a.length == strlen(b)) && strncmp(a.str, b, a.length) == 0;   \
  }                                                                       \
                                                                          \
  static inline bool STRING##_empty(STRING a) { return a.length == 0; }   \
                                                                          \
  static inline STRING STRING##_itoa(int n) {                             \
    STRING    str     = (STRING){0};                                      \
    const int written = snprintf(str.str, SIZE, "%d", n);                 \
    assert(written >= 0);                                                 \
    str.length = (size_t)written;                                         \
    return str;                                                           \
  }                                                                       \
                                                                          \
  static inline uint64_t STRING##_hash(STRING str) {                      \
    return cstring_hash(str.str);                                         \
  }

/// Return a hash of the given string.
static inline uint64_t cstring_hash(const char* str) {
  uint64_t hash = 0;
  for (size_t i = 0; i < strlen(str); ++i) {
    hash = (uint64_t)str[i] + (hash << 6) + (hash << 16) - hash;
  }
  return hash;
}

DEF_STRING(sstring, 32)    // Small.
DEF_STRING(mstring, 256)   // Medium.
DEF_STRING(lstring, 1024)  // Large.
DEF_STRING(xlstring, 4096) // Extra large.