aboutsummaryrefslogtreecommitdiff
path: root/src/lib/_str.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/_str.rs')
-rw-r--r--src/lib/_str.rs490
1 files changed, 490 insertions, 0 deletions
diff --git a/src/lib/_str.rs b/src/lib/_str.rs
new file mode 100644
index 00000000..36b2376d
--- /dev/null
+++ b/src/lib/_str.rs
@@ -0,0 +1,490 @@
+import rustrt::sbuf;
+
+import _vec::rustrt::vbuf;
+
+native "rust" mod rustrt {
+ type sbuf;
+ fn str_buf(str s) -> sbuf;
+ fn str_vec(str s) -> vec[u8];
+ fn str_byte_len(str s) -> uint;
+ fn str_alloc(uint n_bytes) -> str;
+ fn str_from_vec(vec[mutable? u8] b) -> str;
+ fn str_from_cstr(sbuf cstr) -> str;
+ fn str_from_buf(sbuf buf, uint len) -> str;
+ fn str_push_byte(str s, uint byte) -> str;
+ fn str_slice(str s, uint begin, uint end) -> str;
+ fn refcount[T](str s) -> uint;
+}
+
+fn eq(&str a, &str b) -> bool {
+ let uint i = byte_len(a);
+ if (byte_len(b) != i) {
+ ret false;
+ }
+ while (i > 0u) {
+ i -= 1u;
+ auto cha = a.(i);
+ auto chb = b.(i);
+ if (cha != chb) {
+ ret false;
+ }
+ }
+ ret true;
+}
+
+fn lteq(&str a, &str b) -> bool {
+ let uint i = byte_len(a);
+ let uint j = byte_len(b);
+ let uint n = i;
+ if (j < n) {
+ n = j;
+ }
+
+ let uint x = 0u;
+ while (x < n) {
+ auto cha = a.(x);
+ auto chb = b.(x);
+ if (cha < chb) {
+ ret true;
+ }
+ else if (cha > chb) {
+ ret false;
+ }
+ x += 1u;
+ }
+
+ ret i <= j;
+}
+
+
+fn hash(&str s) -> uint {
+ // djb hash.
+ // FIXME: replace with murmur.
+ let uint u = 5381u;
+ for (u8 c in s) {
+ u *= 33u;
+ u += (c as uint);
+ }
+ ret u;
+}
+
+// UTF-8 tags and ranges
+const u8 tag_cont_u8 = 0x80_u8;
+const uint tag_cont = 0x80_u;
+const uint max_one_b = 0x80_u;
+const uint tag_two_b = 0xc0_u;
+const uint max_two_b = 0x800_u;
+const uint tag_three_b = 0xe0_u;
+const uint max_three_b = 0x10000_u;
+const uint tag_four_b = 0xf0_u;
+const uint max_four_b = 0x200000_u;
+const uint tag_five_b = 0xf8_u;
+const uint max_five_b = 0x4000000_u;
+const uint tag_six_b = 0xfc_u;
+
+fn is_utf8(vec[u8] v) -> bool {
+ auto i = 0u;
+ auto total = _vec::len[u8](v);
+ while (i < total) {
+ auto chsize = utf8_char_width(v.(i));
+ if (chsize == 0u) {ret false;}
+ if (i + chsize > total) {ret false;}
+ i += 1u;
+ while (chsize > 1u) {
+ if (v.(i) & 0xc0_u8 != tag_cont_u8) {ret false;}
+ i += 1u;
+ chsize -= 1u;
+ }
+ }
+ ret true;
+}
+
+fn is_ascii(str s) -> bool {
+ let uint i = byte_len(s);
+ while (i > 0u) {
+ i -= 1u;
+ if ((s.(i) & 0x80_u8) != 0u8) {
+ ret false;
+ }
+ }
+ ret true;
+}
+
+fn alloc(uint n_bytes) -> str {
+ ret rustrt::str_alloc(n_bytes);
+}
+
+// Returns the number of bytes (a.k.a. UTF-8 code units) in s.
+// Contrast with a function that would return the number of code
+// points (char's), combining character sequences, words, etc. See
+// http://icu-project.org/apiref/icu4c/classBreakIterator.html for a
+// way to implement those.
+fn byte_len(str s) -> uint {
+ ret rustrt::str_byte_len(s);
+}
+
+fn buf(str s) -> sbuf {
+ ret rustrt::str_buf(s);
+}
+
+fn bytes(str s) -> vec[u8] {
+ ret rustrt::str_vec(s);
+}
+
+fn from_bytes(vec[u8] v) : is_utf8(v) -> str {
+ ret rustrt::str_from_vec(v);
+}
+
+// FIXME temp thing
+fn unsafe_from_bytes(vec[mutable? u8] v) -> str {
+ ret rustrt::str_from_vec(v);
+}
+
+fn unsafe_from_byte(u8 u) -> str {
+ ret rustrt::str_from_vec(vec(u));
+}
+
+fn str_from_cstr(sbuf cstr) -> str {
+ ret rustrt::str_from_cstr(cstr);
+}
+
+fn str_from_buf(sbuf buf, uint len) -> str {
+ ret rustrt::str_from_buf(buf, len);
+}
+
+fn push_utf8_bytes(&mutable str s, char ch) {
+ auto code = ch as uint;
+ if (code < max_one_b) {
+ s = rustrt::str_push_byte(s, code);
+ } else if (code < max_two_b) {
+ s = rustrt::str_push_byte(s, ((code >> 6u) & 0x1f_u) | tag_two_b);
+ s = rustrt::str_push_byte(s, (code & 0x3f_u) | tag_cont);
+ } else if (code < max_three_b) {
+ s = rustrt::str_push_byte(s, ((code >> 12u) & 0x0f_u) | tag_three_b);
+ s = rustrt::str_push_byte(s, ((code >> 6u) & 0x3f_u) | tag_cont);
+ s = rustrt::str_push_byte(s, (code & 0x3f_u) | tag_cont);
+ } else if (code < max_four_b) {
+ s = rustrt::str_push_byte(s, ((code >> 18u) & 0x07_u) | tag_four_b);
+ s = rustrt::str_push_byte(s, ((code >> 12u) & 0x3f_u) | tag_cont);
+ s = rustrt::str_push_byte(s, ((code >> 6u) & 0x3f_u) | tag_cont);
+ s = rustrt::str_push_byte(s, (code & 0x3f_u) | tag_cont);
+ } else if (code < max_five_b) {
+ s = rustrt::str_push_byte(s, ((code >> 24u) & 0x03_u) | tag_five_b);
+ s = rustrt::str_push_byte(s, ((code >> 18u) & 0x3f_u) | tag_cont);
+ s = rustrt::str_push_byte(s, ((code >> 12u) & 0x3f_u) | tag_cont);
+ s = rustrt::str_push_byte(s, ((code >> 6u) & 0x3f_u) | tag_cont);
+ s = rustrt::str_push_byte(s, (code & 0x3f_u) | tag_cont);
+ } else {
+ s = rustrt::str_push_byte(s, ((code >> 30u) & 0x01_u) | tag_six_b);
+ s = rustrt::str_push_byte(s, ((code >> 24u) & 0x3f_u) | tag_cont);
+ s = rustrt::str_push_byte(s, ((code >> 18u) & 0x3f_u) | tag_cont);
+ s = rustrt::str_push_byte(s, ((code >> 12u) & 0x3f_u) | tag_cont);
+ s = rustrt::str_push_byte(s, ((code >> 6u) & 0x3f_u) | tag_cont);
+ s = rustrt::str_push_byte(s, (code & 0x3f_u) | tag_cont);
+ }
+}
+
+fn from_char(char ch) -> str {
+ auto buf = "";
+ push_utf8_bytes(buf, ch);
+ ret buf;
+}
+
+fn from_chars(vec[char] chs) -> str {
+ auto buf = "";
+ for (char ch in chs) {push_utf8_bytes(buf, ch);}
+ ret buf;
+}
+
+fn utf8_char_width(u8 b) -> uint {
+ let uint byte = b as uint;
+ if (byte < 0x80_u) {ret 1u;}
+ if (byte < 0xc0_u) {ret 0u;} // Not a valid start byte
+ if (byte < 0xe0_u) {ret 2u;}
+ if (byte < 0xf0_u) {ret 3u;}
+ if (byte < 0xf8_u) {ret 4u;}
+ if (byte < 0xfc_u) {ret 5u;}
+ ret 6u;
+}
+
+fn char_range_at(str s, uint i) -> tup(char, uint) {
+ auto b0 = s.(i);
+ auto w = utf8_char_width(b0);
+ assert (w != 0u);
+ if (w == 1u) {ret tup(b0 as char, i + 1u);}
+ auto val = 0u;
+ auto end = i + w;
+ i += 1u;
+ while (i < end) {
+ auto byte = s.(i);
+ assert (byte & 0xc0_u8 == tag_cont_u8);
+ val <<= 6u;
+ val += (byte & 0x3f_u8) as uint;
+ i += 1u;
+ }
+ // Clunky way to get the right bits from the first byte. Uses two shifts,
+ // the first to clip off the marker bits at the left of the byte, and then
+ // a second (as uint) to get it to the right position.
+ val += ((b0 << ((w + 1u) as u8)) as uint) << ((w - 1u) * 6u - w - 1u);
+ ret tup(val as char, i);
+}
+
+fn char_at(str s, uint i) -> char {
+ ret char_range_at(s, i)._0;
+}
+
+fn char_len(str s) -> uint {
+ auto i = 0u;
+ auto len = 0u;
+ auto total = byte_len(s);
+ while (i < total) {
+ auto chsize = utf8_char_width(s.(i));
+ assert (chsize > 0u);
+ len += 1u;
+ i += chsize;
+ }
+ assert (i == total);
+ ret len;
+}
+
+fn to_chars(str s) -> vec[char] {
+ let vec[char] buf = vec();
+ auto i = 0u;
+ auto len = byte_len(s);
+ while (i < len) {
+ auto cur = char_range_at(s, i);
+ _vec::push[char](buf, cur._0);
+ i = cur._1;
+ }
+ ret buf;
+}
+
+fn push_char(&mutable str s, char ch) {
+ s += from_char(ch);
+}
+
+fn pop_char(&mutable str s) -> char {
+ auto end = byte_len(s);
+ while (end > 0u && s.(end - 1u) & 0xc0_u8 == tag_cont_u8) {end -= 1u;}
+ assert (end > 0u);
+ auto ch = char_at(s, end - 1u);
+ s = substr(s, 0u, end - 1u);
+ ret ch;
+}
+
+fn shift_char(&mutable str s) -> char {
+ auto r = char_range_at(s, 0u);
+ s = substr(s, r._1, byte_len(s) - r._1);
+ ret r._0;
+}
+
+fn unshift_char(&mutable str s, char ch) {
+ // Workaround for rustboot order-of-evaluation issue -- if I put s
+ // directly after the +, the string ends up containing (only) the
+ // character, twice.
+ auto x = s;
+ s = from_char(ch) + x;
+}
+
+fn refcount(str s) -> uint {
+ auto r = rustrt::refcount[u8](s);
+ if (r == dbg::const_refcount) {
+ ret r;
+ } else {
+ // -1 because calling this function incremented the refcount.
+ ret r - 1u;
+ }
+}
+
+
+// Standard bits from the world of string libraries.
+
+fn index(str s, u8 c) -> int {
+ let int i = 0;
+ for (u8 k in s) {
+ if (k == c) {
+ ret i;
+ }
+ i += 1;
+ }
+ ret -1;
+}
+
+fn rindex(str s, u8 c) -> int {
+ let int n = _str::byte_len(s) as int;
+ while (n >= 0) {
+ if (s.(n) == c) {
+ ret n;
+ }
+ n -= 1;
+ }
+ ret n;
+}
+
+fn find(str haystack, str needle) -> int {
+
+ let int haystack_len = byte_len(haystack) as int;
+ let int needle_len = byte_len(needle) as int;
+
+ if (needle_len == 0) {
+ ret 0;
+ }
+
+ fn match_at(&str haystack,
+ &str needle,
+ int i) -> bool {
+ let int j = i;
+ for (u8 c in needle) {
+ if (haystack.(j) != c) {
+ ret false;
+ }
+ j += 1;
+ }
+ ret true;
+ }
+
+ let int i = 0;
+ while (i <= haystack_len - needle_len) {
+ if (match_at(haystack, needle, i)) {
+ ret i;
+ }
+ i += 1;
+ }
+ ret -1;
+}
+
+fn starts_with(str haystack, str needle) -> bool {
+ let uint haystack_len = byte_len(haystack);
+ let uint needle_len = byte_len(needle);
+ if (needle_len == 0u) {
+ ret true;
+ }
+ if (needle_len > haystack_len) {
+ ret false;
+ }
+ ret eq(substr(haystack, 0u, needle_len), needle);
+}
+
+fn ends_with(str haystack, str needle) -> bool {
+ let uint haystack_len = byte_len(haystack);
+ let uint needle_len = byte_len(needle);
+ if (needle_len == 0u) {
+ ret true;
+ }
+ if (needle_len > haystack_len) {
+ ret false;
+ }
+ ret eq(substr(haystack,
+ haystack_len - needle_len,
+ needle_len),
+ needle);
+}
+
+fn substr(str s, uint begin, uint len) -> str {
+ ret slice(s, begin, begin + len);
+}
+
+fn slice(str s, uint begin, uint end) -> str {
+ // FIXME: Typestate precondition
+ assert (begin <= end);
+ assert (end <= _str::byte_len(s));
+ ret rustrt::str_slice(s, begin, end);
+}
+
+fn shift_byte(&mutable str s) -> u8 {
+ auto len = byte_len(s);
+ assert (len > 0u);
+ auto b = s.(0);
+ s = substr(s, 1u, len - 1u);
+ ret b;
+}
+
+fn pop_byte(&mutable str s) -> u8 {
+ auto len = byte_len(s);
+ assert (len > 0u);
+ auto b = s.(len - 1u);
+ s = substr(s, 0u, len - 1u);
+ ret b;
+}
+
+fn push_byte(&mutable str s, u8 b) {
+ s = rustrt::str_push_byte(s, b as uint);
+}
+
+fn unshift_byte(&mutable str s, u8 b) {
+ auto res = alloc(byte_len(s) + 1u);
+ res += unsafe_from_byte(b);
+ res += s;
+ s = res;
+}
+
+fn split(str s, u8 sep) -> vec[str] {
+ let vec[str] v = vec();
+ let str accum = "";
+ let bool ends_with_sep = false;
+ for (u8 c in s) {
+ if (c == sep) {
+ v += vec(accum);
+ accum = "";
+ ends_with_sep = true;
+ } else {
+ accum += unsafe_from_byte(c);
+ ends_with_sep = false;
+ }
+ }
+ if (_str::byte_len(accum) != 0u ||
+ ends_with_sep) {
+ v += vec(accum);
+ }
+ ret v;
+}
+
+fn concat(vec[str] v) -> str {
+ let str s = "";
+ for (str ss in v) {
+ s += ss;
+ }
+ ret s;
+}
+
+fn connect(vec[str] v, str sep) -> str {
+ let str s = "";
+ let bool first = true;
+ for (str ss in v) {
+ if (first) {
+ first = false;
+ } else {
+ s += sep;
+ }
+ s += ss;
+ }
+ ret s;
+}
+
+// FIXME: This only handles ASCII
+fn to_upper(str s) -> str {
+ auto outstr = "";
+ auto ascii_a = 'a' as u8;
+ auto ascii_z = 'z' as u8;
+ auto diff = 32u8;
+ for (u8 byte in s) {
+ auto next;
+ if (ascii_a <= byte && byte <= ascii_z) {
+ next = byte - diff;
+ } else {
+ next = byte;
+ }
+ push_byte(outstr, next);
+ }
+ ret outstr;
+}
+
+
+// Local Variables:
+// mode: rust;
+// fill-column: 78;
+// indent-tabs-mode: nil
+// c-basic-offset: 4
+// buffer-file-coding-system: utf-8-unix
+// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
+// End: