Index: compiled/String.h |
=================================================================== |
new file mode 100644 |
--- /dev/null |
+++ b/compiled/String.h |
@@ -0,0 +1,325 @@ |
+#pragma once |
+ |
+#include <cstddef> |
+#include <cstring> |
+#include <algorithm> |
+ |
+#include "debug.h" |
+ |
+inline void String_assert_readonly(bool readOnly); |
+ |
+class String |
+{ |
+ friend class DependentString; |
+ friend class OwnedString; |
+ |
+public: |
+ typedef char16_t value_type; |
+ typedef size_t size_type; |
+ |
+ // Type flags, stored in the top 2 bits of the mLen member |
+ static constexpr size_type INVALID = 0xC0000000; |
+ static constexpr size_type DELETED = 0x80000000; |
+ static constexpr size_type READ_ONLY = 0x40000000; |
+ static constexpr size_type READ_WRITE = 0x00000000; |
+ |
+ static constexpr size_type FLAGS_MASK = 0xC0000000; |
+ static constexpr size_type LENGTH_MASK = 0x3FFFFFFF; |
+ |
+ static constexpr size_type npos = -1; |
+ |
+protected: |
+ value_type* mBuf; |
+ size_type mLen; |
+ |
+ String(value_type* buf, size_type len, size_type flags) |
+ : mBuf(buf), mLen((len & LENGTH_MASK) | flags) |
+ { |
+ } |
+ |
+ void reset(value_type* buf, size_type len, size_type flags) |
+ { |
+ mBuf = buf; |
+ mLen = (len & LENGTH_MASK) | flags; |
+ } |
+ |
sergei
2016/02/17 12:54:53
What about adding protected destructor or virtual
Wladimir Palant
2016/02/18 16:06:53
Nice one, I thought that having a protected constr
|
+public: |
+ size_type length() const |
+ { |
+ return mLen & LENGTH_MASK; |
+ } |
+ |
+ bool empty() const |
+ { |
+ return !(mLen & LENGTH_MASK); |
+ } |
+ |
+ const value_type* data() const |
+ { |
+ return mBuf; |
+ } |
+ |
+ value_type* data() |
+ { |
+ String_assert_readonly(is_readOnly()); |
+ return mBuf; |
+ } |
+ |
+ const value_type& operator[](size_type pos) const |
+ { |
+ return mBuf[pos]; |
+ } |
+ |
+ value_type& operator[](size_type pos) |
+ { |
+ String_assert_readonly(is_readOnly()); |
+ return mBuf[pos]; |
+ } |
+ |
+ bool is_readOnly() const |
+ { |
+ return (mLen & FLAGS_MASK) != READ_WRITE; |
+ } |
+ |
+ bool equals(const String& other) const |
+ { |
+ if (length() != other.length()) |
+ return false; |
+ |
+ return std::memcmp(mBuf, other.mBuf, sizeof(value_type) * length()) == 0; |
+ } |
+ |
+ size_type find(value_type c, size_type pos = 0) const |
+ { |
+ for (size_type i = pos; i < length(); ++i) |
+ if (mBuf[i] == c) |
+ return i; |
+ return npos; |
+ } |
+ |
+ size_type find(const String& str, size_type pos = 0) const |
+ { |
+ if (!str.length()) |
+ return pos; |
sergei
2016/02/17 12:54:39
Strictly speaking, if `pos > this->length()` then
Wladimir Palant
2016/02/18 16:06:58
Done.
|
+ |
+ if (length() - pos < str.length()) |
sergei
2016/02/17 12:54:42
We also should check whether `this->length()` is n
Wladimir Palant
2016/02/18 16:06:47
If length() is 0 then length() - pos will be at mo
sergei
2016/02/22 12:45:46
I wanted to have at the beginning of the method th
Wladimir Palant
2016/02/23 12:37:21
Yes, in our case an integer overflow is luckily im
sergei
2016/02/23 15:07:27
As a matter of form we still need to check that `p
Wladimir Palant
2016/02/23 21:34:54
True, done.
|
+ return npos; |
+ |
+ for (; pos < length() - str.length(); ++pos) |
+ { |
+ if (mBuf[pos] == str[0] && |
+ std::memcmp(mBuf + pos, str.mBuf, sizeof(value_type) * str.length()) == 0) |
+ { |
+ return pos; |
+ } |
+ } |
+ |
+ return npos; |
+ } |
+ |
+ size_type rfind(value_type c, size_type pos = npos) const |
+ { |
+ if (length() == 0) |
+ return npos; |
+ |
+ if (pos == npos) |
+ pos = length() - 1; |
sergei
2016/02/17 12:54:54
It seems safer to use something like `pos = min(po
Wladimir Palant
2016/02/18 16:06:46
Done.
|
+ |
+ for (int i = pos; i >= 0; --i) |
+ if (mBuf[i] == c) |
+ return i; |
+ return npos; |
+ } |
+}; |
+ |
+class DependentString : public String |
+{ |
+public: |
+ DependentString() |
+ : String(nullptr, 0, INVALID) |
+ { |
+ } |
+ |
+ DependentString(value_type* buf, size_type len) |
+ : String(buf, len, READ_WRITE) |
+ { |
+ } |
+ |
+ DependentString(const value_type* buf, size_type len) |
+ : String(const_cast<value_type*>(buf), len, READ_ONLY) |
+ { |
+ } |
+ |
+ DependentString(String& str, size_type pos = 0, size_type len = npos) |
sergei
2016/02/17 12:54:51
I would add `explicit` for this particular method.
Wladimir Palant
2016/02/18 16:06:51
Why? I think that passing OwnedString to a functio
sergei
2016/02/22 12:45:45
May be it's desired. It seems it's not actually us
Wladimir Palant
2016/02/23 12:37:26
It's being used in various getters, e.g. Filter::G
sergei
2016/02/23 15:07:26
Yes, the variant with const, but this constructor
Wladimir Palant
2016/02/23 21:34:54
bindings.h currently enforces the return value to
|
+ : String( |
+ str.mBuf + std::min(pos, str.length()), |
+ std::min(len, str.length() - std::min(pos, str.length())), |
+ READ_WRITE |
+ ) |
+ { |
+ } |
+ |
+ DependentString(const String& str, size_type pos = 0, size_type len = npos) |
+ : String( |
+ str.mBuf + std::min(pos, str.length()), |
+ std::min(len, str.length() - std::min(pos, str.length())), |
+ READ_ONLY |
+ ) |
+ { |
+ } |
+ |
+ void reset(value_type* buf, size_type len) |
+ { |
+ *this = DependentString(buf, len); |
+ } |
+ |
+ void reset(const value_type* buf, size_type len) |
+ { |
+ *this = DependentString(buf, len); |
+ } |
+ |
+ void reset(String& str, size_type pos = 0, size_type len = npos) |
+ { |
+ *this = DependentString(str, pos, len); |
+ } |
+ |
+ void reset(const String& str, size_type pos = 0, size_type len = npos) |
+ { |
+ *this = DependentString(str, pos, len); |
+ } |
+ |
+ bool is_invalid() const |
sergei
2016/02/17 12:54:52
what about moving it into base class String?
Wladimir Palant
2016/02/18 16:06:52
Given that it is only necessary for StringMap - wh
sergei
2016/02/22 12:45:49
Because flag values are defined in `String`, flag
Wladimir Palant
2016/02/23 12:37:24
Done.
|
+ { |
+ return (mLen & FLAGS_MASK) == INVALID; |
+ } |
+ |
+ bool is_deleted() const |
+ { |
+ return (mLen & FLAGS_MASK) == DELETED; |
sergei
2016/02/17 12:54:49
How can it actually happen? It seems it is never s
Wladimir Palant
2016/02/18 16:06:55
No, it's not - yet. The reason is that StringMap c
sergei
2016/02/22 12:45:50
Acknowledged.
|
+ } |
+}; |
+ |
+class OwnedString : public String |
+{ |
+private: |
+ value_type* allocate(size_type len) |
+ { |
+ if (len) |
+ return new value_type[len]; |
+ else |
+ return nullptr; |
+ } |
+ |
+ void resize(size_type newLength, bool copy) |
sergei
2016/02/17 12:54:43
I'm not sure about `copy` argument, it's called on
Wladimir Palant
2016/02/18 16:06:49
Indeed, not any more - removed that parameter.
|
+ { |
+ size_type oldLength = length(); |
+ value_type* oldBuffer = mBuf; |
+ |
+ reset(nullptr, newLength, READ_WRITE); |
+ newLength = length(); |
+ mBuf = allocate(newLength); |
+ annotate_address(mBuf, "String"); |
+ |
+ if (copy && oldLength) |
+ std::memcpy(mBuf, oldBuffer, sizeof(value_type) * std::min(oldLength, newLength)); |
sergei
2016/02/17 12:54:41
If either destination or source buffer is nullptr
Wladimir Palant
2016/02/18 16:06:57
oldLength is non-zero so oldBuffer cannot be nullp
sergei
2016/02/22 12:45:38
Acknowledged, sorry, overlooked.
|
+ if (oldBuffer) |
+ delete[] oldBuffer; |
+ } |
+ |
+public: |
+ OwnedString(size_type len = 0) |
+ : String(nullptr, len, READ_WRITE) |
+ { |
+ mBuf = allocate(length()); |
+ annotate_address(mBuf, "String"); |
+ } |
+ |
+ OwnedString(const String& str) |
+ : OwnedString(str.length()) |
+ { |
+ std::memcpy(mBuf, str.mBuf, sizeof(value_type) * length()); |
sergei
2016/02/17 12:54:40
Here both mBuf and str.mBuf can be nullptr.
Wladimir Palant
2016/02/18 16:06:54
Should be fine as length() will be 0 in that case?
sergei
2016/02/22 12:45:48
According to http://en.cppreference.com/w/cpp/stri
Wladimir Palant
2016/02/23 12:37:23
Done.
|
+ } |
+ |
+ OwnedString(const OwnedString& str) |
+ : OwnedString(static_cast<const String&>(str)) |
+ { |
+ } |
+ |
+ OwnedString(const value_type* str, size_type len) |
+ : OwnedString(DependentString(str, len)) |
+ { |
+ } |
+ |
+ OwnedString(OwnedString&& str) |
+ : OwnedString(str.length()) |
+ { |
+ mBuf = str.mBuf; |
+ str.mBuf = nullptr; |
+ str.mLen = READ_WRITE | 0; |
+ } |
+ |
+ OwnedString(const char* source, size_type len) |
+ : OwnedString(len) |
+ { |
+ for (size_type i = 0; i < len; i++) |
+ mBuf[i] = source[i]; |
+ } |
+ |
+ ~OwnedString() |
+ { |
+ if (mBuf) |
+ delete[] mBuf; |
+ } |
+ |
+ OwnedString& operator=(const String& str) |
+ { |
+ *this = std::move(OwnedString(str)); |
+ return *this; |
+ } |
+ |
+ OwnedString& operator=(const OwnedString& str) |
+ { |
+ *this = std::move(OwnedString(str)); |
+ return *this; |
+ } |
+ |
+ OwnedString& operator=(OwnedString&& str) |
+ { |
+ mBuf = str.mBuf; |
+ mLen = str.mLen; |
+ str.mBuf = nullptr; |
+ str.mLen = READ_WRITE | 0; |
+ return *this; |
+ } |
+ |
+ void append(const value_type* source, size_type sourceLen) |
+ { |
+ if (!sourceLen) |
sergei
2016/02/17 12:54:50
it would be also good to check that source is not
Wladimir Palant
2016/02/18 16:06:50
That would be a bug in the caller - meaning that w
sergei
2016/02/22 12:45:39
Good.
|
+ return; |
+ |
+ size_t oldLength = length(); |
+ resize(oldLength + sourceLen, true); |
+ std::memcpy(mBuf + oldLength, source, sizeof(value_type) * sourceLen); |
+ } |
+ |
+ void append(const String& str) |
+ { |
+ append(str.mBuf, str.length()); |
+ } |
+ |
+ void append(value_type c) |
+ { |
+ append(&c, 1); |
+ } |
+}; |
+ |
+inline DependentString operator "" _str(const String::value_type* str, |
+ String::size_type len) |
+{ |
+ return DependentString(str, len); |
+} |
+ |
+inline void String_assert_readonly(bool readOnly) |
+{ |
+ assert(!readOnly, u"Writing access to a read-only string"_str); |
+} |