History

Initial public commit for Dis VM

arr committed 10 years ago

Commit f95902eNo parent (first commit)40 files · +9358 −0Browse files

Showing the changes to src/vm/string.cpp (1 of 40 files). Show all

Unified · Split

src/vm/string.cpp+522−0
@@ -0,0 +1,522 @@
1//
2// Dis VM
3// File: string.cpp
4// Author: arr
5//
6
7#include <cinttypes>
8#include <runtime.h>
9#include <utf8.h>
10#include <vm_memory.h>
11#include <debug.h>
12#include <exceptions.h>
13
14using namespace disvm;
15using namespace disvm::runtime;
16
17std::shared_ptr<const type_descriptor_t> vm_string_t::type_desc()
18{
19 return intrinsic_type_desc::type<vm_string_t>();
20}
21
22namespace
23{
24 // Enumeration for character type compare cases.
25 enum class compare_case
26 {
27 ascii_ascii,
28 ascii_rune,
29 rune_ascii,
30 rune_rune
31 };
32
33 // Scenario matrix for compare cases
34 const compare_case scenario_matrix[][2] =
35 {
36 { compare_case::ascii_ascii, compare_case::ascii_rune },
37 { compare_case::rune_ascii, compare_case::rune_rune },
38 };
39
40 // Case indices
41 const auto ascii_idx = std::size_t{ 0 };
42 const auto rune_idx = std::size_t{ 1 };
43
44 // Compare function for character types.
45 template <typename CharacterType1, typename CharacterType2>
46 int _compare(word_t len, CharacterType1 *s1, CharacterType2 *s2)
47 {
48 for (auto i = word_t{ 0 }; i < len; ++i)
49 {
50 const auto sc1 = static_cast<rune_t>(s1[i]);
51 const auto sc2 = static_cast<rune_t>(s2[i]);
52
53 if (sc1 != sc2)
54 return (sc1 < sc2) ? (-1) : 1;
55 }
56
57 return 0;
58 }
59}
60
61int vm_string_t::compare(const vm_string_t *s1, const vm_string_t *s2)
62{
63 auto len_1 = word_t{ 0 };
64 auto scen_1 = ascii_idx;
65 void *str_mem_1 = "";
66 if (s1 != nullptr)
67 {
68 len_1 = s1->_length;
69 scen_1 = s1->_character_size == sizeof(char) ? ascii_idx : rune_idx;
70 str_mem_1 = (s1->_is_alloc) ? s1->_mem.alloc : s1->_mem.local;
71 }
72
73 auto len_2 = word_t{ 0 };
74 auto scen_2 = ascii_idx;
75 void *str_mem_2 = "";
76 if (s2 != nullptr)
77 {
78 len_2 = s2->_length;
79 scen_2 = s2->_character_size == sizeof(char) ? ascii_idx : rune_idx;
80 str_mem_2 = (s2->_is_alloc) ? s2->_mem.alloc : s2->_mem.local;
81 }
82
83 // Check the simple compare case between the same string.
84 if (str_mem_1 == str_mem_2)
85 return 0;
86
87 const auto scenario = scenario_matrix[scen_1][scen_2];
88
89 auto result = int{ 0 };
90 const auto min_length = std::min(len_1, len_2);
91 switch (scenario)
92 {
93 default:
94 assert(false && "Unknown compare scenario");
95 case compare_case::ascii_ascii:
96 case compare_case::rune_rune:
97 result = std::memcmp(str_mem_1, str_mem_2, min_length);
98 break;
99 case compare_case::ascii_rune:
100 result = _compare(min_length, reinterpret_cast<char *>(str_mem_1), reinterpret_cast<rune_t *>(str_mem_2));
101 break;
102 case compare_case::rune_ascii:
103 result = _compare(min_length, reinterpret_cast<rune_t *>(str_mem_1), reinterpret_cast<char *>(str_mem_2));
104 break;
105 }
106
107 if (result != 0)
108 return result;
109
110 // Comparing lengths if one string is a proper sub-string of the other.
111 return (len_1 - len_2);
112}
113
114namespace
115{
116 template<typename DestCharacterType, typename SourceCharacterType>
117 void copy_characters_to(DestCharacterType *dest, word_t dest_start, const SourceCharacterType *source, word_t source_length)
118 {
119 for (auto i = word_t{ 0 }; i < source_length; ++i)
120 dest[dest_start + i] = static_cast<DestCharacterType>(source[i]);
121 }
122
123 template<>
124 void copy_characters_to(char *dest, word_t dest_start, const char *source, word_t source_length)
125 {
126 assert(dest != nullptr && source != nullptr);
127 std::memcpy((dest + dest_start), source, source_length);
128 }
129
130 template<>
131 void copy_characters_to(rune_t *dest, word_t dest_start, const rune_t *source, word_t source_length)
132 {
133 assert(dest != nullptr && source != nullptr);
134 std::memcpy((dest + dest_start), source, (source_length * sizeof(rune_t)));
135 }
136
137 void combine(
138 bool dest_is_rune,
139 uint8_t *dest,
140 bool source1_is_rune,
141 word_t source1_length,
142 const uint8_t *source1,
143 bool source2_is_rune,
144 word_t source2_length,
145 const uint8_t *source2)
146 {
147 if (dest_is_rune)
148 {
149 auto dest_rune = reinterpret_cast<rune_t *>(dest);
150 if (source1_is_rune)
151 copy_characters_to(dest_rune, 0, reinterpret_cast<const rune_t *>(source1), source1_length);
152 else
153 copy_characters_to(dest_rune, 0, source1, source1_length);
154
155 if (source2_is_rune)
156 copy_characters_to(dest_rune, source1_length, reinterpret_cast<const rune_t *>(source2), source2_length);
157 else
158 copy_characters_to(dest_rune, source1_length, source2, source2_length);
159 }
160 else
161 {
162 copy_characters_to(dest, 0, source1, source1_length);
163 copy_characters_to(dest, source1_length, source2, source2_length);
164 }
165 }
166
167 word_t compute_max_length(word_t current_length)
168 {
169 // Length + 1 (null) + space for string expansion
170 return (current_length + 1 + (current_length / 4));
171 }
172}
173
174vm_string_t::vm_string_t()
175 : vm_alloc_t(vm_string_t::type_desc())
176 , _character_size{ sizeof(char) }
177 , _encoded_str{ nullptr }
178 , _is_alloc{ false }
179 , _length{ 0 }
180 , _length_max{ sizeof(_mem.local) }
181 , _mem{}
182{ }
183
184vm_string_t::vm_string_t(std::size_t encoded_str_len, const uint8_t *encoded_str)
185 : vm_alloc_t(vm_string_t::type_desc())
186 , _character_size{ sizeof(char) }
187 , _encoded_str{ nullptr }
188 , _is_alloc{ false }
189 , _length{ 0 }
190 , _length_max{ sizeof(_mem.local) }
191 , _mem{}
192{
193 assert((encoded_str_len > 0 && encoded_str != nullptr) || (encoded_str_len == 0 && encoded_str == nullptr));
194 auto character_count = utf8::count_codepoints(encoded_str, encoded_str_len);
195 _character_size = (encoded_str_len != character_count) ? sizeof(rune_t) : sizeof(char);
196
197 _length = static_cast<word_t>(character_count);
198 _length_max = sizeof(_mem.local) / _character_size;
199
200 // Allocate memory based on character count and size
201 auto dest = _mem.local;
202 if ((encoded_str_len + 1) > sizeof(_mem.local))
203 {
204 // If we are going to allocate, make sure it is larger than needed.
205 _length_max = compute_max_length(_length);
206 _mem.alloc = calloc_memory<uint8_t>(_length_max * _character_size);
207 _is_alloc = true;
208
209 dest = _mem.alloc;
210 }
211
212 // Copy over the entire buffer if ASCII or convert and copy if multi-byte
213 if (_character_size == sizeof(char))
214 {
215 std::memcpy(dest, encoded_str, encoded_str_len);
216 }
217 else
218 {
219 auto rune_dest = reinterpret_cast<rune_t *>(dest);
220 for (auto i = word_t{ 0 }; i < _length; ++i)
221 encoded_str += utf8::decode(encoded_str, rune_dest[i]);
222 }
223
224 if (debug::is_component_tracing_enabled<debug::component_trace_t::memory>())
225 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "init: vm string: %d %d >>%s<<\n", _length, (_length_max * _character_size), dest);
226}
227
228vm_string_t::vm_string_t(const vm_string_t &s1, const vm_string_t &s2)
229 : vm_alloc_t(vm_string_t::type_desc())
230 , _character_size{ std::max(s1._character_size, s2._character_size) }
231 , _encoded_str{ nullptr }
232 , _is_alloc{ true }
233 , _length{ s1._length + s2._length }
234 , _length_max{ s1._length + s2._length }
235 , _mem{}
236{
237 _mem.alloc = calloc_memory<uint8_t>(_length_max * _character_size);
238
239 const auto s1_data = (s1._is_alloc) ? s1._mem.alloc : s1._mem.local;
240 const auto s2_data = (s2._is_alloc) ? s2._mem.alloc : s2._mem.local;
241
242 // Combine the two strings
243 combine(
244 _character_size == sizeof(rune_t),
245 _mem.alloc,
246 s1._character_size == sizeof(rune_t),
247 s1._length,
248 s1_data,
249 s2._character_size == sizeof(rune_t),
250 s2._length,
251 s2_data);
252}
253
254vm_string_t::vm_string_t(const vm_string_t &other, word_t begin_index, word_t end_index)
255 : vm_alloc_t(other.alloc_type)
256 , _character_size{ other._character_size }
257 , _encoded_str{ nullptr }
258 , _is_alloc{ other._is_alloc }
259 , _length{ end_index - begin_index }
260 , _length_max{ compute_max_length(end_index - begin_index) }
261 , _mem{}
262{
263 if (end_index <= begin_index)
264 throw out_of_range_memory{};
265
266 if (begin_index < 0)
267 throw out_of_range_memory{ 0, other._length, begin_index };
268
269 if (other._length < end_index)
270 throw out_of_range_memory{ 0, other._length, end_index };
271
272 // Allocate memory based on character count and size
273 auto src = other._mem.local;
274 auto dest = _mem.local;
275 const auto length_in_bytes = _length * _character_size;
276 if (_is_alloc)
277 {
278 src = other._mem.alloc;
279 _mem.alloc = calloc_memory<uint8_t>(_length_max);
280 dest = _mem.alloc;
281 }
282
283 // Copy over string content starting at the correct index (char vs. rune_t)
284 ::memcpy(dest, (src + (begin_index * _character_size)), length_in_bytes);
285 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "copy: vm string: %#" PRIxPTR " %d %d %#" PRIxPTR "\n", &other, begin_index, end_index, this);
286}
287
288vm_string_t::~vm_string_t()
289{
290 // Free memory and string buffer
291 if (_is_alloc)
292 {
293 if (static_cast<void *>(_mem.alloc) != static_cast<void *>(_encoded_str))
294 free_memory(_encoded_str);
295
296 free_memory(_mem.alloc);
297 }
298 else
299 {
300 if (static_cast<void *>(_mem.local) != static_cast<void *>(_encoded_str))
301 free_memory(_encoded_str);
302 }
303
304 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "destroy: vm string\n");
305
306 debug::assign_debug_pointer(&_mem.alloc);
307 debug::assign_debug_pointer(&_encoded_str);
308}
309
310vm_string_t &vm_string_t::append(const vm_string_t &other)
311{
312 // This function does not adhere to the immutable string contract.
313 // Therefore, it is unsafe to call this function if there is more
314 // than 1 reference to this string instance.
315 assert(get_ref_count() <= 1);
316
317 // Check if the new string has any length at all.
318 const auto other_length = other._length;
319 if (other_length == 0)
320 return *this;
321
322 const auto other_is_rune = other._character_size == sizeof(rune_t);
323 const auto other_source = (other._is_alloc) ? other._mem.alloc : other._mem.local;
324
325 const auto local_is_rune = _character_size == sizeof(rune_t);
326 const auto local_length = _length;
327 const auto local_source = (_is_alloc) ? _mem.alloc : _mem.local;
328
329 // Release the encoded string for this instance.
330 {
331 std::lock_guard<std::mutex> lock{ _encoded_str_lock };
332 if (_encoded_str != reinterpret_cast<char*>(local_source))
333 {
334 free_memory(_encoded_str);
335 _encoded_str = nullptr;
336 }
337 }
338
339 // Define the values for the appended string
340 const auto new_length = local_length + other_length;
341 const auto new_length_max = (new_length == 0) ? static_cast<word_t>(sizeof(_mem.local)) : compute_max_length(new_length);
342 const auto new_is_rune = local_is_rune || other_is_rune;
343 uint8_t *new_source = nullptr;
344
345 // Check if this string and the supplied string have the same character size.
346 if (new_is_rune)
347 new_source = calloc_memory<uint8_t>(new_length_max * sizeof(rune_t));
348 else
349 new_source = calloc_memory<uint8_t>(new_length_max);
350
351 // Combine the two string sources
352 combine(
353 new_is_rune,
354 new_source,
355 local_is_rune,
356 local_length,
357 local_source,
358 other_is_rune,
359 other_length,
360 other_source);
361
362 _length = new_length;
363 _length_max = new_length_max;
364
365 // Free the local allocation
366 if (_is_alloc)
367 {
368 free_memory(local_source);
369 _mem.alloc = nullptr;
370 }
371
372 _mem.alloc = new_source;
373 _is_alloc = true;
374 _character_size = new_is_rune ? sizeof(rune_t) : sizeof(char);
375
376 return *this;
377}
378
379word_t vm_string_t::get_length() const
380{
381 return _length;
382}
383
384rune_t vm_string_t::get_rune(word_t index) const
385{
386 if (index < 0 && _length <= index)
387 throw out_of_range_memory{ 0, _length - 1, index };
388
389 // Determine if local or allocated memory
390 auto source = reinterpret_cast<const char *>(_mem.local);
391 if (_is_alloc)
392 source = reinterpret_cast<const char *>(_mem.alloc);
393
394 // Check if multi-byte or ASCII
395 if (_character_size != sizeof(char))
396 return reinterpret_cast<const rune_t *>(source)[index];
397 else
398 return static_cast<const rune_t>(source[index]);
399}
400
401void vm_string_t::set_rune(word_t index, rune_t value)
402{
403 // This function does not adhere to the immutable string contract.
404 // Therefore, it is unsafe to call this function if there is more
405 // than 1 reference to this string instance.
406 assert(get_ref_count() <= 1);
407
408 if (value > utf8::constants::max_supported_codepoint)
409 throw invalid_utf8{};
410
411 // Index equal to length is allowed to grow the string
412 if (index < 0 && _length <= index)
413 throw out_of_range_memory{ 0, _length, index };
414
415 // Determine if local or allocated memory
416 auto source = static_cast<uint8_t *>(_mem.local);
417 if (_is_alloc)
418 {
419 source = static_cast<uint8_t *>(_mem.alloc);
420
421 // Since we are setting a rune, the string value should be freed.
422 if (_encoded_str != nullptr)
423 free_memory(_encoded_str);
424 }
425
426 // Reset the string so it can be regenerated.
427 _encoded_str = nullptr;
428
429 const auto new_character_size = (value > utf8::constants::max_codepoint_ascii) ? sizeof(rune_t) : sizeof(char);
430 assert(new_character_size < std::numeric_limits<uint8_t>::max());
431
432 if (new_character_size > _character_size) // Expand the string if the new character is non-ASCII.
433 {
434 _character_size = static_cast<uint8_t>(new_character_size);
435 assert(_character_size == sizeof(rune_t));
436
437 _length_max = (_length == 0) ? static_cast<word_t>(sizeof(_mem.local)) : compute_max_length(_length);
438 auto new_alloc = calloc_memory<rune_t>(_length_max * _character_size);
439
440 // Copy source into the new alloc.
441 copy_characters_to(new_alloc, 0, source, _length);
442
443 // Free memory, if allocated
444 if (_is_alloc)
445 free_memory(source);
446
447 debug::assign_debug_memory(&_mem, sizeof(_mem));
448
449 _is_alloc = true;
450 _mem.alloc = reinterpret_cast<uint8_t *>(new_alloc);
451 source = _mem.alloc;
452 }
453 else if (static_cast<word_t>(_length_max) <= index) // Check if string can accomodate the new character.
454 {
455 _length_max = (_length == 0) ? static_cast<word_t>(sizeof(_mem.local)) : compute_max_length(_length);
456 auto new_alloc = calloc_memory<uint8_t>(_length_max * _character_size);
457 ::memcpy(new_alloc, source, _length * _character_size);
458
459 // Free memory, if allocated
460 if (_is_alloc)
461 free_memory(source);
462
463 debug::assign_debug_memory(&_mem, sizeof(_mem));
464
465 _is_alloc = true;
466 _mem.alloc = new_alloc;
467 source = _mem.alloc;
468 }
469
470 // Check if multi-byte or ASCII.
471 if (_character_size != sizeof(char))
472 reinterpret_cast<rune_t *>(source)[index] = value;
473 else
474 source[index] = static_cast<uint8_t>(value & utf8::constants::max_codepoint_ascii);
475
476 // Increment the length, if the index matches the length.
477 if (_length == index)
478 _length++;
479}
480
481int vm_string_t::compare_to(const vm_string_t *s) const
482{
483 return vm_string_t::compare(this, s);
484}
485
486const char *vm_string_t::str() const
487{
488 if (_encoded_str != nullptr)
489 return _encoded_str;
490
491 {
492 // [PERF] Use a spin-lock instead of a mutex here.
493 std::lock_guard<std::mutex> lock{ _encoded_str_lock };
494
495 if (_encoded_str != nullptr)
496 return _encoded_str;
497
498 // If the string is ASCII then just return the memory
499 if (_character_size == sizeof(char))
500 {
501 _encoded_str = (_is_alloc) ? reinterpret_cast<char *>(_mem.alloc) : reinterpret_cast<char *>(_mem.local);
502 return _encoded_str;
503 }
504
505 const auto rune_str = (_is_alloc) ? reinterpret_cast<rune_t *>(_mem.alloc) : reinterpret_cast<rune_t *>(_mem.local);
506 const auto str_len = (_length * _character_size) + 1; // Plus 1 for a null terminator
507 auto encoded_str_local = alloc_memory<char>(str_len);
508
509 auto s = reinterpret_cast<uint8_t *>(encoded_str_local);
510 for (auto i = word_t{ 0 }; i < _length; ++i)
511 {
512 auto curr_rune = rune_str[i];
513 s = utf8::encode(curr_rune, s);
514 }
515
516 // Ensure the final value is null.
517 *s = 0;
518
519 _encoded_str = encoded_str_local;
520 return _encoded_str;
521 }
522}