The Pedigree Project 0.1
String.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "pedigree/kernel/Log.h"
21#include "pedigree/kernel/utilities/Cord.h"
22#include "pedigree/kernel/utilities/String.h"
23#include "pedigree/kernel/utilities/StringView.h"
24#include "pedigree/kernel/utilities/assert.h"
25#include "pedigree/kernel/utilities/utility.h"
26
27#include <stdarg.h>
28
30static constexpr size_t StringMinimumAllocationSize = 64;
31
32String::String() : m_Data(nullptr), m_Length(0), m_Size(0), m_Hash(0) {}
33
34String::String(const char* s) : String() {
35 assign(s);
36}
37
38String::String(const char* s, size_t length) : String() {
39 assign(s, length);
40}
41
42String::String(const char* s, size_t length, bool unsafe) : String() {
43 assign(s, length, unsafe);
44}
45
46String::String(const String& x) : String() {
47 assign(x);
48}
49
50String::String(const StringView& x) : String() {
51 assign(x.str(), x.length(), true);
52}
53
54String::String(String&& x) noexcept : String() {
55 move(static_cast<String&&>(x));
56}
57
58String::String(const Cord& x) : String() {
59 assign(x);
60}
61
62String::~String() {
63 clear();
64}
65
66void String::move(String&& other) noexcept {
67 clear();
68
69 // take ownership of the object
70 m_Data = other.m_Data;
71 m_Length = other.m_Length;
72 m_Size = other.m_Size;
73 m_Hash = other.m_Hash;
74
75 // free other string but don't destroy the heap pointer if we had one
76 // as it is now owned by this new instance
77 other.m_Data = 0;
78 other.clear();
79}
80
81String& String::operator=(String&& x) noexcept {
82 move(static_cast<String&&>(x));
83 return *this;
84}
85
86String& String::operator=(const String& x) {
87 assign(x);
88 return *this;
89}
90
91#if !STRING_DISABLE_EXPENSIVE_COPY_CONSTRUCTION
92String& String::operator=(const char* s) {
93 assign(s);
94 return *this;
95}
96#endif
97
98String& String::operator=(const Cord& x) {
99 assign(x);
100 return *this;
101}
102
103String& String::operator+=(const String& x) {
104 assert(assignable());
105
106 // Empty strings need not have storage, even for a terminating byte.
107 if (!x.length())
108 return *this;
109
110 if (this == &x) {
111 // The copy breaks self-append aliasing before the recursive append.
112 // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
113 String copy(x);
114 return *this += copy;
115 }
116
117 size_t newLength = x.length() + m_Length;
118
119 reserve(newLength + 1);
120
121 char* dst = extract();
122 const char* src = x.extract();
123
124 // Copy!
125 MemoryCopy(&dst[m_Length], src, x.length() + 1);
126 m_Length += x.length();
127
128 m_Hash = 0; // hash is no longer valid
129 return *this;
130}
131
132String& String::operator+=(const char* s) {
133 assert(assignable());
134
135 const char* current = extract();
136 if (s && current) {
137 const uintptr_t address = reinterpret_cast<uintptr_t>(s);
138 const uintptr_t begin = reinterpret_cast<uintptr_t>(current);
139 if ((address >= begin) && (address < (begin + m_Size))) {
140 String copy(s);
141 return *this += copy;
142 }
143 }
144
145 size_t slen = StringLength(s);
146 size_t newLength = slen + m_Length;
147
148 reserve(slen + m_Length + 1);
149 MemoryCopy(&m_Data[m_Length], s, slen + 1);
150 m_Length += slen;
151
152 m_Hash = 0;
153 return *this;
154}
155
156bool String::operator==(const String& s) const {
162
163 if (m_Length != s.m_Length) {
164 return false;
165 } else if (LIKELY(m_Hash && s.maybeHash())) {
166 if (m_Hash != s.hash()) {
167 // precomputed hash didn't match, don't bother
168 return false;
169 }
170 }
171
172 const char* buf = extract();
173 const char* other_buf = s.extract();
174
175 // Neither of these can be null because of the above conditions.
176 return !StringMatchN(buf, other_buf, m_Length);
177}
178
179bool String::operator==(const StringView& s) const {
180 // use StringView::operator==(const String &)
181 return s == *this;
182}
183
184bool String::operator==(const char* s) const {
185 const char* buf = extract();
186
187 if ((!m_Length) && (s == 0)) {
188 return true;
189 } else if (s == 0) {
190 // m_Length > 0 but other buffer is null.
191 return false;
192 } else if ((!m_Length) && *s) {
193 // Quick check when we're zero-length.
194 return false;
195 } else if (StringLength(s) != m_Length) {
196 return false;
197 } else {
198 return !StringMatchN(buf, s, m_Length);
199 }
200}
201
202bool String::compare(const char* s, size_t len) const {
203 if (m_Length != len) {
204 // Mismatch in length
205 return false;
206 } else if (UNLIKELY(s == 0)) {
207 // other buffer is null, don't match
208 return false;
209 } else {
210 const char* buf = extract();
211 return !StringMatchN(buf, s, m_Length);
212 }
213}
214
215char String::operator[](size_t i) const {
216 assert(i <= m_Length);
217 const char* buf = extract();
218 return buf[i];
219}
220
221uint32_t String::hash() const {
222 if (!m_Hash) {
223 return computeHash();
224 }
225
226 return m_Hash;
227}
228
229uint32_t String::hash() {
230 if (!m_Hash) {
231 computeHash();
232 }
233
234 return m_Hash;
235}
236
237uint32_t String::maybeHash() const {
238 return m_Hash;
239}
240
241size_t String::nextCharacter(size_t c) const {
242 const char* buf = extract();
243 return ::nextCharacter(buf, c);
244}
245
246size_t String::prevCharacter(size_t c) const {
247 const char* buf = extract();
248 return ::prevCharacter(buf, c);
249}
250
251void String::assign(const String& x) {
252 assert(assignable());
253
254 if (this == &x) {
255 return;
256 }
257
258 // Empty strings may have no storage, including a terminating byte.
259 if (!x.length()) {
260 clear();
261 return;
262 }
263
264 if (extract() && x.extract()) {
265 assert(extract() != x.extract());
266 }
267
268 reserve(x.size(), false);
269 MemoryCopy(m_Data, x.extract(), x.size());
270 m_Length = x.length();
271
272 // no need to recompute in this case
273 m_Hash = x.m_Hash;
274
275#if ADDITIONAL_CHECKS
276 if (*this != x) {
277 ERROR("mismatch: '" << *this << "' != '" << x << "'");
278 }
279 assert(*this == x);
280#endif
281}
282
283void String::assign(const Cord& x) {
284 assert(assignable());
285
286 reserve(x.length() + 1);
287
288 size_t offset = 0;
289 char* buf = extract();
290 for (auto& it : x.m_Segments) {
291 StringCopyN(buf + offset, it.ptr, it.length);
292 offset += it.length;
293 }
294 buf[offset] = 0;
295
296 m_Length = offset;
297
298 m_Hash = 0;
299}
300
301void String::assign(const char* s, size_t len, bool unsafe) {
302 assert(assignable());
303
304 const char* current = extract();
305 if (s && current) {
306 const uintptr_t address = reinterpret_cast<uintptr_t>(s);
307 const uintptr_t begin = reinterpret_cast<uintptr_t>(current);
308 if ((address >= begin) && (address < (begin + m_Size))) {
309 String copy(s, len, unsafe);
310 assign(copy);
311 return;
312 }
313 }
314
315 // Trying to assign self to self?
316 assert((m_Data == nullptr) || (m_Data && (m_Data != s)));
317
318 size_t copyLength = 0;
319 size_t origLength = len;
320 // len overrides all other optimizations
321 if (len) {
322 // Fix up length if the passed string is much smaller than the 'len'
323 // parameter (otherwise we think we have a giant string).
324 size_t trueLength = 0;
325 if (unsafe) {
326 trueLength = BoundedStringLength(s, len);
327 } else {
328 trueLength = StringLength(s);
329 }
330
331 if (trueLength < len) {
332 len = trueLength;
333 }
334
335 m_Length = len;
336 copyLength = len;
337 } else if (!s || !*s) {
338 m_Length = 0;
339 } else {
340 m_Length = StringLength(s);
341 copyLength = m_Length;
342 }
343
344 if (!m_Length) {
345 delete[] m_Data;
346 m_Data = 0;
347 m_Size = 0;
348 } else {
349 reserve(pedigree_std::max(origLength, copyLength + 1), false);
350 MemoryCopy(m_Data, s, copyLength);
351 m_Data[copyLength] = '\0';
352 }
353
354#if ADDITIONAL_CHECKS
355 if (!len) {
356 assert(*this == s);
357 }
358#endif
359
360 m_Hash = 0;
361}
362
363void String::reserve(size_t size) {
364 reserve(size, true);
365}
366
367void String::reserve(size_t size, bool zero) {
368 assert(resizable());
369
370 size = pedigree_std::max(size, StringMinimumAllocationSize);
371
372 if (size > m_Size) {
373 char* tmp = m_Data;
374 m_Data = new char[size];
375 if (tmp) {
376 MemoryCopy(m_Data, tmp, m_Size > size ? size : m_Size);
377 delete[] tmp;
378 } else if (zero) {
379 ByteSet(m_Data, 0, size);
380 }
381 m_Size = size;
382 }
383}
384
386 assert(resizable());
387
388 size_t newSize = pedigree_std::max(m_Length + 1, StringMinimumAllocationSize);
389
390 if (!m_Data || (newSize >= m_Size))
391 return;
392
393 char* oldData = m_Data;
394
395 m_Data = new char[newSize];
396 MemoryCopy(m_Data, oldData, m_Length + 1);
397
398 delete[] oldData;
399
400 m_Size = newSize;
401}
402
403void String::clear() noexcept {
404 assert(assignable());
405
406 if (m_Data) {
407 delete[] m_Data;
408 }
409 m_Data = 0;
410 m_Length = 0;
411 m_Size = 0;
412 m_Hash = 0;
413}
414
415void String::ltrim(size_t n) {
416 assert(assignable());
417
418 if (!n) {
419 return;
420 }
421
422 if (n > m_Length) {
423 clear();
424 return;
425 }
426
427 MemoryCopy(m_Data, &m_Data[n], m_Length - n);
428 m_Length -= n;
429 m_Data[m_Length] = 0;
430
431 m_Hash = 0;
432}
433
434void String::rtrim(size_t n) {
435 assert(assignable());
436
437 if (!n) {
438 return;
439 }
440
441 if (n > m_Length) {
442 clear();
443 return;
444 }
445
446 m_Data[m_Length - n] = 0;
447 m_Length -= n;
448
449 m_Hash = 0;
450}
451
452String String::split(size_t offset) {
453 String result;
454 split(offset, result);
455 return result;
456}
457
458void String::split(size_t offset, String& back) {
459 assert(assignable());
460
461 if (offset >= m_Length) {
462 back.clear();
463 return;
464 }
465
466 char* buf = extract();
467
468 back.assign(&buf[offset], m_Length - offset, true);
469 m_Length = offset;
470 buf[m_Length] = 0;
471
472 m_Hash = 0;
473}
474
476 assert(assignable());
477
478 lstrip();
479 rstrip();
480}
481
483 assert(assignable());
484
485 char* buf = extract();
486 if (!buf) {
487 // nothing to strip
488 return;
489 }
490
491 if (!iswhitespace(buf[0]))
492 return;
493
494 // finish up the byte tail
495 size_t n = 0;
496 while (n < m_Length && iswhitespace(buf[n]))
497 n++;
498
499 // Move the data to cover up the whitespace and avoid reallocating m_Data
500 m_Length -= n;
501 MemoryCopy(buf, (buf + n), m_Length);
502 buf[m_Length] = 0;
503
504 m_Hash = 0;
505}
506
508 assert(assignable());
509
510 char* buf = extract();
511 if (!buf || !m_Length) {
512 // nothing to strip
513 return;
514 }
515
516 if (!iswhitespace(buf[m_Length - 1]))
517 return;
518
519 size_t n = m_Length;
520 while (n > 0 && iswhitespace(buf[n - 1]))
521 n--;
522
523 // m_Size is still valid - it's the size of the buffer. m_Length is now
524 // updated to contain the proper length of the string, but the buffer is
525 // not reallocated.
526 m_Length = n;
527 buf[m_Length] = 0;
528
529 m_Hash = 0;
530}
531
532Vector<String> String::tokenise(char token) {
533 Vector<String> list;
534 tokenise(token, list);
535 return list;
536}
537
538size_t String::Utf32ToUtf8(uint32_t utf32, char* utf8) {
539 // clear out the string before conversion
540 ByteSet(utf8, 0, 4);
541
542 size_t nbuf = 0;
543 if (utf32 <= 0x7F) {
544 utf8[0] = utf32 & 0x7F;
545 nbuf = 1;
546 } else if (utf32 <= 0x7FF) {
547 utf8[0] = 0xC0 | ((utf32 >> 6) & 0x1F);
548 utf8[1] = 0x80 | (utf32 & 0x3F);
549 nbuf = 2;
550 } else if (utf32 <= 0xFFFF) {
551 utf8[0] = 0xE0 | ((utf32 >> 12) & 0x0F);
552 utf8[1] = 0x80 | ((utf32 >> 6) & 0x3F);
553 utf8[2] = 0x80 | (utf32 & 0x3F);
554 nbuf = 3;
555 } else if (utf32 <= 0x10FFFF) {
556 utf8[0] = 0xF0 | ((utf32 >> 18) & 0x07);
557 utf8[1] = 0x80 | ((utf32 >> 12) & 0x3F);
558 utf8[2] = 0x80 | ((utf32 >> 6) & 0x3F);
559 utf8[3] = 0x80 | (utf32 & 0x3F);
560 nbuf = 4;
561 }
562
563 return nbuf;
564}
565
566void String::tokenise(char token, Vector<StringView>& output) const {
567 const char* orig_buffer = extract();
568 const char* buffer = orig_buffer;
569
570 output.clear();
571 // reserve for the worst-case, where we tokenise every character of this string
572 // output.reserve(m_Length, false);
573
574 const char* pos = buffer ? StringFind(buffer, token) : nullptr;
575 while (pos && (*buffer)) {
576 if (pos == buffer) {
577 ++buffer;
578 continue;
579 }
580
581 if (pos > buffer) {
582 output.createBack(buffer, pos - buffer);
583 }
584
585 buffer = pos + 1;
586
587 pos = StringFind(buffer, token);
588 }
589
590 if (buffer && !pos) {
591 // might be able to just copy this string rather than copy & move
592 if (buffer == orig_buffer) {
593 output.createBack(view());
594 } else {
595 size_t length = m_Length - (buffer - orig_buffer);
596 if (length) {
597 output.createBack(buffer, length);
598 }
599 }
600 }
601}
602
603void String::tokenise(char token, Vector<String>& output) const {
604 Vector<StringView> views;
605 tokenise(token, views);
606
607 output.clear();
608 output.reserve(views.count(), false);
609 for (auto& it : views) {
610 output.createBack(it);
611 }
612}
613
615 assert(assignable());
616
617 if (!m_Length)
618 return;
619
620 char* buf = extract();
621
622 StringCopy(buf, &buf[1]);
623 --m_Length;
624
625 m_Hash = 0;
626}
627
629 assert(assignable());
630
631 if (!m_Length)
632 return;
633
634 char* buf = extract();
635
636 m_Length--;
637 buf[m_Length] = '\0';
638
639 m_Hash = 0;
640}
641
642void String::Format(const char* fmt, ...) {
643 assert(assignable());
644
645 reserve(256);
646 va_list vl;
647 va_start(vl, fmt);
648 m_Length = VStringFormat(m_Data, fmt, vl);
649 va_end(vl);
650
651 m_Hash = 0;
652}
653
654bool String::endswith(const char c) const {
655 if (!m_Length) {
656 return false;
657 }
658
659 const char* buf = extract();
660 return buf[m_Length - 1] == c;
661}
662
663bool String::endswith(const String& s) const {
664 // Not a suffix check.
665 if (m_Length == s.length())
666 return *this == s;
667
668 const char* otherbuf = s.extract();
669 return endswith(otherbuf, s.length());
670}
671
672bool String::endswith(const char* s, size_t len) const {
673 if (!len) {
674 len = StringLength(s);
675 }
676
677 // Suffix exceeds our length.
678 if (m_Length < len)
679 return false;
680
681 const char* mybuf = extract();
682 mybuf += m_Length - len;
683
684 return !MemoryCompare(mybuf, s, len);
685}
686
687bool String::startswith(const char c) const {
688 if (!m_Length) {
689 return false;
690 }
691
692 const char* buf = extract();
693 return buf[0] == c;
694}
695
696bool String::startswith(const String& s) const {
697 // Not a prefix check.
698 if (m_Length == s.length())
699 return *this == s;
700
701 const char* otherbuf = s.extract();
702 return startswith(otherbuf, s.length());
703}
704
705bool String::startswith(const char* s, size_t len) const {
706 if (!len) {
707 len = StringLength(s);
708 }
709
710 // Prefix exceeds our length.
711 if (m_Length < len)
712 return false;
713
714 const char* mybuf = extract();
715
716 // Do the check.
717 return !MemoryCompare(mybuf, s, len);
718}
719
720bool String::iswhitespace(const char c) const {
721 return (c <= ' ' || c == '\x7f');
722}
723
724char* String::extract() const {
725 return m_Data;
726}
727
728ssize_t String::find(const char c) const {
729 if (!m_Length)
730 return -1;
731
735 ssize_t signedLength = m_Length;
736
737 char* buf = extract();
738 for (ssize_t i = 0; i < signedLength; ++i) {
739 if (buf[i] == c) {
740 return i;
741 }
742 }
743
744 return -1;
745}
746
747ssize_t String::rfind(const char c) const {
748 if (!m_Length)
749 return -1;
750
751 char* buf = extract();
752 for (ssize_t i = m_Length - 1, n = 0; i >= 0; --i, ++n) {
753 if (buf[i] == c) {
754 return n;
755 }
756 }
757
758 return -1;
759}
760
762 if (m_Length) {
763 m_Hash = spookyHash(extract(), m_Length);
764 } else {
765 m_Hash = 0;
766 }
767}
768
769uint32_t String::computeHash() const {
770 if (m_Length) {
771 return spookyHash(extract(), m_Length);
772 } else {
773 return 0;
774 }
775}
776
778 String result;
779 result.assign(*this);
780 return result;
781}
782
784 // hash already calculated, enable hashing
785 const char* buf = extract();
786 return StringView(buf, m_Length, m_Hash, true);
787}
788
789bool String::resizable() const {
790 return true;
791}
792
793bool String::assignable() const {
794 return true;
795}
796
797void String::setLength(size_t n) {
798 m_Length = n;
799}
800
801void String::setSize(size_t n) {
802 m_Size = n;
803}
Definition Cord.h:33
StringView view() const
Definition String.cc:783
void computeHash()
Definition String.cc:761
String copy() const
Definition String.cc:777
virtual bool assignable() const
Definition String.cc:793
size_t m_Length
Definition String.h:234
uint32_t hash() const
Definition String.cc:221
void lstrip()
Definition String.cc:482
void move(String &&other) noexcept
Definition String.cc:66
bool startswith(const char c) const
Definition String.cc:687
bool compare(const char *s, size_t len) const
Definition String.cc:202
bool operator==(const String &s) const
Definition String.cc:156
void strip()
Definition String.cc:475
void rstrip()
Definition String.cc:507
virtual bool resizable() const
Definition String.cc:789
void setLength(size_t n)
Definition String.cc:797
bool iswhitespace(const char c) const
Definition String.cc:720
virtual char * extract() const
Definition String.cc:724
String()
Definition String.cc:32
void ltrim(size_t n)
Definition String.cc:415
void setSize(size_t n)
Definition String.cc:801
ssize_t find(const char c) const
Definition String.cc:728
size_t nextCharacter(size_t c) const
Definition String.cc:241
String split(size_t offset)
Definition String.cc:452
size_t m_Size
Definition String.h:236
void rtrim(size_t n)
Definition String.cc:434
bool endswith(const char c) const
Definition String.cc:654
char * m_Data
Definition String.h:232
static size_t Utf32ToUtf8(uint32_t utf32, char *utf8)
Definition String.cc:538
void lchomp()
Definition String.cc:614
size_t prevCharacter(size_t c) const
Definition String.cc:246
void chomp()
Definition String.cc:628
uint32_t maybeHash() const
Definition String.cc:237
uint32_t m_Hash
Definition String.h:238
void downsize()
Definition String.cc:385
A vector / dynamic array.
Definition Vector.h:33
size_t count() const
Definition Vector.h:270