iodine 0.4.19 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -2
  3. data/CHANGELOG.md +22 -0
  4. data/LIMITS.md +19 -9
  5. data/README.md +92 -77
  6. data/SPEC-PubSub-Draft.md +113 -0
  7. data/SPEC-Websocket-Draft.md +127 -143
  8. data/bin/http-hello +0 -1
  9. data/bin/raw-rbhttp +1 -1
  10. data/bin/raw_broadcast +8 -10
  11. data/bin/updated api +2 -2
  12. data/bin/ws-broadcast +2 -4
  13. data/bin/ws-echo +2 -2
  14. data/examples/config.ru +13 -13
  15. data/examples/echo.ru +5 -6
  16. data/examples/hello.ru +2 -3
  17. data/examples/info.md +316 -0
  18. data/examples/pubsub_engine.ru +81 -0
  19. data/examples/redis.ru +9 -9
  20. data/examples/shootout.ru +45 -11
  21. data/ext/iodine/defer.c +194 -297
  22. data/ext/iodine/defer.h +61 -53
  23. data/ext/iodine/evio.c +0 -260
  24. data/ext/iodine/evio.h +50 -22
  25. data/ext/iodine/evio_callbacks.c +26 -0
  26. data/ext/iodine/evio_epoll.c +251 -0
  27. data/ext/iodine/evio_kqueue.c +193 -0
  28. data/ext/iodine/extconf.rb +1 -1
  29. data/ext/iodine/facil.c +1420 -542
  30. data/ext/iodine/facil.h +151 -64
  31. data/ext/iodine/fio_ary.h +418 -0
  32. data/ext/iodine/{base64.c → fio_base64.c} +33 -24
  33. data/ext/iodine/{base64.h → fio_base64.h} +6 -7
  34. data/ext/iodine/{fio_cli_helper.c → fio_cli.c} +77 -58
  35. data/ext/iodine/{fio_cli_helper.h → fio_cli.h} +9 -4
  36. data/ext/iodine/fio_hashmap.h +759 -0
  37. data/ext/iodine/fio_json_parser.h +651 -0
  38. data/ext/iodine/fio_llist.h +257 -0
  39. data/ext/iodine/fio_mem.c +672 -0
  40. data/ext/iodine/fio_mem.h +140 -0
  41. data/ext/iodine/fio_random.c +248 -0
  42. data/ext/iodine/{random.h → fio_random.h} +11 -14
  43. data/ext/iodine/{sha1.c → fio_sha1.c} +28 -24
  44. data/ext/iodine/{sha1.h → fio_sha1.h} +38 -16
  45. data/ext/iodine/{sha2.c → fio_sha2.c} +66 -49
  46. data/ext/iodine/{sha2.h → fio_sha2.h} +57 -26
  47. data/ext/iodine/{fiobj_internal.c → fio_siphash.c} +9 -90
  48. data/ext/iodine/fio_siphash.h +18 -0
  49. data/ext/iodine/fio_tmpfile.h +38 -0
  50. data/ext/iodine/fiobj.h +24 -7
  51. data/ext/iodine/fiobj4sock.h +23 -0
  52. data/ext/iodine/fiobj_ary.c +143 -226
  53. data/ext/iodine/fiobj_ary.h +17 -16
  54. data/ext/iodine/fiobj_data.c +1160 -0
  55. data/ext/iodine/fiobj_data.h +164 -0
  56. data/ext/iodine/fiobj_hash.c +298 -406
  57. data/ext/iodine/fiobj_hash.h +101 -54
  58. data/ext/iodine/fiobj_json.c +478 -601
  59. data/ext/iodine/fiobj_json.h +34 -9
  60. data/ext/iodine/fiobj_numbers.c +383 -51
  61. data/ext/iodine/fiobj_numbers.h +87 -11
  62. data/ext/iodine/fiobj_str.c +423 -184
  63. data/ext/iodine/fiobj_str.h +81 -32
  64. data/ext/iodine/fiobject.c +273 -522
  65. data/ext/iodine/fiobject.h +477 -112
  66. data/ext/iodine/http.c +2243 -83
  67. data/ext/iodine/http.h +842 -121
  68. data/ext/iodine/http1.c +810 -385
  69. data/ext/iodine/http1.h +16 -39
  70. data/ext/iodine/http1_parser.c +146 -74
  71. data/ext/iodine/http1_parser.h +15 -4
  72. data/ext/iodine/http_internal.c +1258 -0
  73. data/ext/iodine/http_internal.h +226 -0
  74. data/ext/iodine/http_mime_parser.h +341 -0
  75. data/ext/iodine/iodine.c +86 -68
  76. data/ext/iodine/iodine.h +26 -11
  77. data/ext/iodine/iodine_helpers.c +8 -7
  78. data/ext/iodine/iodine_http.c +487 -324
  79. data/ext/iodine/iodine_json.c +304 -0
  80. data/ext/iodine/iodine_json.h +6 -0
  81. data/ext/iodine/iodine_protocol.c +107 -45
  82. data/ext/iodine/iodine_pubsub.c +526 -225
  83. data/ext/iodine/iodine_pubsub.h +10 -0
  84. data/ext/iodine/iodine_websockets.c +268 -510
  85. data/ext/iodine/iodine_websockets.h +2 -4
  86. data/ext/iodine/pubsub.c +726 -432
  87. data/ext/iodine/pubsub.h +85 -103
  88. data/ext/iodine/rb-call.c +4 -4
  89. data/ext/iodine/rb-defer.c +46 -22
  90. data/ext/iodine/rb-fiobj2rb.h +117 -0
  91. data/ext/iodine/rb-rack-io.c +73 -238
  92. data/ext/iodine/rb-rack-io.h +2 -2
  93. data/ext/iodine/rb-registry.c +35 -93
  94. data/ext/iodine/rb-registry.h +1 -0
  95. data/ext/iodine/redis_engine.c +742 -304
  96. data/ext/iodine/redis_engine.h +42 -39
  97. data/ext/iodine/resp_parser.h +311 -0
  98. data/ext/iodine/sock.c +627 -490
  99. data/ext/iodine/sock.h +345 -297
  100. data/ext/iodine/spnlock.inc +15 -4
  101. data/ext/iodine/websocket_parser.h +16 -20
  102. data/ext/iodine/websockets.c +188 -257
  103. data/ext/iodine/websockets.h +24 -133
  104. data/lib/iodine.rb +52 -7
  105. data/lib/iodine/cli.rb +6 -24
  106. data/lib/iodine/json.rb +40 -0
  107. data/lib/iodine/version.rb +1 -1
  108. data/lib/iodine/websocket.rb +5 -3
  109. data/lib/rack/handler/iodine.rb +58 -13
  110. metadata +38 -48
  111. data/bin/ws-shootout +0 -107
  112. data/examples/broadcast.ru +0 -56
  113. data/ext/iodine/bscrypt-common.h +0 -116
  114. data/ext/iodine/bscrypt.h +0 -49
  115. data/ext/iodine/fio2resp.c +0 -60
  116. data/ext/iodine/fio2resp.h +0 -51
  117. data/ext/iodine/fio_dict.c +0 -446
  118. data/ext/iodine/fio_dict.h +0 -99
  119. data/ext/iodine/fio_hash_table.h +0 -370
  120. data/ext/iodine/fio_list.h +0 -111
  121. data/ext/iodine/fiobj_internal.h +0 -280
  122. data/ext/iodine/fiobj_primitives.c +0 -131
  123. data/ext/iodine/fiobj_primitives.h +0 -55
  124. data/ext/iodine/fiobj_sym.c +0 -135
  125. data/ext/iodine/fiobj_sym.h +0 -60
  126. data/ext/iodine/hex.c +0 -124
  127. data/ext/iodine/hex.h +0 -70
  128. data/ext/iodine/http1_request.c +0 -81
  129. data/ext/iodine/http1_request.h +0 -58
  130. data/ext/iodine/http1_response.c +0 -417
  131. data/ext/iodine/http1_response.h +0 -95
  132. data/ext/iodine/http_request.c +0 -111
  133. data/ext/iodine/http_request.h +0 -102
  134. data/ext/iodine/http_response.c +0 -1703
  135. data/ext/iodine/http_response.h +0 -250
  136. data/ext/iodine/misc.c +0 -182
  137. data/ext/iodine/misc.h +0 -74
  138. data/ext/iodine/random.c +0 -208
  139. data/ext/iodine/redis_connection.c +0 -278
  140. data/ext/iodine/redis_connection.h +0 -86
  141. data/ext/iodine/resp.c +0 -842
  142. data/ext/iodine/resp.h +0 -261
  143. data/ext/iodine/siphash.c +0 -154
  144. data/ext/iodine/siphash.h +0 -22
  145. data/ext/iodine/xor-crypt.c +0 -193
  146. data/ext/iodine/xor-crypt.h +0 -107
