import rustrt.sbuf; import _vec.rustrt.vbuf; native "rust" mod rustrt { type sbuf; fn str_buf(str s) -> sbuf; 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 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] { fn ith(str s, uint i) -> u8 { ret s.(i); } ret _vec.init_fn[u8](bind ith(s, _), byte_len(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); } // FIXME even temp-er thing; rustc can use "unsafe_from_bytes" above fn unsafe_from_mutable_bytes(vec[mutable u8] mv) -> str { let vec[u8] v = vec(); for (mutable u8 b in mv) { v += vec(b); } ret rustrt.str_from_vec(v); } fn unsafe_from_byte(u8 u) -> str { ret rustrt.str_from_vec(vec(u)); } unsafe fn str_from_cstr(sbuf cstr) -> str { ret rustrt.str_from_cstr(cstr); } unsafe 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); check(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); check(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)); check(chsize > 0u); len += 1u; i += chsize; } check(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;} check(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 { let str accum = ""; let uint i = begin; while (i < begin+len) { accum += unsafe_from_byte(s.(i)); i += 1u; } ret accum; } fn shift_byte(&mutable str s) -> u8 { auto len = byte_len(s); check(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); check(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: