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) {
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) {
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) {
253
254 if (this == &x)
255 return;
256
257 if (extract() && x.extract()) {
258 assert(extract() != x.extract());
259 }
260
261 reserve(x.size(), false);
262 MemoryCopy(m_Data, x.extract(), x.size());
263 m_Length = x.length();
264
265 // no need to recompute in this case
266 m_Hash = x.m_Hash;
267
268#if ADDITIONAL_CHECKS
269 if (*this != x) {
270 ERROR("mismatch: '" << *this << "' != '" << x << "'");
271 }
272 assert(*this == x);
273#endif
274}
275
276void String::assign(const Cord& x) {
278
279 reserve(x.length() + 1);
280
281 size_t offset = 0;
282 char* buf = extract();
283 for (auto& it : x.m_Segments) {
284 StringCopyN(buf + offset, it.ptr, it.length);
285 offset += it.length;
286 }
287 buf[offset] = 0;
288
289 m_Length = offset;
290
291 m_Hash = 0;
292}
293
294void String::assign(const char* s, size_t len, bool unsafe) {
296
297 const char* current = extract();
298 if (s && current) {
299 const uintptr_t address = reinterpret_cast<uintptr_t>(s);
300 const uintptr_t begin = reinterpret_cast<uintptr_t>(current);
301 if ((address >= begin) && (address < (begin + m_Size))) {
302 String copy(s, len, unsafe);
303 assign(copy);
304 return;
305 }
306 }
307
308 // Trying to assign self to self?
309 assert((m_Data == nullptr) || (m_Data && (m_Data != s)));
310
311 size_t copyLength = 0;
312 size_t origLength = len;
313 // len overrides all other optimizations
314 if (len) {
315 // Fix up length if the passed string is much smaller than the 'len'
316 // parameter (otherwise we think we have a giant string).
317 size_t trueLength = 0;
318 if (unsafe) {
319 trueLength = BoundedStringLength(s, len);
320 } else {
321 trueLength = StringLength(s);
322 }
323
324 if (trueLength < len) {
325 len = trueLength;
326 }
327
328 m_Length = len;
329 copyLength = len;
330 } else if (!s || !*s) {
331 m_Length = 0;
332 } else {
333 m_Length = StringLength(s);
334 copyLength = m_Length;
335 }
336
337 if (!m_Length) {
338 delete[] m_Data;
339 m_Data = 0;
340 m_Size = 0;
341 } else {
342 reserve(pedigree_std::max(origLength, copyLength + 1), false);
343 MemoryCopy(m_Data, s, copyLength);
344 m_Data[copyLength] = '\0';
345 }
346
347#if ADDITIONAL_CHECKS
348 if (!len) {
349 assert(*this == s);
350 }
351#endif
352
353 m_Hash = 0;
354}
355
356void String::reserve(size_t size) {
357 reserve(size, true);
358}
359
360void String::reserve(size_t size, bool zero) {
361 assert(resizable());
362
363 size = pedigree_std::max(size, StringMinimumAllocationSize);
364
365 if (size > m_Size) {
366 char* tmp = m_Data;
367 m_Data = new char[size];
368 if (tmp) {
369 MemoryCopy(m_Data, tmp, m_Size > size ? size : m_Size);
370 delete[] tmp;
371 } else if (zero) {
372 ByteSet(m_Data, 0, size);
373 }
374 m_Size = size;
375 }
376}
377
379 assert(resizable());
380
381 size_t newSize = pedigree_std::max(m_Length + 1, StringMinimumAllocationSize);
382
383 if (!m_Data || (newSize >= m_Size))
384 return;
385
386 char* oldData = m_Data;
387
388 m_Data = new char[newSize];
389 MemoryCopy(m_Data, oldData, m_Length + 1);
390
391 delete[] oldData;
392
393 m_Size = newSize;
394}
395
396void String::clear() noexcept {
398
399 if (m_Data) {
400 delete[] m_Data;
401 }
402 m_Data = 0;
403 m_Length = 0;
404 m_Size = 0;
405 m_Hash = 0;
406}
407
408void String::ltrim(size_t n) {
410
411 if (n > m_Length) {
412 clear();
413 return;
414 }
415
416 MemoryCopy(m_Data, &m_Data[n], m_Length - n);
417 m_Length -= n;
418 m_Data[m_Length] = 0;
419
420 m_Hash = 0;
421}
422
423void String::rtrim(size_t n) {
425
426 if (n > m_Length) {
427 clear();
428 return;
429 }
430
431 m_Data[m_Length - n] = 0;
432 m_Length -= n;
433
434 m_Hash = 0;
435}
436
437String String::split(size_t offset) {
438 String result;
439 split(offset, result);
440 return result;
441}
442
443void String::split(size_t offset, String& back) {
445
446 if (offset >= m_Length) {
447 back.clear();
448 return;
449 }
450
451 char* buf = extract();
452
453 back.assign(&buf[offset], m_Length - offset, true);
454 m_Length = offset;
455 buf[m_Length] = 0;
456
457 m_Hash = 0;
458}
459
462
463 lstrip();
464 rstrip();
465}
466
469
470 char* buf = extract();
471 if (!buf) {
472 // nothing to strip
473 return;
474 }
475
476 if (!iswhitespace(buf[0]))
477 return;
478
479 // finish up the byte tail
480 size_t n = 0;
481 while (n < m_Length && iswhitespace(buf[n]))
482 n++;
483
484 // Move the data to cover up the whitespace and avoid reallocating m_Data
485 m_Length -= n;
486 MemoryCopy(buf, (buf + n), m_Length);
487 buf[m_Length] = 0;
488
489 m_Hash = 0;
490}
491
494
495 char* buf = extract();
496 if (!buf) {
497 // nothing to strip
498 return;
499 }
500
501 if (!iswhitespace(buf[m_Length - 1]))
502 return;
503
504 size_t n = m_Length;
505 while (n > 0 && iswhitespace(buf[n - 1]))
506 n--;
507
508 // m_Size is still valid - it's the size of the buffer. m_Length is now
509 // updated to contain the proper length of the string, but the buffer is
510 // not reallocated.
511 m_Length = n;
512 buf[m_Length] = 0;
513
514 m_Hash = 0;
515}
516
517Vector<String> String::tokenise(char token) {
518 Vector<String> list;
519 tokenise(token, list);
520 return list;
521}
522
523size_t String::Utf32ToUtf8(uint32_t utf32, char* utf8) {
524 // clear out the string before conversion
525 ByteSet(utf8, 0, 4);
526
527 size_t nbuf = 0;
528 if (utf32 <= 0x7F) {
529 utf8[0] = utf32 & 0x7F;
530 nbuf = 1;
531 } else if (utf32 <= 0x7FF) {
532 utf8[0] = 0xC0 | ((utf32 >> 6) & 0x1F);
533 utf8[1] = 0x80 | (utf32 & 0x3F);
534 nbuf = 2;
535 } else if (utf32 <= 0xFFFF) {
536 utf8[0] = 0xE0 | ((utf32 >> 12) & 0x0F);
537 utf8[1] = 0x80 | ((utf32 >> 6) & 0x3F);
538 utf8[2] = 0x80 | (utf32 & 0x3F);
539 nbuf = 3;
540 } else if (utf32 <= 0x10FFFF) {
541 utf8[0] = 0xF0 | ((utf32 >> 18) & 0x07);
542 utf8[1] = 0x80 | ((utf32 >> 12) & 0x3F);
543 utf8[2] = 0x80 | ((utf32 >> 6) & 0x3F);
544 utf8[3] = 0x80 | (utf32 & 0x3F);
545 nbuf = 4;
546 }
547
548 return nbuf;
549}
550
551void String::tokenise(char token, Vector<StringView>& output) const {
552 const char* orig_buffer = extract();
553 const char* buffer = orig_buffer;
554
555 output.clear();
556 // reserve for the worst-case, where we tokenise every character of this string
557 // output.reserve(m_Length, false);
558
559 const char* pos = buffer ? StringFind(buffer, token) : nullptr;
560 while (pos && (*buffer)) {
561 if (pos == buffer) {
562 ++buffer;
563 continue;
564 }
565
566 if (pos > buffer) {
567 output.createBack(buffer, pos - buffer);
568 }
569
570 buffer = pos + 1;
571
572 pos = StringFind(buffer, token);
573 }
574
575 if (buffer && !pos) {
576 // might be able to just copy this string rather than copy & move
577 if (buffer == orig_buffer) {
578 output.createBack(view());
579 } else {
580 size_t length = m_Length - (buffer - orig_buffer);
581 if (length) {
582 output.createBack(buffer, length);
583 }
584 }
585 }
586}
587
588void String::tokenise(char token, Vector<String>& output) const {
589 Vector<StringView> views;
590 tokenise(token, views);
591
592 output.clear();
593 output.reserve(views.count(), false);
594 for (auto& it : views) {
595 output.createBack(it);
596 }
597}
598
601
602 if (!m_Length)
603 return;
604
605 char* buf = extract();
606
607 StringCopy(buf, &buf[1]);
608 --m_Length;
609
610 m_Hash = 0;
611}
612
615
616 if (!m_Length)
617 return;
618
619 char* buf = extract();
620
621 m_Length--;
622 buf[m_Length] = '\0';
623
624 m_Hash = 0;
625}
626
627void String::Format(const char* fmt, ...) {
629
630 reserve(256);
631 va_list vl;
632 va_start(vl, fmt);
633 m_Length = VStringFormat(m_Data, fmt, vl);
634 va_end(vl);
635
636 m_Hash = 0;
637}
638
639bool String::endswith(const char c) const {
640 if (!m_Length) {
641 return false;
642 }
643
644 const char* buf = extract();
645 return buf[m_Length - 1] == c;
646}
647
648bool String::endswith(const String& s) const {
649 // Not a suffix check.
650 if (m_Length == s.length())
651 return *this == s;
652
653 const char* otherbuf = s.extract();
654 return endswith(otherbuf, s.length());
655}
656
657bool String::endswith(const char* s, size_t len) const {
658 if (!len) {
659 len = StringLength(s);
660 }
661
662 // Suffix exceeds our length.
663 if (m_Length < len)
664 return false;
665
666 const char* mybuf = extract();
667 mybuf += m_Length - len;
668
669 return !MemoryCompare(mybuf, s, len);
670}
671
672bool String::startswith(const char c) const {
673 if (!m_Length) {
674 return false;
675 }
676
677 const char* buf = extract();
678 return buf[0] == c;
679}
680
681bool String::startswith(const String& s) const {
682 // Not a prefix check.
683 if (m_Length == s.length())
684 return *this == s;
685
686 const char* otherbuf = s.extract();
687 return startswith(otherbuf, s.length());
688}
689
690bool String::startswith(const char* s, size_t len) const {
691 if (!len) {
692 len = StringLength(s);
693 }
694
695 // Prefix exceeds our length.
696 if (m_Length < len)
697 return false;
698
699 const char* mybuf = extract();
700
701 // Do the check.
702 return !MemoryCompare(mybuf, s, len);
703}
704
705bool String::iswhitespace(const char c) const {
706 return (c <= ' ' || c == '\x7f');
707}
708
709char* String::extract() const {
710 return m_Data;
711}
712
713ssize_t String::find(const char c) const {
714 if (!m_Length)
715 return -1;
716
720 ssize_t signedLength = m_Length;
721
722 char* buf = extract();
723 for (ssize_t i = 0; i < signedLength; ++i) {
724 if (buf[i] == c) {
725 return i;
726 }
727 }
728
729 return -1;
730}
731
732ssize_t String::rfind(const char c) const {
733 if (!m_Length)
734 return -1;
735
736 char* buf = extract();
737 for (ssize_t i = m_Length - 1, n = 0; i >= 0; --i, ++n) {
738 if (buf[i] == c) {
739 return n;
740 }
741 }
742
743 return -1;
744}
745
747 if (m_Length) {
748 m_Hash = spookyHash(extract(), m_Length);
749 } else {
750 m_Hash = 0;
751 }
752}
753
754uint32_t String::computeHash() const {
755 if (m_Length) {
756 return spookyHash(extract(), m_Length);
757 } else {
758 return 0;
759 }
760}
761
763 String result;
764 result.assign(*this);
765 return result;
766}
767
769 // hash already calculated, enable hashing
770 const char* buf = extract();
771 return StringView(buf, m_Length, m_Hash, true);
772}
773
774bool String::resizable() const {
775 return true;
776}
777
778bool String::assignable() const {
779 return true;
780}
781
782void String::setLength(size_t n) {
783 m_Length = n;
784}
785
786void String::setSize(size_t n) {
787 m_Size = n;
788}
Definition Cord.h:33
StringView view() const
Definition String.cc:768
void computeHash()
Definition String.cc:746
String copy() const
Definition String.cc:762
virtual bool assignable() const
Definition String.cc:778
size_t m_Length
Definition String.h:234
uint32_t hash() const
Definition String.cc:221
void lstrip()
Definition String.cc:467
void move(String &&other) noexcept
Definition String.cc:66
bool startswith(const char c) const
Definition String.cc:672
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:460
void rstrip()
Definition String.cc:492
virtual bool resizable() const
Definition String.cc:774
void setLength(size_t n)
Definition String.cc:782
bool iswhitespace(const char c) const
Definition String.cc:705
virtual char * extract() const
Definition String.cc:709
String()
Definition String.cc:32
void ltrim(size_t n)
Definition String.cc:408
void setSize(size_t n)
Definition String.cc:786
ssize_t find(const char c) const
Definition String.cc:713
size_t nextCharacter(size_t c) const
Definition String.cc:241
String split(size_t offset)
Definition String.cc:437
size_t m_Size
Definition String.h:236
void rtrim(size_t n)
Definition String.cc:423
bool endswith(const char c) const
Definition String.cc:639
char * m_Data
Definition String.h:232
static size_t Utf32ToUtf8(uint32_t utf32, char *utf8)
Definition String.cc:523
void lchomp()
Definition String.cc:599
size_t prevCharacter(size_t c) const
Definition String.cc:246
void chomp()
Definition String.cc:613
uint32_t maybeHash() const
Definition String.cc:237
uint32_t m_Hash
Definition String.h:238
void downsize()
Definition String.cc:378
A vector / dynamic array.
Definition Vector.h:33
#define assert(x)
Definition assert.h:39
size_t count() const
Definition Vector.h:270