@@ -1,7 +1,7 @@
1
1
  #ifndef H_FIOBJ_NUMBERS_H
2
2
  #define H_FIOBJ_NUMBERS_H
3
3
  /*
4
- Copyright: Boaz Segev, 2017
4
+ Copyright: Boaz Segev, 2017-2018
5
5
  License: MIT
6
6
  */
7
7
 
@@ -15,33 +15,109 @@ extern "C" {
15
15
  Numbers API (Integers)
16
16
  ***************************************************************************** */
17
17
 
18
- extern const uintptr_t FIOBJ_T_NUMBER;
19
-
20
18
  /** Creates a Number object. Remember to use `fiobj_free`. */
21
- fiobj_s *fiobj_num_new(int64_t num);
19
+ FIO_INLINE FIOBJ fiobj_num_new(intptr_t num);
22
20
 
23
- /** Mutates a Number object's value. Effects every object's reference! */
24
- void fiobj_num_set(fiobj_s *target, int64_t num);
21
+ /** Creates a temporary Number object. Avoid using `fiobj_free`. */
22
+ FIOBJ fiobj_num_tmp(intptr_t num);
25
23
 
26
24
  /* *****************************************************************************
27
25
  Float API (Double)
28
26
  ***************************************************************************** */
29
27
 
30
- extern const uintptr_t FIOBJ_T_FLOAT;
31
-
32
28
  /** Creates a Float object. Remember to use `fiobj_free`. */
33
- fiobj_s *fiobj_float_new(double num);
29
+ FIOBJ fiobj_float_new(double num);
34
30
 
35
31
  /** Mutates a Float object's value. Effects every object's reference! */
36
- void fiobj_float_set(fiobj_s *obj, double num);
32
+ void fiobj_float_set(FIOBJ obj, double num);
33
+
34
+ /** Creates a temporary Float object. Avoid using `fiobj_free`. */
35
+ FIOBJ fiobj_float_tmp(double num);
36
+
37
+ /* *****************************************************************************
38
+ Numerical Helpers: not FIOBJ specific, but included as part of the library
39
+ ***************************************************************************** */
40
+
41
+ /**
42
+ * A helper function that converts between String data to a signed int64_t.
43
+ *
44
+ * Numbers are assumed to be in base 10.
45
+ *
46
+ * The `0x##` (or `x##`) and `0b##` (or `b##`) are recognized as base 16 and
47
+ * base 2 (binary MSB first) respectively.
48
+ *
49
+ * The pointer will be updated to point to the first byte after the number.
50
+ */
51
+ intptr_t fio_atol(char **pstr);
52
+
53
+ /** A helper function that convers between String data to a signed double. */
54
+ double fio_atof(char **pstr);
55
+
56
+ /**
57
+ * A helper function that convers between a signed int64_t to a string.
58
+ *
59
+ * No overflow guard is provided, make sure there's at least 66 bytes available
60
+ * (for base 2).
61
+ *
62
+ * Supports base 2, base 10 and base 16. An unsupported base will silently
63
+ * default to base 10. Prefixes aren't added (i.e., no "0x" or "0b" at the
64
+ * beginning of the string).
65
+ *
66
+ * Returns the number of bytes actually written (excluding the NUL terminator).
67
+ */
68
+ size_t fio_ltoa(char *dest, int64_t num, uint8_t base);
69
+
70
+ /**
71
+ * A helper function that convers between a double to a string.
72
+ *
73
+ * No overflow guard is provided, make sure there's at least 130 bytes available
74
+ * (for base 2).
75
+ *
76
+ * Supports base 2, base 10 and base 16. An unsupported base will silently
77
+ * default to base 10. Prefixes aren't added (i.e., no "0x" or "0b" at the
78
+ * beginning of the string).
79
+ *
80
+ * Returns the number of bytes actually written (excluding the NUL terminator).
81
+ */
82
+ size_t fio_ftoa(char *dest, double num, uint8_t base);
83
+
84
+ /** Converts a number to a temporary, thread safe, C string object */
85
+ fio_cstr_s fio_ltocstr(long);
86
+
87
+ /** Converts a float to a temporary, thread safe, C string object */
88
+ fio_cstr_s fio_ftocstr(double);
37
89
 
38
90
  /* *****************************************************************************
39
91
  Pointer Wrapping Helper MACROs (uses integers)
40
92
  ***************************************************************************** */
41
93
 
42
- #define fiobj_ptr_wrap(ptr) fiobj_num_new((int64_t)(ptr))
94
+ #define fiobj_ptr_wrap(ptr) fiobj_num_new((uintptr_t)(ptr))
43
95
  #define fiobj_ptr_unwrap(obj) ((void *)fiobj_obj2num((obj)))
44
96
 
97
+ /* *****************************************************************************
98
+ Inline Number Initialization
99
+ ***************************************************************************** */
100
+
101
+ FIOBJ fiobj_num_new_bignum(intptr_t num);
102
+
103
+ /** Creates a Number object. Remember to use `fiobj_free`. */
104
+ FIO_INLINE FIOBJ fiobj_num_new(intptr_t num) {
105
+ if ((((uintptr_t)num &
106
+ (FIOBJ_NUMBER_SIGN_BIT | FIOBJ_NUMBER_SIGN_EXCLUDE_BIT)) == 0) ||
107
+ (((uintptr_t)num &
108
+ (FIOBJ_NUMBER_SIGN_BIT | FIOBJ_NUMBER_SIGN_EXCLUDE_BIT)) ==
109
+ (FIOBJ_NUMBER_SIGN_BIT | FIOBJ_NUMBER_SIGN_EXCLUDE_BIT))) {
110
+ const uintptr_t num_abs = (uintptr_t)num & FIOBJ_NUMBER_SIGN_MASK;
111
+ const uintptr_t num_sign = (uintptr_t)num & FIOBJ_NUMBER_SIGN_BIT;
112
+ return ((num_abs << 1) | num_sign | FIOBJECT_NUMBER_FLAG);
113
+ }
114
+ return fiobj_num_new_bignum(num);
115
+ }
116
+
117
+ #if DEBUG
118
+ void fiobj_test_numbers(void);
119
+ #endif
120
+
45
121
  #ifdef __cplusplus
46
122
  } /* extern "C" */
