mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
move all vlib modules to vlib/
This commit is contained in:
227
vlib/builtin/array.v
Normal file
227
vlib/builtin/array.v
Normal file
@@ -0,0 +1,227 @@
|
||||
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
module builtin
|
||||
|
||||
struct array {
|
||||
// Using a void pointer allows to implement arrays without generics and without generating
|
||||
// extra code for every type.
|
||||
pub:
|
||||
data voidptr
|
||||
len int
|
||||
cap int
|
||||
element_size int
|
||||
}
|
||||
|
||||
// Private function, used by V (`nums := []int`)
|
||||
fn new_array(mylen, cap, elm_size int) array {
|
||||
arr := array {
|
||||
len: mylen
|
||||
cap: cap
|
||||
element_size: elm_size
|
||||
data: malloc(cap * elm_size)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// Private function, used by V (`nums := [1, 2, 3]`)
|
||||
fn new_array_from_c_array(len, cap, elm_size int, c_array voidptr) array {
|
||||
arr := array {
|
||||
len: len
|
||||
cap: cap
|
||||
element_size: elm_size
|
||||
data: malloc(cap * elm_size)
|
||||
}
|
||||
// TODO Write all memory functions (like memcpy) in V
|
||||
C.memcpy(arr.data, c_array, len * elm_size)
|
||||
return arr
|
||||
}
|
||||
|
||||
// Private function, used by V (`nums := [1, 2, 3] !`)
|
||||
fn new_array_from_c_array_no_alloc(len, cap, elm_size int, c_array voidptr) array {
|
||||
arr := array {
|
||||
len: len
|
||||
cap: cap
|
||||
element_size: elm_size
|
||||
data: c_array
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// Private function, used by V (`[0; 100]`)
|
||||
fn array_repeat(val voidptr, nr_repeats, elm_size int) array {
|
||||
arr := array {
|
||||
len: nr_repeats
|
||||
cap: nr_repeats
|
||||
element_size: elm_size
|
||||
data: malloc(nr_repeats * elm_size)
|
||||
}
|
||||
for i := 0; i < nr_repeats; i++ {
|
||||
C.memcpy(arr.data + i * elm_size, val, elm_size)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
pub fn (a mut array) append_array(b array) {
|
||||
for i := 0; i < b.len; i++ {
|
||||
val := b[i]
|
||||
a._push(val)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (a mut array) sort_with_compare(compare voidptr) {
|
||||
C.qsort(a.data, a.len, a.element_size, compare)
|
||||
}
|
||||
|
||||
pub fn (a mut array) insert(i int, val voidptr) {
|
||||
if i >= a.len {
|
||||
panic('array.insert: index larger than length')
|
||||
return
|
||||
}
|
||||
a._push(val)
|
||||
size := a.element_size
|
||||
C.memmove(a.data + (i + 1) * size, a.data + i * size, (a.len - i) * size)
|
||||
a.set(i, val)
|
||||
}
|
||||
|
||||
pub fn (a mut array) prepend(val voidptr) {
|
||||
a.insert(0, val)
|
||||
}
|
||||
|
||||
pub fn (a mut array) delete(idx int) {
|
||||
size := a.element_size
|
||||
C.memmove(a.data + idx * size, a.data + (idx + 1) * size, (a.len - idx) * size)
|
||||
a.len--
|
||||
a.cap--
|
||||
}
|
||||
|
||||
fn (a array) _get(i int) voidptr {
|
||||
if i < 0 || i >= a.len {
|
||||
panic('array index out of range: $i/$a.len')
|
||||
}
|
||||
return a.data + i * a.element_size
|
||||
}
|
||||
|
||||
pub fn (a array) first() voidptr {
|
||||
if a.len == 0 {
|
||||
panic('array.first: empty array')
|
||||
}
|
||||
return a.data + 0
|
||||
}
|
||||
|
||||
pub fn (a array) last() voidptr {
|
||||
if a.len == 0 {
|
||||
panic('array.last: empty array')
|
||||
}
|
||||
return a.data + (a.len - 1) * a.element_size
|
||||
}
|
||||
|
||||
pub fn (s array) left(n int) array {
|
||||
if n >= s.len {
|
||||
return s
|
||||
}
|
||||
return s.slice(0, n)
|
||||
}
|
||||
|
||||
pub fn (s array) right(n int) array {
|
||||
if n >= s.len {
|
||||
return s
|
||||
}
|
||||
return s.slice(n, s.len)
|
||||
}
|
||||
|
||||
pub fn (s array) slice(start, _end int) array {
|
||||
mut end := _end
|
||||
if start > end {
|
||||
panic('invalid slice index: $start > $end')
|
||||
}
|
||||
if end >= s.len {
|
||||
end = s.len
|
||||
}
|
||||
l := end - start
|
||||
res := array {
|
||||
element_size: s.element_size
|
||||
data: s.data + start * s.element_size
|
||||
len: l
|
||||
cap: l
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn (a mut array) set(idx int, val voidptr) {
|
||||
if idx < 0 || idx >= a.len {
|
||||
panic('array index out of range: $idx / $a.len')
|
||||
}
|
||||
C.memcpy(a.data + a.element_size * idx, val, a.element_size)
|
||||
}
|
||||
|
||||
pub fn (arr mut array) _push(val voidptr) {
|
||||
if arr.len >= arr.cap - 1 {
|
||||
cap := (arr.len + 1) * 2
|
||||
// println('_push: realloc, new cap=$cap')
|
||||
if arr.cap == 0 {
|
||||
arr.data = malloc(cap * arr.element_size)
|
||||
}
|
||||
else {
|
||||
arr.data = C.realloc(arr.data, cap * arr.element_size)
|
||||
}
|
||||
arr.cap = cap
|
||||
}
|
||||
C.memcpy(arr.data + arr.element_size * arr.len, val, arr.element_size)
|
||||
arr.len++
|
||||
}
|
||||
|
||||
pub fn (arr mut array) _push_many(val voidptr, size int) {
|
||||
if arr.len >= arr.cap - size {
|
||||
cap := (arr.len + size) * 2
|
||||
// println('_push: realloc, new cap=$cap')
|
||||
if arr.cap == 0 {
|
||||
arr.data = malloc(cap * arr.element_size)
|
||||
}
|
||||
else {
|
||||
arr.data = C.realloc(arr.data, cap * arr.element_size)
|
||||
}
|
||||
arr.cap = cap
|
||||
}
|
||||
C.memcpy(arr.data + arr.element_size * arr.len, val, arr.element_size * size)
|
||||
arr.len += size
|
||||
}
|
||||
|
||||
pub fn (a[]int) str() string {
|
||||
mut res := '['
|
||||
for i := 0; i < a.len; i++ {
|
||||
val := a[i]
|
||||
res += '$val'
|
||||
if i < a.len - 1 {
|
||||
res += ', '
|
||||
}
|
||||
}
|
||||
res += ']'
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn (a[]int) free() {
|
||||
// println('array free')
|
||||
C.free(a.data)
|
||||
}
|
||||
|
||||
// TODO generic
|
||||
// "[ 'a', 'b', 'c' ]"
|
||||
pub fn (a[]string) str() string {
|
||||
mut res := '['
|
||||
for i := 0; i < a.len; i++ {
|
||||
val := a[i]
|
||||
res += '"$val"'
|
||||
if i < a.len - 1 {
|
||||
res += ', '
|
||||
}
|
||||
}
|
||||
res += ']'
|
||||
return res
|
||||
}
|
||||
|
||||
fn free(a voidptr) {
|
||||
C.free(a)
|
||||
}
|
||||
|
||||
138
vlib/builtin/array_test.v
Normal file
138
vlib/builtin/array_test.v
Normal file
@@ -0,0 +1,138 @@
|
||||
import os
|
||||
|
||||
const (
|
||||
q = [1, 2, 3]
|
||||
A = 8
|
||||
)
|
||||
|
||||
fn test_ints() {
|
||||
mut a := [1, 5, 2, 3]
|
||||
assert a.len == 4
|
||||
assert a[0] == 1
|
||||
assert a[2] == 2
|
||||
assert a.last() == 3
|
||||
println(a)
|
||||
a << 4
|
||||
println(a)
|
||||
assert a.len == 5
|
||||
assert a[4] == 4
|
||||
assert a.last() == 4
|
||||
// assert a.contains(4) == true
|
||||
// assert a.contains(777) == false
|
||||
// assert a.str() == '51234'
|
||||
mut s := a.str()
|
||||
assert s == '[1, 5, 2, 3, 4]'
|
||||
// Insert
|
||||
// val := 5
|
||||
// a.insert(1, val)// 1 5 2 3 4
|
||||
s = a.str()
|
||||
assert a[1] == 5
|
||||
assert a.last() == 4
|
||||
// a.sort()
|
||||
s = a.str()
|
||||
assert s == '[1, 5, 2, 3, 4]'
|
||||
// Delete
|
||||
a.delete(0)
|
||||
assert a.str() == '[5, 2, 3, 4]'
|
||||
a.delete(1)
|
||||
assert a.str() == '[5, 3, 4]'
|
||||
}
|
||||
|
||||
fn test_short() {
|
||||
a := [1, 2, 3]
|
||||
assert a.len == 3
|
||||
assert a.cap == 3
|
||||
assert a[0] == 1
|
||||
assert a[1] == 2
|
||||
assert a[2] == 3
|
||||
}
|
||||
|
||||
fn test_large() {
|
||||
mut a := [0; 0]
|
||||
for i := 0; i < 10000; i++ {
|
||||
a << i
|
||||
}
|
||||
assert a.len == 10000
|
||||
assert a[234] == 234
|
||||
}
|
||||
|
||||
struct Chunk {
|
||||
val string
|
||||
}
|
||||
|
||||
struct K {
|
||||
q []Chunk
|
||||
}
|
||||
|
||||
fn test_empty() {
|
||||
mut chunks := []Chunk{}
|
||||
a := Chunk{}
|
||||
assert chunks.len == 0
|
||||
chunks << a
|
||||
assert chunks.len == 1
|
||||
chunks = []Chunk{}
|
||||
assert chunks.len == 0
|
||||
chunks << a
|
||||
assert chunks.len == 1
|
||||
}
|
||||
fn test_push() {
|
||||
mut a := []int
|
||||
a << 1
|
||||
a << 3
|
||||
assert a[1] == 3
|
||||
assert a.str() == '[1, 3]'
|
||||
}
|
||||
|
||||
fn test_strings() {
|
||||
s := 'hi'
|
||||
if s.contains('i') {
|
||||
// println('$s')
|
||||
}
|
||||
a := ['a', 'b', 'c']
|
||||
assert a.str() == '["a", "b", "c"]'
|
||||
// println(a)
|
||||
}
|
||||
|
||||
fn test_repeat() {
|
||||
a := [0; 5]
|
||||
// a := [0 x 5]
|
||||
assert a.len == 5
|
||||
assert a[0] == 0 && a[1] == 0 && a[2] == 0 && a[3] == 0 && a[4] == 0
|
||||
}
|
||||
|
||||
fn test_right() {
|
||||
a := [1, 2, 3, 4]
|
||||
b := a.right(1)
|
||||
assert b[0] == 2
|
||||
assert b[1] == 3
|
||||
}
|
||||
|
||||
fn test_left() {
|
||||
a := [1, 2, 3]
|
||||
b := a.left(2)
|
||||
assert b[0] == 1
|
||||
assert b[1] == 2
|
||||
}
|
||||
|
||||
fn test_slice() {
|
||||
a := [1, 2, 3, 4]
|
||||
b := a.slice(2, 4)
|
||||
for val in b {
|
||||
println(val)
|
||||
}
|
||||
assert a.slice(1, 2).len == 1
|
||||
println(a.slice(2, 4))
|
||||
}
|
||||
|
||||
fn test_push_many() {
|
||||
mut a := [1, 2, 3]
|
||||
b := [4, 5, 6]
|
||||
a._push_many(b.data, b.len)
|
||||
assert a.len == 6
|
||||
assert a[0] == 1
|
||||
assert a[3] == 4
|
||||
assert a[5] == 6
|
||||
println(a)
|
||||
}
|
||||
|
||||
|
||||
116
vlib/builtin/builtin.v
Normal file
116
vlib/builtin/builtin.v
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
module builtin
|
||||
|
||||
pub fn exit(code int) {
|
||||
C.exit(code)
|
||||
}
|
||||
|
||||
// isnil returns true if an object is nil (only for C objects).
|
||||
pub fn isnil(v voidptr) bool {
|
||||
return v == 0
|
||||
}
|
||||
|
||||
fn on_panic(f fn (int) int) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
pub fn print_backtrace() {
|
||||
return
|
||||
$if mac {
|
||||
buffer := [100]voidptr
|
||||
nr_ptrs := C.backtrace(buffer, 100)
|
||||
C.backtrace_symbols_fd(buffer, nr_ptrs, 1)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn panic(s string) {
|
||||
println('V panic: $s')
|
||||
print_backtrace()
|
||||
C.exit(1)
|
||||
}
|
||||
|
||||
pub fn println(s string) {
|
||||
// Should never happen
|
||||
if isnil(s.str) {
|
||||
panic('println(NIL)')
|
||||
}
|
||||
C.printf('%.*s\n', s.len, s.str)
|
||||
}
|
||||
|
||||
pub fn eprintln(s string) {
|
||||
if isnil(s.str) {
|
||||
panic('eprintln(NIL)')
|
||||
}
|
||||
$if mac {
|
||||
C.fprintf(stderr, '%.*s\n', s.len, s.str)
|
||||
}
|
||||
// TODO issues with stderr and cross compiling for Linux
|
||||
$else {
|
||||
println(s)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(s string) {
|
||||
C.printf('%.*s', s.len, s.str)
|
||||
}
|
||||
|
||||
__global total_m i64 = 0
|
||||
pub fn malloc(n int) byteptr {
|
||||
if n < 0 {
|
||||
panic('malloc(<0)')
|
||||
}
|
||||
/*
|
||||
TODO
|
||||
#ifdef VPLAY
|
||||
if n > 10000 {
|
||||
panic('allocating more than 10 KB is not allowed in the playground')
|
||||
}
|
||||
#endif
|
||||
#ifdef DEBUG_ALLOC
|
||||
total_m += n
|
||||
println('\n\n\nmalloc($n) total=$total_m')
|
||||
print_backtrace()
|
||||
#endif
|
||||
*/
|
||||
ptr := C.malloc(n)
|
||||
if isnil(ptr) {
|
||||
panic('malloc($n) failed')
|
||||
}
|
||||
return ptr
|
||||
}
|
||||
|
||||
pub fn calloc(n int) byteptr {
|
||||
if n < 0 {
|
||||
panic('calloc(<0)')
|
||||
}
|
||||
return C.calloc(n, 1)
|
||||
}
|
||||
|
||||
fn _strlen(s byteptr) int {
|
||||
// TODO don't call a C function, implement it in V
|
||||
return C.strlen(s)
|
||||
}
|
||||
|
||||
// `fn foo() ?Foo { return foo }` => `fn foo() ?Foo { return opt_ok(foo); }`
|
||||
fn opt_ok(data voidptr) Option {
|
||||
return Option {
|
||||
data: data
|
||||
ok: true
|
||||
}
|
||||
}
|
||||
|
||||
fn memdup(src voidptr, sz int) voidptr {
|
||||
mem := malloc(sz)
|
||||
return C.memcpy(mem, src, sz)
|
||||
}
|
||||
|
||||
pub fn error(s string) Option {
|
||||
return Option {
|
||||
error: s
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
161
vlib/builtin/int.v
Normal file
161
vlib/builtin/int.v
Normal file
@@ -0,0 +1,161 @@
|
||||
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
module builtin
|
||||
|
||||
pub fn (d double) str() string {
|
||||
buf := malloc(sizeof(double) * 5 + 1)// TODO
|
||||
C.sprintf(buf, '%f', d)
|
||||
return tos(buf, _strlen(buf))
|
||||
}
|
||||
|
||||
pub fn (d f64) str() string {
|
||||
buf := malloc(sizeof(double) * 5 + 1)// TODO
|
||||
C.sprintf(buf, '%f', d)
|
||||
return tos(buf, _strlen(buf))
|
||||
}
|
||||
|
||||
pub fn (d f32) str() string {
|
||||
buf := malloc(sizeof(double) * 5 + 1)// TODO
|
||||
C.sprintf(buf, '%f', d)
|
||||
return tos(buf, _strlen(buf))
|
||||
}
|
||||
|
||||
pub fn ptr_str(ptr voidptr) string {
|
||||
buf := malloc(sizeof(double) * 5 + 1)// TODO
|
||||
C.sprintf(buf, '%p', ptr)
|
||||
return tos(buf, _strlen(buf))
|
||||
}
|
||||
|
||||
// fn (nn i32) str() string {
|
||||
// return i
|
||||
// }
|
||||
pub fn (nn int) str() string {
|
||||
mut n := nn
|
||||
if n == 0 {
|
||||
return '0'
|
||||
}
|
||||
max := 16
|
||||
mut buf := malloc(max)
|
||||
mut len := 0
|
||||
mut is_neg := false
|
||||
if n < 0 {
|
||||
n = -n
|
||||
is_neg = true
|
||||
}
|
||||
// Fill the string from the end
|
||||
for n > 0 {
|
||||
d := n % 10
|
||||
buf[max - len - 1] = d + int(`0`)
|
||||
len++
|
||||
n = n / 10
|
||||
}
|
||||
// Prepend - if it's negative
|
||||
if is_neg {
|
||||
buf[max - len - 1] = `-`
|
||||
len++
|
||||
}
|
||||
return tos(buf + max - len, len)
|
||||
}
|
||||
|
||||
pub fn (nn u8) str() string {
|
||||
mut n := nn
|
||||
if n == u8(0) {
|
||||
return '0'
|
||||
}
|
||||
max := 5
|
||||
mut buf := malloc(max)
|
||||
mut len := 0
|
||||
mut is_neg := false
|
||||
if n < u8(0) {
|
||||
n = -n
|
||||
is_neg = true
|
||||
}
|
||||
// Fill the string from the end
|
||||
for n > u8(0) {
|
||||
d := n % u8(10)
|
||||
buf[max - len - 1] = d + u8(`0`)
|
||||
len++
|
||||
n = n / u8(10)
|
||||
}
|
||||
// Prepend - if it's negative
|
||||
if is_neg {
|
||||
buf[max - len - 1] = `-`
|
||||
len++
|
||||
}
|
||||
return tos(buf + max - len, len)
|
||||
}
|
||||
|
||||
pub fn (nn i64) str() string {
|
||||
mut n := nn
|
||||
if n == i64(0) {
|
||||
return '0'
|
||||
}
|
||||
max := 32
|
||||
mut buf := malloc(max)
|
||||
mut len := 0
|
||||
mut is_neg := false
|
||||
if n < i64(0) {
|
||||
n = -n
|
||||
is_neg = true
|
||||
}
|
||||
// Fill the string from the end
|
||||
for n > i64(0) {
|
||||
d := int(n % i64(10))
|
||||
buf[max - len - 1] = d + int(`0`)
|
||||
len++
|
||||
n = n / i64(10)
|
||||
}
|
||||
// Prepend - if it's negative
|
||||
if is_neg {
|
||||
buf[max - len - 1] = `-`
|
||||
len++
|
||||
}
|
||||
return tos(buf + max - len, len)
|
||||
}
|
||||
|
||||
pub fn (b bool) str() string {
|
||||
if b {
|
||||
return 'true'
|
||||
}
|
||||
return 'false'
|
||||
}
|
||||
|
||||
pub fn (n int) hex() string {
|
||||
s := n.str()
|
||||
hex := malloc(s.len + 3) // 0x + \n
|
||||
C.sprintf(hex, '0x%x', n)
|
||||
return tos(hex, s.len + 3)
|
||||
}
|
||||
|
||||
pub fn (n i64) hex() string {
|
||||
s := n.str()
|
||||
hex := malloc(s.len + 3)
|
||||
C.sprintf(hex, '0x%x', n)
|
||||
return tos(hex, s.len + 3)
|
||||
}
|
||||
|
||||
pub fn (a[]byte) contains(val byte) bool {
|
||||
for aa in a {
|
||||
if aa == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/* TODO
|
||||
fn (c rune) str() string {
|
||||
}
|
||||
*/
|
||||
pub fn (c byte) str() string {
|
||||
mut str := string {
|
||||
len: 1
|
||||
str: malloc(2)
|
||||
}
|
||||
str.str[0] = c
|
||||
str.str[1] = `\0`
|
||||
return str
|
||||
}
|
||||
|
||||
181
vlib/builtin/map.v
Normal file
181
vlib/builtin/map.v
Normal file
@@ -0,0 +1,181 @@
|
||||
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
module builtin
|
||||
|
||||
struct map {
|
||||
// cap int
|
||||
// keys []string
|
||||
// table byteptr
|
||||
// keys_table *string
|
||||
// table *Entry
|
||||
element_size int
|
||||
// collisions []Entry
|
||||
pub:
|
||||
entries []Entry
|
||||
is_sorted bool
|
||||
}
|
||||
|
||||
struct Entry {
|
||||
pub:
|
||||
key string
|
||||
val voidptr
|
||||
// linked list for collisions
|
||||
// next *Entry
|
||||
}
|
||||
|
||||
pub fn new_map(cap, elm_size int) map {
|
||||
res := map {
|
||||
// len: len,
|
||||
element_size: elm_size
|
||||
// entries:
|
||||
// keys: []string
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
fn (m &map) new_entry(key string, val voidptr) Entry {
|
||||
new_e := Entry {
|
||||
key: key
|
||||
val: malloc(m.element_size)
|
||||
// next: 0
|
||||
}
|
||||
C.memcpy(new_e.val, val, m.element_size)
|
||||
return new_e
|
||||
}
|
||||
|
||||
fn (m mut map) _set(key string, val voidptr) {
|
||||
e := m.new_entry(key, val)
|
||||
for i := 0; i < m.entries.len; i++ {
|
||||
entry := m.entries[i]
|
||||
if entry.key == key {
|
||||
// e := Entry2{key: key, val: val}
|
||||
m.entries[i] = e
|
||||
return
|
||||
}
|
||||
}
|
||||
m.entries << e// m.new_entry(key, val)
|
||||
m.is_sorted = false
|
||||
}
|
||||
|
||||
fn volt_abs(n int) int {
|
||||
// println('volt_abs($n)')
|
||||
if n < 0 {
|
||||
// println('< 0: -($n)')
|
||||
return -n
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
fn (m map) bs(query string, start, end int, out voidptr) {
|
||||
// println('bs "$query" $start -> $end')
|
||||
mid := start + ((end - start) / 2)
|
||||
if end - start == 0 {
|
||||
last := m.entries[end]
|
||||
C.memcpy(out, last.val, m.element_size)
|
||||
return
|
||||
}
|
||||
if end - start == 1 {
|
||||
first := m.entries[start]
|
||||
C.memcpy(out, first.val, m.element_size)
|
||||
return
|
||||
}
|
||||
if mid >= m.entries.len {
|
||||
return
|
||||
}
|
||||
mid_msg := m.entries[mid]
|
||||
// println('mid.key=$mid_msg.key')
|
||||
if query < mid_msg.key {
|
||||
m.bs(query, start, mid, out)
|
||||
return
|
||||
}
|
||||
m.bs(query, mid, end, out)
|
||||
}
|
||||
|
||||
fn compare_map(a, b *Entry) int {
|
||||
if a.key < b.key {
|
||||
return -1
|
||||
}
|
||||
if a.key > b.key {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
pub fn (m mut map) sort() {
|
||||
m.entries.sort_with_compare(compare_map)
|
||||
m.is_sorted = true
|
||||
}
|
||||
|
||||
pub fn (m map) keys() []string {
|
||||
mut keys := []string{}
|
||||
for i := 0; i < m.entries.len; i++ {
|
||||
entry := m.entries[i]
|
||||
keys << entry.key
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
fn (m map) get(key string, out voidptr) bool {
|
||||
if m.is_sorted {
|
||||
// println('\n\nget "$key" sorted')
|
||||
m.bs(key, 0, m.entries.len, out)
|
||||
return true
|
||||
}
|
||||
for i := 0; i < m.entries.len; i++ {
|
||||
entry := m.entries[i]
|
||||
if entry.key == key {
|
||||
C.memcpy(out, entry.val, m.element_size)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
pub fn (m map) exists(key string) bool {
|
||||
for i := 0; i < m.entries.len; i++ {
|
||||
entry := m.entries[i]
|
||||
if entry.key == key {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
pub fn (m map) print() {
|
||||
println('<<<<<<<<')
|
||||
for i := 0; i < m.entries.len; i++ {
|
||||
// entry := m.entries[i]
|
||||
// println('$entry.key => $entry.val')
|
||||
}
|
||||
/*
|
||||
for i := 0; i < m.cap * m.element_size; i++ {
|
||||
b := m.table[i]
|
||||
print('$i: ')
|
||||
C.printf('%02x', b)
|
||||
println('')
|
||||
}
|
||||
*/
|
||||
println('>>>>>>>>>>')
|
||||
}
|
||||
|
||||
pub fn (m map) free() {
|
||||
// C.free(m.table)
|
||||
// C.free(m.keys_table)
|
||||
}
|
||||
|
||||
pub fn (m map_string) str() string {
|
||||
// return 'not impl'
|
||||
if m.entries.len == 0 {
|
||||
return '{}'
|
||||
}
|
||||
// TODO use bytes buffer
|
||||
mut s := '{\n'
|
||||
for entry in m.entries {
|
||||
val := m[entry.key]
|
||||
s += ' "$entry.key" => "$val"\n'
|
||||
}
|
||||
s += '}'
|
||||
return s
|
||||
}
|
||||
12
vlib/builtin/option.v
Normal file
12
vlib/builtin/option.v
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
module builtin
|
||||
|
||||
struct Option {
|
||||
data voidptr
|
||||
error string
|
||||
ok bool
|
||||
}
|
||||
|
||||
840
vlib/builtin/string.v
Normal file
840
vlib/builtin/string.v
Normal file
@@ -0,0 +1,840 @@
|
||||
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
module builtin
|
||||
|
||||
// V strings are not null-terminated.
|
||||
struct string {
|
||||
pub:
|
||||
str byteptr
|
||||
len int
|
||||
}
|
||||
|
||||
struct ustring {
|
||||
pub:
|
||||
s string
|
||||
runes []int
|
||||
len int
|
||||
}
|
||||
|
||||
// For C strings only
|
||||
fn C.strlen(s byteptr) int
|
||||
|
||||
fn todo() { }
|
||||
|
||||
// Converts a C string to a V string
|
||||
pub fn tos(s byteptr, len int) string {
|
||||
// This should never happen.
|
||||
if isnil(s) {
|
||||
panic('tos(): nil string')
|
||||
}
|
||||
return string {
|
||||
str: s
|
||||
len: len
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tos_clone(s byteptr) string {
|
||||
if isnil(s) {
|
||||
panic('tos: nil string')
|
||||
return string{}
|
||||
}
|
||||
len := strlen(s)
|
||||
res := tos(s, len)
|
||||
return res.clone()
|
||||
}
|
||||
|
||||
// Same as `tos`, but calculates the length. Called by `string(bytes)` casts.
|
||||
fn tos2(s byteptr) string {
|
||||
if isnil(s) {
|
||||
panic('tos2: nil string')
|
||||
return string{}
|
||||
}
|
||||
len := C.strlen(s)
|
||||
res := tos(s, len)
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn (a string) clone() string {
|
||||
mut b := string {
|
||||
len: a.len
|
||||
str: malloc(a.len + 1)
|
||||
}
|
||||
for i := 0; i < a.len; i++ {
|
||||
b[i] = a[i]
|
||||
}
|
||||
b[a.len] = `\0`
|
||||
return b
|
||||
}
|
||||
|
||||
pub fn (s string) cstr() byteptr {
|
||||
clone := s.clone()
|
||||
return clone.str
|
||||
}
|
||||
|
||||
pub fn (s string) replace(rep, with string) string {
|
||||
if s.len == 0 || rep.len == 0 {
|
||||
return s
|
||||
}
|
||||
if !s.contains(rep) {
|
||||
return s
|
||||
}
|
||||
// println('"$s" replace "$rep" with "$with" rep.len=$rep.len')
|
||||
// TODO PERF Allocating ints is expensive. Should be a stack array
|
||||
// Get locations of all reps within this string
|
||||
mut idxs := []int{}
|
||||
// idxs := []int {
|
||||
// 2, 8, 14
|
||||
// }
|
||||
for i := 0; i < s.len; i++ {
|
||||
// Do we have the string we are looking for (rep) starting at i?
|
||||
// Go thru all chars in rep and compare.
|
||||
mut rep_i := 0
|
||||
mut j := i
|
||||
for rep_i < rep.len && j < s.len && s[j] == rep[rep_i] {
|
||||
rep_i++
|
||||
j++
|
||||
}
|
||||
if rep_i == rep.len {
|
||||
idxs << i
|
||||
}
|
||||
}
|
||||
// Dont change the string if there's nothing to replace
|
||||
if idxs.len == 0 {
|
||||
return s
|
||||
}
|
||||
// Now we know the number of replacements we need to do and we can calc the len of the new string
|
||||
new_len := s.len + idxs.len * (with.len - rep.len)
|
||||
mut b := malloc(new_len + 1)// add a newline just in case
|
||||
// Fill the new string
|
||||
mut idx_pos := 0
|
||||
mut cur_idx := idxs[idx_pos]
|
||||
mut b_i := 0
|
||||
for i := 0; i < s.len; i++ {
|
||||
// Reached the location of rep, replace it with "with"
|
||||
if i == cur_idx {
|
||||
for j := 0; j < with.len; j++ {
|
||||
b[b_i] = with[j]
|
||||
b_i++
|
||||
}
|
||||
// Skip the length of rep, since we just replaced it with "with"
|
||||
i += rep.len - 1
|
||||
// Go to the next index
|
||||
idx_pos++
|
||||
if idx_pos < idxs.len {
|
||||
cur_idx = idxs[idx_pos]
|
||||
}
|
||||
}
|
||||
// Rep doesnt start here, just copy
|
||||
else {
|
||||
b[b_i] = s[i]
|
||||
b_i++
|
||||
}
|
||||
}
|
||||
b[new_len] = `\0`
|
||||
return tos(b, new_len)
|
||||
}
|
||||
|
||||
pub fn (s string) int() int {
|
||||
return C.atoi(s.str)
|
||||
}
|
||||
|
||||
pub fn (s string) f32() f32 {
|
||||
return C.atof(s.str)
|
||||
}
|
||||
|
||||
// ==
|
||||
fn (s string) eq(a string) bool {
|
||||
if isnil(s.str) {
|
||||
panic('string.eq(): nil string')
|
||||
}
|
||||
if s.len != a.len {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < s.len; i++ {
|
||||
if s[i] != a[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// !=
|
||||
fn (s string) ne(a string) bool {
|
||||
return !s.eq(a)
|
||||
}
|
||||
|
||||
// s < a
|
||||
fn (s string) lt(a string) bool {
|
||||
for i := 0; i < s.len; i++ {
|
||||
if i >= a.len || s[i] > a[i] {
|
||||
return false
|
||||
}
|
||||
else if s[i] < a[i] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if s.len < a.len {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// s <= a
|
||||
fn (s string) le(a string) bool {
|
||||
return s.lt(a) || s.eq(a)
|
||||
}
|
||||
|
||||
// s > a
|
||||
fn (s string) gt(a string) bool {
|
||||
return !s.le(a)
|
||||
}
|
||||
|
||||
// s >= a
|
||||
fn (s string) ge(a string) bool {
|
||||
return !s.lt(a)
|
||||
}
|
||||
|
||||
// TODO `fn (s string) + (a string)` ? To be consistent with operator overloading syntax.
|
||||
pub fn (s string) add(a string) string {
|
||||
new_len := a.len + s.len
|
||||
mut res := string {
|
||||
len: new_len
|
||||
str: malloc(new_len + 1)
|
||||
}
|
||||
for j := 0; j < s.len; j++ {
|
||||
res[j] = s[j]
|
||||
}
|
||||
for j := 0; j < a.len; j++ {
|
||||
res[s.len + j] = a[j]
|
||||
}
|
||||
res[new_len] = `\0`// V strings are not null terminated, but just in case
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn (s string) split(delim string) []string {
|
||||
// println('string split delim="$delim" s="$s"')
|
||||
mut res := []string
|
||||
if delim.len == 0 {
|
||||
res << s
|
||||
return res
|
||||
}
|
||||
if delim.len == 1 {
|
||||
return s.split_single(delim[0])
|
||||
// println('split 1 only')
|
||||
// os.exit()
|
||||
}
|
||||
mut i := 0
|
||||
mut start := 0// - 1
|
||||
for i < s.len {
|
||||
// printiln(i)
|
||||
mut a := s[i] == delim[0]
|
||||
mut j := 1
|
||||
for j < delim.len && a {
|
||||
a = a && s[i + j] == delim[j]
|
||||
j++
|
||||
}
|
||||
last := i == s.len - 1
|
||||
if a || last {
|
||||
if last {
|
||||
i++
|
||||
}
|
||||
mut val := s.substr(start, i)
|
||||
// println('got it "$val" start=$start i=$i delim="$delim"')
|
||||
if val.len > 0 {
|
||||
// todo perf
|
||||
// val now is '___VAL'. remove '___' from the start
|
||||
if val.starts_with(delim) {
|
||||
// println('!!')
|
||||
val = val.right(delim.len)
|
||||
}
|
||||
res << val.trim_space()
|
||||
}
|
||||
start = i
|
||||
}
|
||||
i++
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn (s string) split_single(delim byte) []string {
|
||||
mut res := []string
|
||||
if int(delim) == 0 {
|
||||
res << s
|
||||
return res
|
||||
}
|
||||
mut i := 0
|
||||
mut start := 0
|
||||
for i < s.len {
|
||||
a := s[i] == delim
|
||||
b := i == s.len - 1
|
||||
if a || b {
|
||||
if i == s.len - 1 {
|
||||
i++
|
||||
}
|
||||
val := s.substr(start, i)
|
||||
if val.len > 0 {
|
||||
res << val.trim_space()
|
||||
}
|
||||
start = i + 1
|
||||
}
|
||||
i++
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn (s string) split_into_lines() []string {
|
||||
mut res := []string
|
||||
if s.len == 0 {
|
||||
return res
|
||||
}
|
||||
mut start := 0
|
||||
for i := 0; i < s.len; i++ {
|
||||
last := i == s.len - 1
|
||||
if int(s[i]) == 10 || last {
|
||||
if last {
|
||||
i++
|
||||
}
|
||||
line := s.substr(start, i)
|
||||
res << line
|
||||
start = i + 1
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// 'hello'.left(2) => 'he'
|
||||
pub fn (s string) left(n int) string {
|
||||
if n >= s.len {
|
||||
return s
|
||||
}
|
||||
return s.substr(0, n)
|
||||
}
|
||||
|
||||
pub fn (s string) right(n int) string {
|
||||
if n >= s.len {
|
||||
return ''
|
||||
}
|
||||
return s.substr(n, s.len)
|
||||
}
|
||||
|
||||
// Because the string is immutable, it is safe for multiple strings to share
|
||||
// the same storage, so slicing s results in a new 2-word structure with a
|
||||
// potentially different pointer and length that still refers to the same byte
|
||||
// sequence. This means that slicing can be done without allocation or copying,
|
||||
// making string slices as efficient as passing around explicit indexes.
|
||||
// substr without allocations. Reuses memory and works great. BUT. This substring does not have
|
||||
// a \0 at the end, and it's not possible to add it. So if we have s = 'privet'
|
||||
// and substr := s.substr_fast(1, 4) ('riv')
|
||||
// puts(substr.str) will print 'rivet'
|
||||
// Avoid using C functions with these substrs!
|
||||
pub fn (s string) substr(start, end int) string {
|
||||
/*
|
||||
if start > end || start >= s.len || end > s.len || start < 0 || end < 0 {
|
||||
panic('substr($start, $end) out of bounds (len=$s.len)')
|
||||
return ''
|
||||
}
|
||||
*/
|
||||
if start >= s.len {
|
||||
return ''
|
||||
}
|
||||
len := end - start
|
||||
res := string {
|
||||
str: s.str + start
|
||||
len: len
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// KMP search
|
||||
pub fn (s string) index(p string) int {
|
||||
if p.len > s.len {
|
||||
return -1
|
||||
}
|
||||
mut prefix := [0]
|
||||
mut j := 0
|
||||
for i := 1; i < p.len; i++ {
|
||||
for p[j] != p[i] && j > 0 {
|
||||
j = prefix[j - 1]
|
||||
}
|
||||
if p[j] == p[i] {
|
||||
j++
|
||||
}
|
||||
prefix << j
|
||||
}
|
||||
j = 0
|
||||
for i := 0; i < s.len; i++ {
|
||||
for p[j] != s[i] && j > 0 {
|
||||
j = prefix[j - 1]
|
||||
}
|
||||
if p[j] == s[i] {
|
||||
j++
|
||||
}
|
||||
if j == p.len {
|
||||
prefix.free()
|
||||
return i - p.len + 1
|
||||
}
|
||||
}
|
||||
prefix.free()
|
||||
return -1
|
||||
}
|
||||
|
||||
pub fn (s string) last_index(p string) int {
|
||||
if p.len > s.len {
|
||||
return -1
|
||||
}
|
||||
mut i := s.len - p.len
|
||||
for i >= 0 {
|
||||
mut j := 0
|
||||
for j < p.len && s[i + j] == p[j] {
|
||||
j++
|
||||
}
|
||||
if j == p.len {
|
||||
return i
|
||||
}
|
||||
i--
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
pub fn (s string) index_after(p string, start int) int {
|
||||
if p.len > s.len {
|
||||
return -1
|
||||
}
|
||||
mut strt := start
|
||||
if start < 0 {
|
||||
strt = 0
|
||||
}
|
||||
if start >= s.len {
|
||||
return -1
|
||||
}
|
||||
mut i := strt
|
||||
for i < s.len {
|
||||
mut j := 0
|
||||
mut ii := i
|
||||
for j < p.len && s[ii] == p[j] {
|
||||
j++
|
||||
ii++
|
||||
}
|
||||
if j == p.len {
|
||||
return i
|
||||
}
|
||||
i++
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
pub fn (s string) contains(p string) bool {
|
||||
res := s.index(p) > 0 - 1
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn (s string) starts_with(p string) bool {
|
||||
res := s.index(p) == 0
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn (s string) ends_with(p string) bool {
|
||||
if p.len > s.len {
|
||||
return false
|
||||
}
|
||||
res := s.last_index(p) == s.len - p.len
|
||||
return res
|
||||
}
|
||||
|
||||
// TODO only works with ASCII
|
||||
pub fn (s string) to_lower() string {
|
||||
mut b := malloc(s.len)// TODO + 1 ??
|
||||
for i := 0; i < s.len; i++ {
|
||||
b[i] = C.tolower(s.str[i])
|
||||
}
|
||||
return tos(b, s.len)
|
||||
}
|
||||
|
||||
pub fn (s string) to_upper() string {
|
||||
mut b := malloc(s.len)// TODO + 1 ??
|
||||
for i := 0; i < s.len; i++ {
|
||||
b[i] = C.toupper(s.str[i])
|
||||
}
|
||||
return tos(b, s.len)
|
||||
}
|
||||
|
||||
// 'hey [man] how you doin'
|
||||
// find_between('[', ']') == 'man'
|
||||
pub fn (s string) find_between(start, end string) string {
|
||||
start_pos := s.index(start)
|
||||
if start_pos == -1 {
|
||||
return ''
|
||||
}
|
||||
// First get everything to the right of 'start'
|
||||
val := s.right(start_pos + start.len)
|
||||
end_pos := val.index(end)
|
||||
if end_pos == -1 {
|
||||
return val
|
||||
}
|
||||
return val.left(end_pos)
|
||||
}
|
||||
|
||||
// TODO generic
|
||||
pub fn (ar[]string) contains(val string) bool {
|
||||
for s in ar {
|
||||
if s == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO generic
|
||||
pub fn (ar[]int) contains(val int) bool {
|
||||
for i, s in ar {
|
||||
if s == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn (a[]string) to_c() voidptr {
|
||||
char ** res = malloc(sizeof(char*) * a.len);
|
||||
for i := 0; i < a.len; i++ {
|
||||
val := a[i]
|
||||
# res[i] = val.str;
|
||||
}
|
||||
return res;
|
||||
return 0
|
||||
}
|
||||
*/
|
||||
|
||||
fn is_space(c byte) bool {
|
||||
return C.isspace(c)
|
||||
}
|
||||
|
||||
pub fn (c byte) is_space() bool {
|
||||
return is_space(c)
|
||||
}
|
||||
|
||||
pub fn (s string) trim_space() string {
|
||||
if s == '' {
|
||||
return ''
|
||||
}
|
||||
// println('TRIM SPACE "$s"')
|
||||
mut i := 0
|
||||
for i < s.len && is_space(s[i]) {
|
||||
i++
|
||||
}
|
||||
mut res := s.right(i)
|
||||
mut end := res.len - 1
|
||||
for end >= 0 && is_space(res[end]) {
|
||||
// C.printf('end=%d c=%d %c\n', end, res.str[end])
|
||||
end--
|
||||
}
|
||||
res = res.left(end + 1)
|
||||
// println('after SPACE "$res"')
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn (s string) trim(c byte) string {
|
||||
if s == '' {
|
||||
return ''
|
||||
}
|
||||
mut i := 0
|
||||
for i < s.len && c == s[i] {
|
||||
i++
|
||||
}
|
||||
mut res := s.right(i)
|
||||
mut end := res.len - 1
|
||||
for end >= 0 && c == res[end] {
|
||||
end--
|
||||
}
|
||||
res = res.left(end + 1)
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn (s string) trim_left(cutset string) string {
|
||||
mut start := s.index(cutset)
|
||||
if start != 0 {
|
||||
return s
|
||||
}
|
||||
for start < s.len - 1 && s[start] == cutset[0] {
|
||||
start++
|
||||
}
|
||||
return s.right(start)
|
||||
}
|
||||
|
||||
pub fn (s string) trim_right(cutset string) string {
|
||||
pos := s.last_index(cutset)
|
||||
if pos == -1 {
|
||||
return s
|
||||
}
|
||||
return s.left(pos)
|
||||
}
|
||||
|
||||
// fn print_cur_thread() {
|
||||
// //C.printf("tid = %08x \n", pthread_self());
|
||||
// }
|
||||
fn compare_strings(a, b *string) int {
|
||||
if a.lt(b) {
|
||||
return -1
|
||||
}
|
||||
if a.gt(b) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
fn compare_strings_by_len(a, b *string) int {
|
||||
if a.len < b.len {
|
||||
return -1
|
||||
}
|
||||
if a.len > b.len {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
fn compare_lower_strings(a, b *string) int {
|
||||
aa := a.to_lower()
|
||||
bb := b.to_lower()
|
||||
return compare_strings(aa, bb)
|
||||
}
|
||||
|
||||
pub fn (s mut []string) sort() {
|
||||
s.sort_with_compare(compare_strings)
|
||||
}
|
||||
|
||||
pub fn (s mut []string) sort_ignore_case() {
|
||||
s.sort_with_compare(compare_lower_strings)
|
||||
}
|
||||
|
||||
pub fn (s mut []string) sort_by_len() {
|
||||
s.sort_with_compare(compare_strings_by_len)
|
||||
}
|
||||
|
||||
pub fn (s string) ustring() ustring {
|
||||
mut res := ustring {
|
||||
s: s
|
||||
// runes will have at least s.len elements, save reallocations
|
||||
// TODO use VLA for small strings?
|
||||
runes: new_array(0, s.len, sizeof(int))
|
||||
}
|
||||
for i := 0; i < s.len; i++ {
|
||||
char_len := utf8_char_len(s.str[i])
|
||||
//# char_len =UTF8_CHAR_LEN(s.str[i]);
|
||||
// println('cl=$char_len')
|
||||
res.runes << i
|
||||
i += char_len - 1
|
||||
res.len++
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// A hack that allows to create ustring without allocations.
|
||||
// It's called from functions like draw_text() where we know that the string is going to be freed
|
||||
// right away. Uses global buffer for storing runes []int array.
|
||||
__global g_ustring_runes []int
|
||||
pub fn (s string) ustring_tmp() ustring {
|
||||
mut res := ustring {
|
||||
s: s
|
||||
runes: 0
|
||||
}
|
||||
res.runes = g_ustring_runes
|
||||
res.runes.len = s.len
|
||||
mut j := 0
|
||||
for i := 0; i < s.len; i++ {
|
||||
//char_len := 0
|
||||
//# char_len =UTF8_CHAR_LEN(s.str[i]);
|
||||
char_len := utf8_char_len(s.str[i])
|
||||
res.runes[j] = i
|
||||
j++
|
||||
i += char_len - 1
|
||||
res.len++
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
fn (u ustring) substr(start, end int) string {
|
||||
// println('substr($start, $end)')
|
||||
// println('runes=')
|
||||
// println(u.runes)
|
||||
start = u.runes[start]
|
||||
// handle last char
|
||||
if end >= u.runes.len {
|
||||
end = u.s.len
|
||||
}
|
||||
else {
|
||||
end = u.runes[end]
|
||||
}
|
||||
// println('fast $start, $end')
|
||||
return u.s.substr(start, end)
|
||||
}
|
||||
|
||||
fn (u ustring) left(pos int) string {
|
||||
return u.substr(0, pos)
|
||||
}
|
||||
|
||||
fn (u ustring) right(pos int) string {
|
||||
return u.substr(pos, u.len)
|
||||
}
|
||||
|
||||
fn (s string) at(idx int) byte {
|
||||
if idx < 0 || idx >= s.len {
|
||||
panic('string index out of range: $idx / $s.len')
|
||||
}
|
||||
return s.str[idx]
|
||||
}
|
||||
|
||||
pub fn (u ustring) at(idx int) string {
|
||||
return u.substr(idx, idx + 1)
|
||||
}
|
||||
|
||||
fn (u ustring) free() {
|
||||
u.runes.free()
|
||||
}
|
||||
|
||||
fn abs(a int) int {
|
||||
if a >= 0 {
|
||||
return a
|
||||
}
|
||||
return -a
|
||||
}
|
||||
|
||||
pub fn (c byte) is_digit() bool {
|
||||
return c >= `0` && c <= `9`
|
||||
}
|
||||
|
||||
pub fn (c byte) is_letter() bool {
|
||||
return (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`)
|
||||
}
|
||||
|
||||
pub fn (s string) free() {
|
||||
C.free(s.str)
|
||||
}
|
||||
|
||||
fn (arr[]string) free() {
|
||||
for s in arr {
|
||||
s.free()
|
||||
}
|
||||
C.free(arr.data)
|
||||
}
|
||||
|
||||
// all_before('23:34:45.234', '.') == '23:34:45'
|
||||
pub fn (s string) all_before(dot string) string {
|
||||
pos := s.index(dot)
|
||||
if pos == -1 {
|
||||
return s
|
||||
}
|
||||
return s.left(pos)
|
||||
}
|
||||
|
||||
pub fn (s string) all_before_last(dot string) string {
|
||||
pos := s.last_index(dot)
|
||||
if pos == -1 {
|
||||
return s
|
||||
}
|
||||
return s.left(pos)
|
||||
}
|
||||
|
||||
pub fn (s string) all_after(dot string) string {
|
||||
pos := s.last_index(dot)
|
||||
if pos == -1 {
|
||||
return s
|
||||
}
|
||||
return s.right(pos + dot.len)
|
||||
}
|
||||
|
||||
// fn (s []string) substr(a, b int) string {
|
||||
// return join_strings(s.slice_fast(a, b))
|
||||
// }
|
||||
pub fn (a[]string) join(del string) string {
|
||||
if a.len == 0 {
|
||||
return ''
|
||||
}
|
||||
mut len := 0
|
||||
for i, val in a {
|
||||
len += val.len + del.len
|
||||
}
|
||||
len -= del.len
|
||||
// Allocate enough memory
|
||||
mut res := ''
|
||||
res.len = len
|
||||
res.str = malloc(res.len + 1)
|
||||
mut idx := 0
|
||||
// Go thru every string and copy its every char one by one
|
||||
for i, val in a {
|
||||
for j := 0; j < val.len; j++ {
|
||||
c := val[j]
|
||||
res.str[idx] = val.str[j]
|
||||
idx++
|
||||
}
|
||||
// Add del if it's not last
|
||||
if i != a.len - 1 {
|
||||
for k := 0; k < del.len; k++ {
|
||||
res.str[idx] = del.str[k]
|
||||
idx++
|
||||
}
|
||||
}
|
||||
}
|
||||
res.str[res.len] = `\0`
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn (s[]string) join_lines() string {
|
||||
return s.join('\n')
|
||||
}
|
||||
|
||||
pub fn (s string) reverse() string {
|
||||
mut res := string {
|
||||
len: s.len
|
||||
str: malloc(s.len + 1)
|
||||
}
|
||||
|
||||
for i := s.len - 1; i >= 0; i-- {
|
||||
res[s.len-i-1] = s[i]
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// 'hello'.limit(2) => 'he'
|
||||
// 'hi'.limit(10) => 'hi'
|
||||
pub fn (s string) limit(max int) string {
|
||||
u := s.ustring()
|
||||
if u.len <= max {
|
||||
return s
|
||||
}
|
||||
return u.substr(0, max)
|
||||
}
|
||||
|
||||
// TODO is_white_space()
|
||||
pub fn (c byte) is_white() bool {
|
||||
i := int(c)
|
||||
return i == 10 || i == 32 || i == 9 || i == 13 || c == `\r`
|
||||
}
|
||||
|
||||
// TODO move this to strings.repeat()
|
||||
pub fn repeat_char(c byte, n int) string {
|
||||
if n <= 0 {
|
||||
return ''
|
||||
}
|
||||
mut arr := malloc(n + 1)
|
||||
for i := 0; i < n; i++ {
|
||||
arr[i] = c
|
||||
}
|
||||
arr[n] = `\0`
|
||||
return tos(arr, n)
|
||||
}
|
||||
|
||||
pub fn (s string) hash() int {
|
||||
mut hash := int(0)
|
||||
for i := 0; i < s.len; i++ {
|
||||
// if key == 'Content-Type' {
|
||||
// println('$i) $hash')
|
||||
// }
|
||||
hash = hash * int(31) + int(s.str[i])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
39
vlib/builtin/string_builder.v
Normal file
39
vlib/builtin/string_builder.v
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
module builtin
|
||||
|
||||
struct StringBuilder {
|
||||
buf []byte
|
||||
len int
|
||||
}
|
||||
|
||||
pub fn new_string_builder(initial_size int) StringBuilder {
|
||||
return StringBuilder {
|
||||
buf: new_array(0, initial_size, sizeof(byte))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (b mut StringBuilder) write(s string) {
|
||||
b.buf._push_many(s.str, s.len)
|
||||
b.len += s.len
|
||||
}
|
||||
|
||||
pub fn (b mut StringBuilder) writeln(s string) {
|
||||
b.buf._push_many(s.str, s.len)
|
||||
b.buf << `\n`
|
||||
b.len += s.len + 1
|
||||
}
|
||||
|
||||
pub fn (b StringBuilder) str() string {
|
||||
return tos(b.buf.data, b.len)
|
||||
}
|
||||
|
||||
pub fn (b StringBuilder) cut(n int) {
|
||||
b.len -= n
|
||||
}
|
||||
|
||||
pub fn (b mut StringBuilder) free() {
|
||||
C.free(b.buf.data)
|
||||
}
|
||||
295
vlib/builtin/string_test.v
Normal file
295
vlib/builtin/string_test.v
Normal file
@@ -0,0 +1,295 @@
|
||||
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
fn test_add() {
|
||||
mut a := 'a'
|
||||
a += 'b'
|
||||
assert a==('ab')
|
||||
a = 'a'
|
||||
for i := 1; i < 1000; i++ {
|
||||
a += 'b'
|
||||
}
|
||||
assert a.len == 1000
|
||||
assert a.ends_with('bbbbb')
|
||||
a += '123'
|
||||
assert a.ends_with('3')
|
||||
}
|
||||
|
||||
fn test_ends_with() {
|
||||
a := 'browser.v'
|
||||
assert a.ends_with('.v')
|
||||
}
|
||||
|
||||
fn test_between() {
|
||||
s := 'hello [man] how you doing'
|
||||
assert s.find_between('[', ']') == 'man'
|
||||
}
|
||||
|
||||
fn test_compare() {
|
||||
a := 'Music'
|
||||
b := 'src'
|
||||
assert b>=(a)
|
||||
}
|
||||
|
||||
fn test_lt() {
|
||||
a := ''
|
||||
b := 'a'
|
||||
c := 'a'
|
||||
d := 'b'
|
||||
e := 'aa'
|
||||
f := 'ab'
|
||||
assert a<(b)
|
||||
assert !(b<c)
|
||||
assert c<(d)
|
||||
assert !(d<e)
|
||||
assert c<(e)
|
||||
assert e<(f)
|
||||
}
|
||||
|
||||
fn test_ge() {
|
||||
a := 'aa'
|
||||
b := 'aa'
|
||||
c := 'ab'
|
||||
d := 'abc'
|
||||
e := 'aaa'
|
||||
assert b>=(a)
|
||||
assert c>=(b)
|
||||
assert d>=(c)
|
||||
assert !(c>=d)
|
||||
assert e>=(a)
|
||||
}
|
||||
|
||||
fn test_compare_strings() {
|
||||
a := 'aa'
|
||||
b := 'aa'
|
||||
c := 'ab'
|
||||
d := 'abc'
|
||||
e := 'aaa'
|
||||
assert compare_strings(a, b) == 0
|
||||
assert compare_strings(b, c) == -1
|
||||
assert compare_strings(c, d) == -1
|
||||
assert compare_strings(d, e) == 1
|
||||
assert compare_strings(a, e) == -1
|
||||
assert compare_strings(e, a) == 1
|
||||
}
|
||||
|
||||
fn test_sort() {
|
||||
mut vals := [
|
||||
'arr', 'an', 'a', 'any'
|
||||
]
|
||||
len := vals.len
|
||||
vals.sort()
|
||||
assert len == vals.len
|
||||
assert vals[0] == 'a'
|
||||
assert vals[1] == 'an'
|
||||
assert vals[2] == 'any'
|
||||
assert vals[3] == 'arr'
|
||||
}
|
||||
|
||||
fn test_split() {
|
||||
mut s := 'volt/twitch.v:34'
|
||||
mut vals := s.split(':')
|
||||
assert vals.len == 2
|
||||
assert vals[0]== 'volt/twitch.v'
|
||||
assert vals[1]== '34'
|
||||
// /////////
|
||||
s = '2018-01-01z13:01:02'
|
||||
vals = s.split('z')
|
||||
assert vals.len == 2
|
||||
assert vals[0]=='2018-01-01'
|
||||
assert vals[1]== '13:01:02'
|
||||
// /////////////
|
||||
s = '4627a862c3dec29fb3182a06b8965e0025759e18___1530207969___blue'
|
||||
vals = s.split('___')
|
||||
assert vals.len == 3
|
||||
assert vals[0]== '4627a862c3dec29fb3182a06b8965e0025759e18'
|
||||
assert vals[1]=='1530207969'
|
||||
assert vals[2]== 'blue'
|
||||
}
|
||||
|
||||
fn test_trim_space() {
|
||||
a := ' a '
|
||||
assert a.trim_space() == 'a'
|
||||
code := '
|
||||
|
||||
fn main() {
|
||||
println(2)
|
||||
}
|
||||
|
||||
'
|
||||
code_clean := 'fn main() {
|
||||
println(2)
|
||||
}'
|
||||
assert code.trim_space() == code_clean
|
||||
}
|
||||
|
||||
fn test_join() {
|
||||
mut strings := [ 'a', 'b', 'c' ]
|
||||
mut s := strings.join(' ')
|
||||
assert s == 'a b c'
|
||||
strings = ['one
|
||||
two ',
|
||||
'three!
|
||||
four!']
|
||||
s = strings.join(' ')
|
||||
assert s.contains('one') && s.contains('two ') && s.contains('four')
|
||||
}
|
||||
|
||||
fn test_clone() {
|
||||
mut a := 'a'
|
||||
a += 'a'
|
||||
a += 'a'
|
||||
mut b := a
|
||||
mut c := a.clone()
|
||||
assert c == a
|
||||
assert c == 'aaa'
|
||||
assert b == 'aaa'
|
||||
}
|
||||
|
||||
fn test_replace() {
|
||||
a := 'hello man!'
|
||||
mut b := a.replace('man', 'world')
|
||||
assert b==('hello world!')
|
||||
b = b.replace('!', '')
|
||||
assert b==('hello world')
|
||||
b = b.replace('h', 'H')
|
||||
assert b==('Hello world')
|
||||
b = b.replace('kek', 'lul')
|
||||
assert b==('Hello world')
|
||||
s := 'hey man how are you'
|
||||
assert s.replace('man ', '') == 'hey how are you'
|
||||
lol := 'lol lol lol'
|
||||
assert lol.replace('lol', 'kek') == 'kek kek kek'
|
||||
b = 'oneBtwoBBthree'
|
||||
assert b.replace('B', '') == 'onetwothree'
|
||||
b = '**char'
|
||||
assert b.replace('*char', 'byteptr') == '*byteptr'
|
||||
mut c :='abc'
|
||||
assert c.replace('','-') == c
|
||||
}
|
||||
|
||||
fn test_itoa() {
|
||||
num := 777
|
||||
assert num.str() == '777'
|
||||
big := 7779998
|
||||
assert big.str() == '7779998'
|
||||
a := 3
|
||||
assert a.str() == '3'
|
||||
b := 5555
|
||||
assert b.str() == '5555'
|
||||
zero := 0
|
||||
assert zero.str() == '0'
|
||||
neg := -7
|
||||
assert neg.str() == '-7'
|
||||
}
|
||||
|
||||
fn test_reassign() {
|
||||
a := 'hi'
|
||||
mut b := a
|
||||
b += '!'
|
||||
assert a == 'hi'
|
||||
assert b == 'hi!'
|
||||
}
|
||||
|
||||
fn test_runes() {
|
||||
s := 'привет'
|
||||
assert s.len == 12
|
||||
s2 := 'privet'
|
||||
assert s2.len == 6
|
||||
u := s.ustring()
|
||||
assert u.len == 6
|
||||
assert s2.substr(1, 4).len == 3
|
||||
assert s2.substr(1, 4) == 'riv'
|
||||
assert u.substr(1, 4).len == 6
|
||||
assert u.substr(1, 4) == 'рив'
|
||||
assert s2.substr(1, 2) == 'r'
|
||||
assert u.substr(1, 2) == 'р'
|
||||
assert s2.ustring().at(1) == 'r'
|
||||
assert u.at(1) == 'р'
|
||||
// ///////
|
||||
first := u.at(0)
|
||||
last := u.at(u.len - 1)
|
||||
assert first.len == 2
|
||||
assert last.len == 2
|
||||
}
|
||||
|
||||
fn test_lower() {
|
||||
mut s := 'A'
|
||||
assert s.to_lower() == 'a'
|
||||
assert s.to_lower().len == 1
|
||||
s = 'HELLO'
|
||||
assert s.to_lower() == 'hello'
|
||||
assert s.to_lower().len == 5
|
||||
s = 'Aloha'
|
||||
assert s.to_lower() == 'aloha'
|
||||
s = 'Have A nice Day!'
|
||||
assert s.to_lower() == 'have a nice day!'
|
||||
s = 'hi'
|
||||
assert s.to_lower() == 'hi'
|
||||
}
|
||||
|
||||
fn test_left_right() {
|
||||
s := 'ALOHA'
|
||||
assert s.left(3) == 'ALO'
|
||||
assert s.right(3) == 'HA'
|
||||
u := s.ustring()
|
||||
assert u.left(3) == 'ALO'
|
||||
assert u.right(3) == 'HA'
|
||||
}
|
||||
|
||||
fn test_contains() {
|
||||
s := 'view.v'
|
||||
assert s.contains('vi')
|
||||
assert !s.contains('random')
|
||||
}
|
||||
|
||||
fn test_arr_contains() {
|
||||
a := ['a', 'b', 'c']
|
||||
assert a.contains('b')
|
||||
ints := [1, 2, 3]
|
||||
assert ints.contains(2)
|
||||
}
|
||||
|
||||
fn test_to_num() {
|
||||
s := '7'
|
||||
assert s.int() == 7
|
||||
f := '71.5 hasdf'
|
||||
assert f.f32() == 71.5
|
||||
b := 1.52345
|
||||
mut a := '${b:.03f}'
|
||||
assert a == '1.523'
|
||||
num := 7
|
||||
a = '${num:03d}'
|
||||
vals := ['9']
|
||||
assert vals[0].int() == 9
|
||||
}
|
||||
|
||||
fn test_hash() {
|
||||
s := '10000'
|
||||
assert s.hash() == 46730161
|
||||
s2 := '24640'
|
||||
assert s2.hash() == 47778736
|
||||
s3 := 'Content-Type'
|
||||
assert s3.hash() == 949037134
|
||||
s4 := 'bad_key'
|
||||
assert s4.hash() == -346636507
|
||||
s5 := '24640'
|
||||
// From a map collision test
|
||||
assert s5.hash() % ((1 << 20) -1) == s.hash() % ((1 << 20) -1)
|
||||
assert s5.hash() % ((1 << 20) -1) == 592861
|
||||
}
|
||||
|
||||
fn test_trim_left() {
|
||||
mut s := 'module main'
|
||||
assert s.trim_left(' ') == 'module main'
|
||||
s = ' module main'
|
||||
assert s.trim_left(' ') == 'module main'
|
||||
}
|
||||
|
||||
fn test_all_after() {
|
||||
s := 'fn hello'
|
||||
q := s.all_after('fn ')
|
||||
assert q == 'hello'
|
||||
}
|
||||
|
||||
93
vlib/builtin/utf8.v
Normal file
93
vlib/builtin/utf8.v
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
module builtin
|
||||
|
||||
pub fn utf8_char_len(b byte) int {
|
||||
return (( 0xe5000000 >> (( b >> 3 ) & 0x1e )) & 3 ) + 1
|
||||
}
|
||||
|
||||
// Convert utf32 to utf8
|
||||
// utf32 == Codepoint
|
||||
pub fn utf32_to_str(code u32) string {
|
||||
icode := int(code) //Prevents doing casts everywhere
|
||||
mut buffer := malloc(5)
|
||||
if icode <= 127 /* 0x7F */ {
|
||||
buffer[0] = icode
|
||||
return tos(buffer, 1)
|
||||
}
|
||||
if (icode <= 2047 /* 0x7FF */) {
|
||||
buffer[0] = 192 /*0xC0*/ | (icode >> 6) /* 110xxxxx */
|
||||
buffer[1] = 128 /*0x80*/ | (icode & 63 /*0x3F*/) /* 10xxxxxx */
|
||||
return tos(buffer, 2)
|
||||
}
|
||||
if (icode <= 65535 /* 0xFFFF */) {
|
||||
buffer[0] = 224 /*0xE0*/ | (icode >> 12) /* 1110xxxx */
|
||||
buffer[1] = 128 /*0x80*/ | ((icode >> 6) & 63 /*0x3F*/) /* 10xxxxxx */
|
||||
buffer[2] = 128 /*0x80*/ | (icode & 63 /*0x3F*/) /* 10xxxxxx */
|
||||
return tos(buffer, 3)
|
||||
}
|
||||
if (icode <= 1114111 /* 0x10FFFF */) {
|
||||
buffer[0] = 240 /*0xF0*/ | (icode >> 18) /* 11110xxx */
|
||||
buffer[1] = 128 /*0x80*/ | ((icode >> 12) & 63 /*0x3F*/) /* 10xxxxxx */
|
||||
buffer[2] = 128 /*0x80*/ | ((icode >> 6) & 63 /*0x3F*/) /* 10xxxxxx */
|
||||
buffer[3] = 128 /*0x80*/ | (icode & 63 /*0x3F*/) /* 10xxxxxx */
|
||||
return tos(buffer, 4)
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
// TODO copypasta
|
||||
pub fn utf32_to_str_no_malloc(code u32, buf voidptr) string {
|
||||
icode := int(code) //Prevents doing casts everywhere
|
||||
mut buffer := byteptr(buf)
|
||||
if icode <= 127 /* 0x7F */ {
|
||||
buffer[0] = icode
|
||||
return tos(buffer, 1)
|
||||
}
|
||||
if (icode <= 2047 /* 0x7FF */) {
|
||||
buffer[0] = 192 /*0xC0*/ | (icode >> 6) /* 110xxxxx */
|
||||
buffer[1] = 128 /*0x80*/ | (icode & 63 /*0x3F*/) /* 10xxxxxx */
|
||||
return tos(buffer, 2)
|
||||
}
|
||||
if (icode <= 65535 /* 0xFFFF */) {
|
||||
buffer[0] = 224 /*0xE0*/ | (icode >> 12) /* 1110xxxx */
|
||||
buffer[1] = 128 /*0x80*/ | ((icode >> 6) & 63 /*0x3F*/) /* 10xxxxxx */
|
||||
buffer[2] = 128 /*0x80*/ | (icode & 63 /*0x3F*/) /* 10xxxxxx */
|
||||
return tos(buffer, 3)
|
||||
}
|
||||
if (icode <= 1114111 /* 0x10FFFF */) {
|
||||
buffer[0] = 240 /*0xF0*/ | (icode >> 18) /* 11110xxx */
|
||||
buffer[1] = 128 /*0x80*/ | ((icode >> 12) & 63 /*0x3F*/) /* 10xxxxxx */
|
||||
buffer[2] = 128 /*0x80*/ | ((icode >> 6) & 63 /*0x3F*/) /* 10xxxxxx */
|
||||
buffer[3] = 128 /*0x80*/ | (icode & 63 /*0x3F*/) /* 10xxxxxx */
|
||||
return tos(buffer, 4)
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
// Convert utf8 to utf32
|
||||
pub fn (_rune string) utf32_code() int {
|
||||
if _rune.len == 0 {
|
||||
return 0
|
||||
}
|
||||
// save ASC symbol as is
|
||||
if _rune.len == 1 {
|
||||
return int(_rune[0])
|
||||
}
|
||||
mut b := byte(int(_rune[0]))
|
||||
// TODO should be
|
||||
// res := int( rune[0] << rune.len)
|
||||
b = b << _rune.len
|
||||
mut res := int(b)
|
||||
mut shift := 6 - _rune.len
|
||||
for i := 1; i < _rune.len; i++ {
|
||||
c := int(_rune[i])
|
||||
res = res << shift
|
||||
res |= c & 63 /* 0x3f */
|
||||
shift = 6
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user