47
123
  #endif
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright: Boaz Segev, 2017
2
+ Copyright: Boaz Segev, 2017-2018
3
3
  License: MIT
4
4
  */
5
5
 
@@ -10,16 +10,30 @@ License: MIT
10
10
  #include <unistd.h>
11
11
  #endif
12
12
 
13
- #include "fiobj_internal.h"
13
+ #ifdef _SC_PAGESIZE
14
+ #define PAGE_SIZE sysconf(_SC_PAGESIZE)
15
+ #else
16
+ #define PAGE_SIZE 4096
17
+ #endif
18
+
19
+ #include "fiobject.h"
20
+
21
+ #include "fio_siphash.h"
22
+ #include "fiobj_numbers.h"
14
23
  #include "fiobj_str.h"
15
24
 
25
+ #include <assert.h>
16
26
  #include <errno.h>
17
27
  #include <fcntl.h>
18
28
  #include <limits.h>
29
+ #include <string.h>
19
30
  #include <sys/stat.h>
20
31
 
32
+ #define FIO_OVERRIDE_MALLOC 1
33
+ #include "fio_mem.h"
34
+
21
35
  #ifndef PATH_MAX
22
- #define PATH_MAX 2096
36
+ #define PATH_MAX PAGE_SIZE
23
37
  #endif
24
38
 
25
39
  /* *****************************************************************************
@@ -27,137 +41,182 @@ String Type
27
41
  ***************************************************************************** */
28
42
 
29
43
  typedef struct {
30
- struct fiobj_vtable_s *vtable;
31
- uint64_t capa;
32
- uint64_t len;
33
- uint8_t is_static;
44
+ fiobj_object_header_s head;
45
+ uint64_t hash;
46
+ uint8_t is_small;
47
+ uint8_t frozen;
48
+ uint8_t slen;
49
+ intptr_t len;
50
+ uintptr_t capa;
34
51
  char *str;
35
52
  } fiobj_str_s;
36
53
 
37
- #define obj2str(o) ((fiobj_str_s *)(o))
54
+ #define obj2str(o) ((fiobj_str_s *)(FIOBJ2PTR(o)))
55
+
56
+ #define STR_INTENAL_OFFSET ((uintptr_t)(&(((fiobj_str_s *)0)->slen) + 1))
57
+ #define STR_INTENAL_CAPA ((uintptr_t)(sizeof(fiobj_str_s) - STR_INTENAL_OFFSET))
58
+ #define STR_INTENAL_STR(o) \
59
+ ((char *)((uintptr_t)FIOBJ2PTR(o) + STR_INTENAL_OFFSET))
60
+ #define STR_INTENAL_LEN(o) (((fiobj_str_s *)FIOBJ2PTR(o))->slen)
61
+
62
+ static inline char *fiobj_str_mem_addr(FIOBJ o) {
63
+ if (obj2str(o)->is_small)
64
+ return STR_INTENAL_STR(o);
65
+ return obj2str(o)->str;
66
+ }
67
+ static inline size_t fiobj_str_getlen(FIOBJ o) {
68
+ if (obj2str(o)->is_small)
69
+ return obj2str(o)->slen;
70
+ return obj2str(o)->len;
71
+ }
72
+ static inline size_t fiobj_str_getcapa(FIOBJ o) {
73
+ if (obj2str(o)->is_small)
74
+ return STR_INTENAL_CAPA;
75
+ return obj2str(o)->capa;
76
+ }
77
+ static inline void fiobj_str_setlen(FIOBJ o, size_t len) {
78
+ if (obj2str(o)->is_small) {
79
+ obj2str(o)->slen = len;
80
+ STR_INTENAL_STR(o)[len] = 0;
81
+ } else {
82
+ obj2str(o)->len = len;
83
+ obj2str(o)->str[len] = 0;
84
+ obj2str(o)->hash = 0;
85
+ }
86
+ }
87
+ static inline fio_cstr_s fiobj_str_get_cstr(const FIOBJ o) {
88
+ if (obj2str(o)->is_small)
89
+ return (fio_cstr_s){.buffer = STR_INTENAL_STR(o),
90
+ .len = STR_INTENAL_LEN(o)};
91
+ ;
92
+ return (fio_cstr_s){.buffer = obj2str(o)->str, .len = obj2str(o)->len};
93
+ }
38
94
 
39
95
  /* *****************************************************************************
40
- String VTable
96
+ String VTables
41
97
  ***************************************************************************** */
42
98
 
43
- static void fiobj_str_dealloc(fiobj_s *o) {
44
- free(obj2str(o)->str);
45
- fiobj_dealloc(o);
46
- }
99
+ static fio_cstr_s fio_str2str(const FIOBJ o) { return fiobj_str_get_cstr(o); }
47
100
 
48
- static int fiobj_str_is_eq(const fiobj_s *self, const fiobj_s *other) {
49
- if (!other || (FIOBJ_IS_STRING(other)) ||
50
- obj2str(self)->len != obj2str(other)->len)
51
- return 0;
52
- return self == other || obj2str(self)->str == obj2str(other)->str ||
53
- !memcmp(obj2str(self)->str, obj2str(other)->str, obj2str(self)->len);
101
+ static void fiobj_str_dealloc(FIOBJ o, void (*task)(FIOBJ, void *), void *arg) {
102
+ if (obj2str(o)->is_small == 0 && obj2str(o)->capa)
103
+ fio_free(obj2str(o)->str);
104
+ fio_free(FIOBJ2PTR(o));
105
+ (void)task;
106
+ (void)arg;
54
107
  }
55
108
 
56
- static fio_cstr_s fio_str2str(const fiobj_s *o) {
57
- return (fio_cstr_s){.buffer = obj2str(o)->str, .len = obj2str(o)->len};
109
+ static size_t fiobj_str_is_eq(const FIOBJ self, const FIOBJ other) {
110
+ fio_cstr_s o1 = fiobj_str_get_cstr(self);
111
+ fio_cstr_s o2 = fiobj_str_get_cstr(other);
112
+ return (o1.len == o2.len &&
113
+ (o1.data == o2.data || !memcmp(o1.data, o2.data, o1.len)));
58
114
  }
59
- static int64_t fio_str2i(const fiobj_s *o) {
60
- char *s = obj2str(o)->str;
61
- return fio_atol(&s);
115
+
116
+ static intptr_t fio_str2i(const FIOBJ o) {
117
+ char *pos = fiobj_str_mem_addr(o);
118
+ return fio_atol(&pos);
62
119
  }
63
- static double fio_str2f(const fiobj_s *o) {
64
- char *s = obj2str(o)->str;
65
- return fio_atof(&s);
120
+ static double fio_str2f(const FIOBJ o) {
121
+ char *pos = fiobj_str_mem_addr(o);
122
+ return fio_atof(&pos);
66
123
  }
67
124
 
68
- static int fio_str2bool(const fiobj_s *o) { return obj2str(o)->len != 0; }
69
-
70
- static struct fiobj_vtable_s FIOBJ_VTABLE_STRING = {
71
- .name = "String",
72
- .free = fiobj_str_dealloc,
73
- .to_i = fio_str2i,
74
- .to_f = fio_str2f,
75
- .to_str = fio_str2str,
76
- .is_eq = fiobj_str_is_eq,
77
- .is_true = fio_str2bool,
78
- .count = fiobj_noop_count,
79
- .unwrap = fiobj_noop_unwrap,
80
- .each1 = fiobj_noop_each1,
81
- };
125
+ static size_t fio_str2bool(const FIOBJ o) { return fiobj_str_getlen(o) != 0; }
82
126
 
83
- const uintptr_t FIOBJ_T_STRING = (uintptr_t)(&FIOBJ_VTABLE_STRING);
127
+ uintptr_t fiobject___noop_count(const FIOBJ o);
84
128
 
85
- static struct fiobj_vtable_s FIOBJ_VTABLE_STATIC_STRING = {
86
- .name = "StaticString",
87
- .free = fiobj_simple_dealloc,
129
+ const fiobj_object_vtable_s FIOBJECT_VTABLE_STRING = {
130
+ .class_name = "String",
131
+ .dealloc = fiobj_str_dealloc,
88
132
  .to_i = fio_str2i,
89
133
  .to_f = fio_str2f,
90
134
  .to_str = fio_str2str,
91
135
  .is_eq = fiobj_str_is_eq,
92
136
  .is_true = fio_str2bool,
93
- .count = fiobj_noop_count,
94
- .unwrap = fiobj_noop_unwrap,
95
- .each1 = fiobj_noop_each1,
137
+ .count = fiobject___noop_count,
96
138
  };
97
139
 
98
- const uintptr_t FIOBJ_T_STRING_STATIC =
99
- (uintptr_t)(&FIOBJ_VTABLE_STATIC_STRING);
100
-
101
140
  /* *****************************************************************************
102
141
  String API
103
142
  ***************************************************************************** */
104
143
 
105
- static inline fiobj_s *fiobj_str_alloc(size_t len) {
106
- fiobj_s *o = fiobj_alloc(sizeof(fiobj_str_s) + len + 1);
107
- if (!o)
108
- perror("ERROR: fiobj string couldn't allocate memory"), exit(errno);
109
- *obj2str(o) = (fiobj_str_s){
110
- .vtable = &FIOBJ_VTABLE_STRING,
111
- .len = len,
112
- .capa = len + 1,
113
- .str = malloc(len + 1),
114
- };
115
- if (!obj2str(o)->str)
116
- perror("ERROR: fiobj string couldn't allocate memory"), exit(errno);
117
- obj2str(o)->str[len] = 0;
118
- return o;
119
- }
120
-
121
- /** Creates a String object. Remember to use `fiobj_free`. */
122
- fiobj_s *fiobj_str_new(const char *str, size_t len) {
123
- fiobj_s *s = fiobj_str_alloc(len);
124
- if (str)
125
- memcpy(obj2str(s)->str, str, len);
126
- return s;
127
- }
128
-
129
144
  /** Creates a buffer String object. Remember to use `fiobj_free`. */
130
- fiobj_s *fiobj_str_buf(size_t capa) {
145
+ FIOBJ fiobj_str_buf(size_t capa) {
131
146
  if (capa)
132
- capa = capa - 1;
147
+ capa = capa + 1;
133
148
  else
134
- capa = fiobj_memory_page_size();
135
- fiobj_s *s = fiobj_str_alloc(capa);
136
- fiobj_str_clear(s);
149
+ capa = PAGE_SIZE;
150
+
151
+ fiobj_str_s *s = fio_malloc(sizeof(*s));
152
+ if (!s) {
153
+ perror("ERROR: fiobj string couldn't allocate memory");
154
+ exit(errno);
155
+ }
156
+
157
+ if (capa <= STR_INTENAL_CAPA) {
158
+ *s = (fiobj_str_s){
159
+ .head =
160
+ {
161
+ .ref = 1, .type = FIOBJ_T_STRING,
162
+ },
163
+ .is_small = 1,
164
+ .slen = 0,
165
+ };
166
+ } else {
167
+ *s = (fiobj_str_s){
168
+ .head =
169
+ {
170
+ .ref = 1, .type = FIOBJ_T_STRING,
171
+ },
172
+ .len = 0,
173
+ .capa = capa,
174
+ .str = fio_malloc(capa),
175
+ };
176
+ if (!s->str) {
177
+ perror("ERROR: fiobj string couldn't allocate buffer memory");
178
+ exit(errno);
179
+ }
180
+ }
181
+ return ((uintptr_t)s | FIOBJECT_STRING_FLAG);
182
+ }
183
+
184
+ /** Creates a String object. Remember to use `fiobj_free`. */
185
+ FIOBJ fiobj_str_new(const char *str, size_t len) {
186
+ FIOBJ s = fiobj_str_buf(len);
187
+ char *mem = fiobj_str_mem_addr(s);
188
+ memcpy(mem, str, len);
189
+ fiobj_str_setlen(s, len);
137
190
  return s;
138
191
  }
139
192
 
140
193
  /**
141
194
  * Creates a String object. Remember to use `fiobj_free`.
142
195
  *
143
- * The ownership of the memory indicated by `str` will now "move" to the object,
144
- * so `free` will be called for `str` by the `fiobj` library as needed.
196
+ * The ownership of the memory indicated by `str` will now "move" to the
197
+ * object, so `free` will be called by the `fiobj` library as needed.
145
198
  */
146
- fiobj_s *fiobj_str_move(char *str, size_t len, size_t capacity) {
147
- fiobj_s *o = fiobj_alloc(sizeof(fiobj_str_s) + len + 1);
148
- if (!o)
149
- perror("ERROR: fiobj string couldn't allocate memory"), exit(errno);
150
- *obj2str(o) = (fiobj_str_s){
151
- .vtable = &FIOBJ_VTABLE_STRING,
199
+ FIOBJ fiobj_str_move(char *str, size_t len, size_t capacity) {
200
+ fiobj_str_s *s = fio_malloc(sizeof(*s));
201
+ if (!s) {
202
+ perror("ERROR: fiobj string couldn't allocate memory");
203
+ exit(errno);
204
+ }
205
+ *s = (fiobj_str_s){
206
+ .head =
207
+ {
208
+ .ref = 1, .type = FIOBJ_T_STRING,
209
+ },
152
210
  .len = len,
153
211
  .capa = (capacity < len ? len : capacity),
154
- .str = (char *)str,
212
+ .str = str,
155
213
  };
156
- return o;
214
+ return ((uintptr_t)s | FIOBJECT_STRING_FLAG);
157
215
  }
158
216
 
159
217
  /**
160
- * Creates a static String object from a static C string. Remember `fiobj_free`.
218
+ * Creates a static String object from a static C string. Remember
219
+ * `fiobj_free`.
161
220
  *
162
221
  * This variation avoids allocating memory for an existing static String.
163
222
  *
@@ -166,31 +225,32 @@ fiobj_s *fiobj_str_move(char *str, size_t len, size_t capacity) {
166
225
  *
167
226
  * NOTICE: static strings can't be written to.
168
227
  */
169
- fiobj_s *fiobj_str_static(const char *str, size_t len) {
170
- fiobj_s *o = fiobj_alloc(sizeof(fiobj_str_s) + len + 1);
171
- if (!o)
172
- perror("ERROR: fiobj string couldn't allocate memory"), exit(errno);
173
- *obj2str(o) = (fiobj_str_s){
174
- .vtable = &FIOBJ_VTABLE_STATIC_STRING,
175
- .len = (len ? len : strlen(str)),
228
+ FIOBJ fiobj_str_static(const char *str, size_t len) {
229
+ #if !FIOBJ_DONT_COPY_SMALL_STATIC_STRINGS
230
+ if (len < STR_INTENAL_CAPA)
231
+ return fiobj_str_new(str, len);
232
+ #endif
233
+ fiobj_str_s *s = fio_malloc(sizeof(*s));
234
+ if (!s) {
235
+ perror("ERROR: fiobj string couldn't allocate memory");
236
+ exit(errno);
237
+ }
238
+ *s = (fiobj_str_s){
239
+ .head =
240
+ {
241
+ .ref = 1, .type = FIOBJ_T_STRING,
242
+ },
243
+ .len = len,
176
244
  .capa = 0,
177
245
  .str = (char *)str,
178
246
  };
179
- if (!obj2str(o)->str)
180
- perror("ERROR: fiobj string couldn't allocate memory"), exit(errno);
181
- return o;
182
- }
183
-
184
- /** Creates a copy from an existing String. Remember to use `fiobj_free`. */
185
- fiobj_s *fiobj_str_copy(fiobj_s *src) {
186
- fio_cstr_s s = fiobj_obj2cstr(src);
187
- return fiobj_str_new(s.data, s.len);
247
+ return ((uintptr_t)s | FIOBJECT_STRING_FLAG);
188
248
  }
189
249
 
190
250
  /** Creates a String object using a printf like interface. */
191
- __attribute__((format(printf, 1, 0))) fiobj_s *
192
- fiobj_strvprintf(const char *format, va_list argv) {
193
- fiobj_s *str = NULL;
251
+ __attribute__((format(printf, 1, 0))) FIOBJ fiobj_strvprintf(const char *format,
252
+ va_list argv) {
253
+ FIOBJ str = 0;
194
254
  va_list argv_cpy;
195
255
  va_copy(argv_cpy, argv);
196
256
  int len = vsnprintf(NULL, 0, format, argv_cpy);
@@ -199,151 +259,224 @@ fiobj_strvprintf(const char *format, va_list argv) {
199
259
  str = fiobj_str_new("", 0);
200
260
  if (len <= 0)
201
261
  return str;
202
- str = fiobj_str_new(NULL, len);
203
- vsnprintf(obj2str(str)->str, len + 1, format, argv);
262
+ str = fiobj_str_buf(len);
263
+ char *mem = FIOBJECT2VTBL(str)->to_str(str).data;
264
+ vsnprintf(mem, len + 1, format, argv);
265
+ fiobj_str_setlen(str, len);
204
266
  return str;
205
267
  }
206
- __attribute__((format(printf, 1, 2))) fiobj_s *
207
- fiobj_strprintf(const char *format, ...) {
268
+ __attribute__((format(printf, 1, 2))) FIOBJ fiobj_strprintf(const char *format,
269
+ ...) {
208
270
  va_list argv;
209
271
  va_start(argv, format);
210
- fiobj_s *str = fiobj_strvprintf(format, argv);
272
+ FIOBJ str = fiobj_strvprintf(format, argv);
211
273
  va_end(argv);
212
274
  return str;
213
275
  }
214
276
 
277
+ /**
278
+ * Returns a thread-static temporary string. Avoid calling `fiobj_dup` or
279
+ * `fiobj_free`.
280
+ */
281
+ FIOBJ fiobj_str_tmp(void) {
282
+ static __thread fiobj_str_s tmp = {
283
+ .head =
284
+ {
285
+ .ref = ((~(uint32_t)0) >> 4), .type = FIOBJ_T_STRING,
286
+ },
287
+ .is_small = 1,
288
+ .slen = 0,
289
+ };
290
+ tmp.len = 0;
291
+ tmp.slen = 0;
292
+ return ((uintptr_t)&tmp | FIOBJECT_STRING_FLAG);
293
+ }
294
+
215
295
  /** Dumps the `filename` file's contents into a new String. If `limit == 0`,
216
296
  * than the data will be read until EOF.
217
297
  *
218
- * If the file can't be located, opened or read, or if `start_at` is beyond the
219
- * EOF position, NULL is returned.
298
+ * If the file can't be located, opened or read, or if `start_at` is beyond
299
+ * the EOF position, NULL is returned.
220
300
  *
221
301
  * Remember to use `fiobj_free`.
222
302
  */
223
- fiobj_s *fiobj_str_readfile(const char *filename, size_t start_at,
224
- size_t limit) {
303
+ FIOBJ fiobj_str_readfile(const char *filename, intptr_t start_at,
304
+ intptr_t limit) {
225
305
  #if defined(__unix__) || defined(__linux__) || defined(__APPLE__)
226
306
  /* POSIX implementations. */
227
307
  if (filename == NULL)
228
- return NULL;
308
+ return FIOBJ_INVALID;
229
309
  struct stat f_data;
230
310
  int file = -1;
231
311
  size_t file_path_len = strlen(filename);
232
- if (file_path_len == 0 || file_path_len > PATH_MAX)
233
- return NULL;
312
+ if (file_path_len == 0 || file_path_len >= PATH_MAX)
313
+ return FIOBJ_INVALID;
234
314
 
235
- char real_public_path[PATH_MAX + 1];
236
- real_public_path[PATH_MAX] = 0;
315
+ char real_public_path[PATH_MAX];
316
+ real_public_path[PATH_MAX - 1] = 0;
237
317
 
238
- if (filename[0] == '~' && getenv("HOME") && file_path_len <= PATH_MAX) {
318
+ if (filename[0] == '~' && getenv("HOME") && file_path_len < PATH_MAX) {
239
319
  strcpy(real_public_path, getenv("HOME"));
240
320
  memcpy(real_public_path + strlen(real_public_path), filename + 1,
241
321
  file_path_len);
242
322
  filename = real_public_path;
243
323
  }
244
324
 
245
- if (stat(filename, &f_data) || f_data.st_size < 0)
246
- return NULL;
325
+ if (stat(filename, &f_data) || f_data.st_size <= 0)
326
+ return FIOBJ_INVALID;
327
+
328
+ if (start_at < 0)
329
+ start_at = f_data.st_size + start_at;
330
+
331
+ if (start_at < 0 || start_at >= f_data.st_size)
332
+ return FIOBJ_INVALID;
247
333
 
248
- if (limit <= 0 || (size_t)f_data.st_size < limit + start_at)
334
+ if (limit <= 0 || f_data.st_size < (limit + start_at))
249
335
  limit = f_data.st_size - start_at;
250
- fiobj_s *str = fiobj_str_buf(limit + 1);
336
+ FIOBJ str = fiobj_str_buf(limit + 1);
251
337
  if (!str)
252
- return NULL;
338
+ return FIOBJ_INVALID;
253
339
  file = open(filename, O_RDONLY);
254
340
  if (file < 0) {
255
- fiobj_str_dealloc(str);
256
- return NULL;
341
+ FIOBJECT2VTBL(str)->dealloc(str, NULL, NULL);
342
+ return FIOBJ_INVALID;
257
343
  }
258
-
259
- if (pread(file, obj2str(str)->str, limit, start_at) != (ssize_t)limit) {
260
- fiobj_str_dealloc(str);
344
+ if (pread(file, fiobj_str_mem_addr(str), limit, start_at) != (ssize_t)limit) {
345
+ FIOBJECT2VTBL(str)->dealloc(str, NULL, NULL);
261
346
  close(file);
262
- return NULL;
347
+ return FIOBJ_INVALID;
263
348
  }
264
349
  close(file);
265
- obj2str(str)->len = limit;
266
- obj2str(str)->str[limit] = 0;
350
+ fiobj_str_setlen(str, limit);
267
351
  return str;
268
352
  #else
269
353
  /* TODO: consider adding non POSIX implementations. */
270
- return NULL;
354
+ return FIOBJ_INVALID;
271
355
  #endif
272
356
  }
273
357
 
358
+ /** Prevents the String object from being changed. */
359
+ void fiobj_str_freeze(FIOBJ str) {
360
+ if (FIOBJ_TYPE_IS(str, FIOBJ_T_STRING))
361
+ obj2str(str)->frozen = 1;
362
+ }
363
+
274
364
  /** Confirms the requested capacity is available and allocates as required. */
275
- size_t fiobj_str_capa_assert(fiobj_s *str, size_t size) {
276
- if (str->type != FIOBJ_T_STRING || obj2str(str)->capa == 0 ||
277
- obj2str(str)->capa >= size + 1)
365
+ size_t fiobj_str_capa_assert(FIOBJ str, size_t size) {
366
+
367
+ assert(FIOBJ_TYPE_IS(str, FIOBJ_T_STRING));
368
+ if (obj2str(str)->frozen)
369
+ return 0;
370
+ size += 1;
371
+ if (obj2str(str)->is_small) {
372
+ if (size <= STR_INTENAL_CAPA)
373
+ return STR_INTENAL_CAPA;
374
+ if (size >> 12)
375
+ size = ((size >> 12) + 1) << 12;
376
+ char *mem = fio_malloc(size);
377
+ if (!mem) {
378
+ perror("FATAL ERROR: Couldn't allocate larger String memory");
379
+ exit(errno);
380
+ }
381
+ memcpy(mem, STR_INTENAL_STR(str), obj2str(str)->slen + 1);
382
+ *obj2str(str) = (fiobj_str_s){
383
+ .head =
384
+ {
385
+ .ref = obj2str(str)->head.ref, .type = FIOBJ_T_STRING,
386
+ },
387
+ .len = obj2str(str)->slen,
388
+ .capa = size,
389
+ .str = mem,
390
+ };
278
391
  return obj2str(str)->capa;
279
- /* it's better to crash than live without memory... */
280
- obj2str(str)->str = realloc(obj2str(str)->str, size + 1);
281
- obj2str(str)->capa = size + 1;
282
- obj2str(str)->str[size] = 0;
283
- return obj2str(str)->capa;
392
+ }
393
+ if (obj2str(str)->capa >= size)
394
+ return obj2str(str)->capa;
395
+
396
+ /* large strings should increase memory by page size (assumes 4096 pages) */
397
+ if (size >> 12)
398
+ size = ((size >> 12) + 1) << 12;
399
+ else if (size < (obj2str(str)->capa << 1))
400
+ size = obj2str(str)->capa << 1; /* grow in steps */
401
+
402
+ if (obj2str(str)->capa == 0) {
403
+ /* a static string */
404
+ char *mem = fio_malloc(size);
405
+ if (!mem) {
406
+ perror("FATAL ERROR: Couldn't allocate new String memory");
407
+ exit(errno);
408
+ }
409
+ memcpy(mem, obj2str(str)->str, obj2str(str)->len + 1);
410
+ obj2str(str)->str = mem;
411
+ } else {
412
+ /* it's better to crash than live without memory... */
413
+ obj2str(str)->str =
414
+ fio_realloc2(obj2str(str)->str, size, obj2str(str)->len + 1);
415
+ if (!obj2str(str)->str) {
416
+ perror("FATAL ERROR: Couldn't (re)allocate String memory");
417
+ exit(errno);
418
+ }
419
+ }
420
+ obj2str(str)->capa = size;
421
+ return obj2str(str)->capa - 1;
284
422
  }
285
423
 
286
424
  /** Return's a String's capacity, if any. */
287
- size_t fiobj_str_capa(fiobj_s *str) {
288
- if (str->type != FIOBJ_T_STRING)
425
+ size_t fiobj_str_capa(FIOBJ str) {
426
+ assert(FIOBJ_TYPE_IS(str, FIOBJ_T_STRING));
427
+ if (obj2str(str)->frozen)
289
428
  return 0;
290
- return obj2str(str)->capa;
429
+ return fiobj_str_getcapa(str) - 1;
291
430
  }
292
431
 
293
432
  /** Resizes a String object, allocating more memory if required. */
294
- void fiobj_str_resize(fiobj_s *str, size_t size) {
295
- if (str->type != FIOBJ_T_STRING)
433
+ void fiobj_str_resize(FIOBJ str, size_t size) {
434
+ assert(FIOBJ_TYPE_IS(str, FIOBJ_T_STRING));
435
+ if (obj2str(str)->frozen)
296
436
  return;
297
437
  fiobj_str_capa_assert(str, size);
298
- obj2str(str)->len = size;
299
- obj2str(str)->str[size] = 0;
438
+ fiobj_str_setlen(str, size);
300
439
  return;
301
440
  }
302
441
 
303
442
  /** Deallocates any unnecessary memory (if supported by OS). */
304
- void fiobj_str_minimize(fiobj_s *str) {
305
- if (str->type != FIOBJ_T_STRING)
443
+ void fiobj_str_minimize(FIOBJ str) {
444
+ assert(FIOBJ_TYPE_IS(str, FIOBJ_T_STRING));
445
+ if (obj2str(str)->frozen || obj2str(str)->is_small || obj2str(str)->capa == 0)
306
446
  return;
307
447
  obj2str(str)->capa = obj2str(str)->len + 1;
308
- obj2str(str)->str = realloc(obj2str(str)->str, obj2str(str)->capa);
448
+ obj2str(str)->str = fio_realloc(obj2str(str)->str, obj2str(str)->capa);
309
449
  return;
310
450
  }
311
451
 
312
452
  /** Empties a String's data. */
313
- void fiobj_str_clear(fiobj_s *str) {
314
- if (str->type != FIOBJ_T_STRING)
453
+ void fiobj_str_clear(FIOBJ str) {
454
+ assert(FIOBJ_TYPE_IS(str, FIOBJ_T_STRING));
455
+ if (obj2str(str)->frozen)
315
456
  return;
316
- obj2str(str)->str[0] = 0;
317
- obj2str(str)->len = 0;
457
+ fiobj_str_setlen(str, 0);
318
458
  }
319
459
 
320
460
  /**
321
461
  * Writes data at the end of the string, resizing the string as required.
322
462
  * Returns the new length of the String
323
463
  */
324
- size_t fiobj_str_write(fiobj_s *dest, const char *data, size_t len) {
325
- if (dest->type != FIOBJ_T_STRING)
464
+ size_t fiobj_str_write(FIOBJ dest, const char *data, size_t len) {
465
+ assert(FIOBJ_TYPE_IS(dest, FIOBJ_T_STRING));
466
+ if (obj2str(dest)->frozen)
326
467
  return 0;
327
- fiobj_str_resize(dest, obj2str(dest)->len + len);
328
- if (len < 8) {
329
- size_t pos = obj2str(dest)->len;
330
- while (len) {
331
- len--;
332
- pos--;
333
- obj2str(dest)->str[pos] = data[len];
334
- }
335
- } else {
336
- memcpy(obj2str(dest)->str + obj2str(dest)->len - len, data, len);
337
- }
338
- // ((fio_str_s *)dest)->str[((fio_str_s *)dest)->len] = 0; // see str_resize
339
- return obj2str(dest)->len;
468
+ fiobj_str_resize(dest, fiobj_str_getlen(dest) + len);
469
+ fio_cstr_s s = fiobj_str_get_cstr(dest);
470
+ memcpy(s.data + s.len - len, data, len);
471
+ return s.len;
340
472
  }
341
473
  /**
342
474
  * Writes data at the end of the string, resizing the string as required.
343
475
  * Returns the new length of the String
344
476
  */
345
- size_t fiobj_str_write2(fiobj_s *dest, const char *format, ...) {
346
- if (dest->type != FIOBJ_T_STRING)
477
+ size_t fiobj_str_write2(FIOBJ dest, const char *format, ...) {
478
+ assert(FIOBJ_TYPE_IS(dest, FIOBJ_T_STRING));
479
+ if (obj2str(dest)->frozen)
347
480
  return 0;
348
481
  va_list argv;
349
482
  va_start(argv, format);
@@ -351,23 +484,129 @@ size_t fiobj_str_write2(fiobj_s *dest, const char *format, ...) {
351
484
  va_end(argv);
352
485
  if (len <= 0)
353
486
  return obj2str(dest)->len;
354
- fiobj_str_resize(dest, obj2str(dest)->len + len);
487
+ fiobj_str_resize(dest, fiobj_str_getlen(dest) + len);
355
488
  va_start(argv, format);
356
- vsnprintf(obj2str(dest)->str + obj2str(dest)->len - len, len + 1, format,
357
- argv);
489
+ fio_cstr_s s = fiobj_str_get_cstr(dest);
490
+ vsnprintf(s.data + s.len - len, len + 1, format, argv);
358
491
  va_end(argv);
359
492
  // ((fio_str_s *)dest)->str[((fio_str_s *)dest)->len] = 0; // see str_resize
360
- return obj2str(dest)->len;
493
+ return s.len;
361
494
  }
362
495
  /**
363
496
  * Writes data at the end of the string, resizing the string as required.
364
497
  * Returns the new length of the String
365
498
  */
366
- size_t fiobj_str_join(fiobj_s *dest, fiobj_s *obj) {
367
- if (dest->type != FIOBJ_T_STRING)
499
+ size_t fiobj_str_join(FIOBJ dest, FIOBJ obj) {
500
+ assert(FIOBJ_TYPE_IS(dest, FIOBJ_T_STRING));
501
+ if (obj2str(dest)->frozen)
368
502
  return 0;
369
503
  fio_cstr_s o = fiobj_obj2cstr(obj);
370
504
  if (o.len == 0)
371
505
  return obj2str(dest)->len;
372
506
  return fiobj_str_write(dest, o.data, o.len);
373
507
  }
508
+
509
+ /**
510
+ * Calculates a String's SipHash value for use as a HashMap key.
511
+ */
512
+ uint64_t fiobj_str_hash(FIOBJ o) {
513
+ assert(FIOBJ_TYPE_IS(o, FIOBJ_T_STRING));
514
+ // if (obj2str(o)->is_small) {
515
+ // return fio_siphash(STR_INTENAL_STR(o), STR_INTENAL_LEN(o));
516
+ // } else
517
+ if (obj2str(o)->hash) {
518
+ return obj2str(o)->hash;
519
+ }
520
+ if (obj2str(o)->is_small) {
521
+ obj2str(o)->hash = fio_siphash(STR_INTENAL_STR(o), STR_INTENAL_LEN(o));
522
+ } else {
523
+ obj2str(o)->hash = fio_siphash(obj2str(o)->str, obj2str(o)->len);
524
+ }
525
+ return obj2str(o)->hash;
526
+ }
527
+
528
+ /* *****************************************************************************
529
+ Tests
530
+ ***************************************************************************** */
531
+
532
+ #if DEBUG
533
+ void fiobj_test_string(void) {
534
+ fprintf(stderr, "=== Testing Strings\n");
535
+ fprintf(stderr, "* Internal String Capacity %u with offset, %u\n",
536
+ (unsigned int)STR_INTENAL_CAPA, (unsigned int)STR_INTENAL_OFFSET);
537
+ #define TEST_ASSERT(cond, ...) \
538
+ if (!(cond)) { \
539
+ fprintf(stderr, "* " __VA_ARGS__); \
540
+ fprintf(stderr, "Testing failed.\n"); \
541
+ exit(-1); \
542
+ }
543
+ #define STR_EQ(o, str) \
544
+ TEST_ASSERT((fiobj_str_getlen(o) == strlen(str) && \
545
+ !memcmp(fiobj_str_mem_addr(o), str, strlen(str))), \
546
+ "String not equal to " str)
547
+ FIOBJ o = fiobj_str_new("Hello", 5);
548
+ TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_STRING), "Small String isn't string!\n");
549
+ TEST_ASSERT(obj2str(o)->is_small, "Hello isn't small\n");
550
+ fiobj_str_write(o, " World", 6);
551
+ TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_STRING),
552
+ "Hello World String isn't string!\n");
553
+ TEST_ASSERT(obj2str(o)->is_small, "Hello World isn't small\n");
554
+ TEST_ASSERT(fiobj_obj2cstr(o).len == 11,
555
+ "Invalid small string length (%u != 11)!\n",
556
+ (unsigned int)fiobj_obj2cstr(o).len)
557
+ fiobj_str_write(o, " World, you crazy longer sleep loving person :-)", 48);
558
+ TEST_ASSERT(!obj2str(o)->is_small, "Crazier shouldn't be small\n");
559
+ fiobj_free(o);
560
+
561
+ o = fiobj_str_new(
562
+ "hello my dear friend, I hope that your are well and happy.", 58);
563
+ TEST_ASSERT(FIOBJ_TYPE_IS(o, FIOBJ_T_STRING), "Long String isn't string!\n");
564
+ TEST_ASSERT(!obj2str(o)->is_small,
565
+ "Long String is small! (capa: %lu, len: %lu)\n", obj2str(o)->capa,
566
+ obj2str(o)->len);
567
+ TEST_ASSERT(fiobj_obj2cstr(o).len == 58,
568
+ "Invalid long string length (%lu != 58)!\n",
569
+ fiobj_obj2cstr(o).len)
570
+ uint64_t hash = fiobj_str_hash(o);
571
+ TEST_ASSERT(!obj2str(o)->frozen, "String forzen when only hashing!\n");
572
+ fiobj_str_freeze(o);
573
+ TEST_ASSERT(obj2str(o)->frozen, "String not forzen!\n");
574
+ fiobj_str_write(o, " World", 6);
575
+ TEST_ASSERT(hash == fiobj_str_hash(o),
576
+ "String hash changed after hashing - not frozen?\n");
577
+ TEST_ASSERT(fiobj_obj2cstr(o).len == 58,
578
+ "String was edited after hashing - not frozen!\n (%lu): %s",
579
+ (unsigned long)fiobj_obj2cstr(o).len, fiobj_obj2cstr(o).data);
580
+ fiobj_free(o);
581
+
582
+ o = fiobj_str_static("Hello", 5);
583
+ TEST_ASSERT(obj2str(o)->is_small,
584
+ "Small Static should be converted to dynamic.\n");
585
+ fiobj_free(o);
586
+
587
+ o = fiobj_str_static(
588
+ "hello my dear friend, I hope that your are well and happy.", 58);
589
+ fiobj_str_write(o, " World", 6);
590
+ STR_EQ(o, "hello my dear friend, I hope that your are well and happy."
591
+ " World");
592
+ fiobj_free(o);
593
+
594
+ o = fiobj_strprintf("%u", 42);
595
+ TEST_ASSERT(fiobj_str_getlen(o) == 2, "fiobj_strprintf length error.\n");
596
+ TEST_ASSERT(fiobj_obj2num(o), "fiobj_strprintf integer error.\n");
597
+ TEST_ASSERT(!memcmp(fiobj_obj2cstr(o).data, "42", 2),
598
+ "fiobj_strprintf string error.\n");
599
+ fiobj_free(o);
600
+
601
+ o = fiobj_str_buf(4);
602
+ for (int i = 0; i < 16000; ++i) {
603
+ fiobj_str_write(o, "a", 1);
604
+ }
605
+ TEST_ASSERT(obj2str(o)->len == 16000, "16K fiobj_str_write not 16K.\n");
606
+ TEST_ASSERT(obj2str(o)->capa > 16001,
607
+ "16K fiobj_str_write capa not enough.\n");
608
+ fiobj_free(o);
609
+
610
+ fprintf(stderr, "* passed.\n");
611
+ }
612
+ #endif