string: make substr() copy the data, like in Java and C#; remove .cstr()

this makes managing memory used by strings much easier
V strings are now fully compatible with C strings
pull/1270/head
Alexander Medvednikov 2019-07-22 16:51:33 +02:00
parent 59eac5686f
commit 390394b56b
10 changed files with 68 additions and 66 deletions

View File

@ -43,7 +43,7 @@ fn new_scanner(file_path string) *Scanner {
// BOM check
if raw_text.len >= 3 {
c_text := raw_text.cstr()
c_text := raw_text.str
if c_text[0] == 0xEF && c_text[1] == 0xBB && c_text[2] == 0xBF {
// skip three BOM bytes

View File

@ -70,10 +70,12 @@ pub fn (a string) clone() string {
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 {
@ -332,16 +334,7 @@ pub fn (s string) right(n int) string {
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!
// substr
pub fn (s string) substr(start, end int) string {
/*
if start > end || start >= s.len || end > s.len || start < 0 || end < 0 {
@ -353,11 +346,25 @@ pub fn (s string) substr(start, end int) string {
return ''
}
len := end - start
// Copy instead of pointing, like in Java and C#.
// Much easier to free such strings.
mut res := string {
len: len
str: malloc(len + 1)
}
for i := 0; i < len; i++ {
res.str[i] = s.str[start + i]
}
res.str[len] = `\0`
return res
/*
res := string {
str: s.str + start
len: len
}
return res
*/
}
// KMP search

View File

@ -29,9 +29,9 @@ fn download_file_with_progress(url, out string, cb downloadfn, cb_finished downl
if isnil(curl) {
return
}
cout := out.cstr()
cout := out.str
fp := C.fopen(cout, 'wb')
C.curl_easy_setopt(curl, CURLOPT_URL, url.cstr())
C.curl_easy_setopt(curl, CURLOPT_URL, url.str)
C.curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, download_cb)
data := &DownloadStruct {
stream:fp

View File

@ -104,7 +104,7 @@ pub fn (req &Request) do() Response {
}
// options
// url2 := req.url.clone()
C.curl_easy_setopt(curl, CURLOPT_URL, req.url.cstr())// ..clone())
C.curl_easy_setopt(curl, CURLOPT_URL, req.url.str)// ..clone())
// C.curl_easy_setopt(curl, CURLOPT_URL, 'http://example.com')
// return resp
// curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
@ -117,7 +117,7 @@ pub fn (req &Request) do() Response {
C.curl_easy_setopt(curl, CURLOPT_HEADERDATA, &hchunk)
C.curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1)
if req.typ == 'POST' {
C.curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req.data.cstr())
C.curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req.data.str)
C.curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, 'POST')
// req.headers << 'Content-Type: application/x-www-form-urlencoded'
}
@ -126,7 +126,7 @@ pub fn (req &Request) do() Response {
// for i, h := range req.headers {
for key, val in req.headers {
h := '$key: $val'
hlist = C.curl_slist_append(hlist, h.cstr())
hlist = C.curl_slist_append(hlist, h.str)
}
// curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, // (long)CURL_HTTP_VERSION_2TLS);<3B>`C<>ʀ9<CA80>
C.curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1)
@ -190,11 +190,11 @@ pub fn (req &Request) do() Response {
}
fn unescape(s string) string {
return string(byteptr(C.curl_unescape(s.cstr(), s.len)))
return string(byteptr(C.curl_unescape(s.str, s.len)))
}
fn escape(s string) string {
return string(byteptr(C.curl_escape(s.cstr(), s.len)))
return string(byteptr(C.curl_escape(s.str, s.len)))
}
// ////////////////

View File

@ -159,7 +159,7 @@ pub fn (s Socket) connect(address string, port int) int {
info := &C.addrinfo{!}
sport := '$port'
info_res := C.getaddrinfo(address.cstr(), sport.cstr(), &hints, &info)
info_res := C.getaddrinfo(address.str, sport.str, &hints, &info)
if info_res != 0 {
println('socket: getaddrinfo failed')
return info_res

View File

@ -9,7 +9,7 @@ fn test_socket() {
// println(socket)
// message := 'Hello World'
// socket.send(message.cstr(), message.len)
// socket.send(message.str, message.len)
// println('Sent: ' + message)
// bytes := client.recv(1024)

View File

@ -106,8 +106,7 @@ fn parse_windows_cmd_line(cmd byteptr) []string {
// read_file reads the file in `path` and returns the contents.
pub fn read_file(path string) ?string {
mut mode := 'rb'
cpath := path.cstr()
fp := C.fopen(cpath, mode.cstr())
fp := C.fopen(path.str, mode.str)
if isnil(fp) {
return error('failed to open file "$path"')
}
@ -130,7 +129,7 @@ pub fn file_size(path string) int {
}
pub fn mv(old, new string) {
C.rename(old.cstr(), new.cstr())
C.rename(old.str, new.str)
}
// read_lines reads the file in `path` into an array of lines.
@ -138,8 +137,7 @@ pub fn mv(old, new string) {
pub fn read_lines(path string) []string {
mut res := []string
mut buf := [1000]byte
cpath := path.cstr()
fp := C.fopen(cpath, 'rb')
fp := C.fopen(path.str, 'rb')
if isnil(fp) {
// TODO
// return error('failed to open file "$path"')
@ -171,9 +169,8 @@ fn read_ulines(path string) []ustring {
}
pub fn open(path string) ?File {
cpath := path.cstr()
file := File {
cfile: C.fopen(cpath, 'rb')
cfile: C.fopen(path.str, 'rb')
}
if isnil(file.cfile) {
return error('failed to open file "$path"')
@ -183,9 +180,8 @@ pub fn open(path string) ?File {
// create creates a file at a specified location and returns a writable `File` object.
pub fn create(path string) ?File {
cpath := path.cstr()
file := File {
cfile: C.fopen(cpath, 'wb')
cfile: C.fopen(path.str, 'wb')
}
if isnil(file.cfile) {
return error('failed to create file "$path"')
@ -194,9 +190,8 @@ pub fn create(path string) ?File {
}
pub fn open_append(path string) ?File {
cpath := path.cstr()
file := File {
cfile: C.fopen(cpath, 'ab')
cfile: C.fopen(path.str, 'ab')
}
if isnil(file.cfile) {
return error('failed to create file "$path"')
@ -205,8 +200,8 @@ pub fn open_append(path string) ?File {
}
pub fn (f File) write(s string) {
ss := s.clone()
C.fputs(ss.cstr(), f.cfile)
ss := s.clone() // TODO is clone() needed here?
C.fputs(ss.str, f.cfile)
// ss.free()
// C.fwrite(s.str, 1, s.len, f.cfile)
}
@ -228,7 +223,7 @@ pub fn (f File) writeln(s string) {
// C.fwrite(s.str, 1, s.len, f.cfile)
// ss := s.clone()
// TODO perf
C.fputs(s.cstr(), f.cfile)
C.fputs(s.str, f.cfile)
// ss.free()
C.fputs('\n', f.cfile)
}
@ -243,7 +238,7 @@ pub fn (f File) close() {
// system starts the specified command, waits for it to complete, and returns its code.
pub fn system(cmd string) int {
ret := C.system(cmd.cstr())
ret := C.system(cmd.str)
if ret == -1 {
os.print_c_errno()
}
@ -251,7 +246,7 @@ pub fn system(cmd string) int {
}
fn popen(path string) *FILE {
cpath := path.cstr()
cpath := path.str
$if windows {
return C._popen(cpath, 'r')
}
@ -279,7 +274,7 @@ pub fn exec(cmd string) string {
// `getenv` returns the value of the environment variable named by the key.
pub fn getenv(key string) string {
s := C.getenv(key.cstr())
s := C.getenv(key.str)
if isnil(s) {
return ''
}
@ -291,13 +286,13 @@ pub fn setenv(name string, value string, overwrite bool) int {
format := '$name=$value'
if overwrite {
return C._putenv(format.cstr())
return C._putenv(format.str)
}
return -1
}
$else {
return C.setenv(name.cstr(), value.cstr(), overwrite)
return C.setenv(name.str, value.str, overwrite)
}
}
@ -305,10 +300,10 @@ pub fn unsetenv(name string) int {
$if windows {
format := '${name}='
return C._putenv(format.cstr())
return C._putenv(format.str)
}
$else {
return C.unsetenv(name.cstr())
return C.unsetenv(name.str)
}
}
@ -322,7 +317,7 @@ pub fn file_exists(path string) bool {
pub fn dir_exists(path string) bool {
$if windows {
attr := int(C.GetFileAttributes(path.cstr()))
attr := int(C.GetFileAttributes(path.str))
if attr == INVALID_FILE_ATTRIBUTES {
return false
}
@ -332,7 +327,7 @@ pub fn dir_exists(path string) bool {
return false
}
$else {
dir := C.opendir(path.cstr())
dir := C.opendir(path.str)
res := !isnil(dir)
if res {
C.closedir(dir)
@ -345,27 +340,27 @@ pub fn dir_exists(path string) bool {
pub fn mkdir(path string) {
$if windows {
path = path.replace('/', '\\')
C.CreateDirectory(path.cstr(), 0)
C.CreateDirectory(path.str, 0)
}
$else {
C.mkdir(path.cstr(), 511)// S_IRWXU | S_IRWXG | S_IRWXO
C.mkdir(path.str, 511)// S_IRWXU | S_IRWXG | S_IRWXO
}
}
// rm removes file in `path`.
pub fn rm(path string) {
C.remove(path.cstr())
// C.unlink(path.cstr())
C.remove(path.str)
// C.unlink(path.str)
}
// rmdir removes a specified directory.
pub fn rmdir(path string) {
$if !windows {
C.rmdir(path.cstr())
C.rmdir(path.str)
}
$else {
C.RemoveDirectoryA(path.cstr())
C.RemoveDirectoryA(path.str)
}
}
@ -594,7 +589,7 @@ pub fn is_dir(path string) bool {
}
$else {
statbuf := C.stat{}
cstr := path.cstr()
cstr := path.str
if C.stat(cstr, &statbuf) != 0 {
return false
}
@ -604,10 +599,10 @@ pub fn is_dir(path string) bool {
pub fn chdir(path string) {
$if windows {
C._chdir(path.cstr())
C._chdir(path.str)
}
$else {
C.chdir(path.cstr())
C.chdir(path.str)
}
}
@ -657,7 +652,7 @@ pub fn ls(path string) []string {
mut find_file_data := win32finddata{}
mut dir_files := []string
// We can also check if the handle is valid. but using dir_exists instead
// h_find_dir := C.FindFirstFile(path.cstr(), &find_file_data)
// h_find_dir := C.FindFirstFile(path.str, &find_file_data)
// if (INVALID_HANDLE_VALUE == h_find_dir) {
// return dir_files
// }
@ -671,7 +666,7 @@ pub fn ls(path string) []string {
path_files := '$path\\*'
// NOTE:TODO: once we have a way to convert utf16 wide character to utf8
// we should use FindFirstFileW and FindNextFileW
h_find_files := C.FindFirstFile(path_files.cstr(), &find_file_data)
h_find_files := C.FindFirstFile(path_files.str, &find_file_data)
first_filename := tos(&find_file_data.cFileName, strlen(find_file_data.cFileName))
if first_filename != '.' && first_filename != '..' {
dir_files << first_filename
@ -687,7 +682,7 @@ pub fn ls(path string) []string {
}
$else {
mut res := []string
dir := C.opendir(path.cstr())
dir := C.opendir(path.str)
if isnil(dir) {
println('ls() couldnt open dir "$path"')
print_c_errno()

View File

@ -15,7 +15,7 @@ type HANDLE voidptr
// get_file_handle retrieves the operating-system file handle that is associated with the specified file descriptor.
pub fn get_file_handle(path string) HANDLE {
mode := 'rb'
_fd := C.fopen(path.cstr(), mode.cstr())
_fd := C.fopen(path.str, mode.str)
if _fd == 0 {
return HANDLE(INVALID_HANDLE_VALUE)
}

View File

@ -29,7 +29,7 @@ fn C.PQgetvalue(voidptr, int, int) byteptr
pub fn connect(dbname, user string) DB {
conninfo := 'host=localhost user=$user dbname=$dbname'
conn:=C.PQconnectdb(conninfo.cstr())
conn:=C.PQconnectdb(conninfo.str)
status := C.PQstatus(conn)
if status != CONNECTION_OK {
error_msg := C.PQerrorMessage(conn)
@ -87,7 +87,7 @@ pub fn (db DB) q_strings(query string) []pg.Row {
}
pub fn (db DB) exec(query string) []pg.Row {
res := C.PQexec(db.conn, query.cstr())
res := C.PQexec(db.conn, query.str)
e := string(C.PQerrorMessage(db.conn))
if e != '' {
println('pg exec error:')

View File

@ -53,8 +53,8 @@ pub fn open(name string, level int, mode string) ?zip_ptr {
if mode != M_WRITE && mode != M_RONLY && mode != M_APPEND {
return error('szip: invalid provided open mode')
}
/* struct zip_t* */_p_zip := zip_ptr(C.zip_open(name.cstr(),
_nlevel, mode.cstr()))
/* struct zip_t* */_p_zip := zip_ptr(C.zip_open(name.str,
_nlevel, mode.str))
if _p_zip == zip_ptr(0) {
return error('szip: cannot open/create/append new zip archive.')
}
@ -83,7 +83,7 @@ pub fn (z mut zip_ptr) close() {
* @return the return code - 0 on success, negative number (< 0) on error.
*/
pub fn (zentry mut zip_ptr) open_entry(name string) /*?*/bool {
res := C.zip_entry_open(zentry, name.cstr())
res := C.zip_entry_open(zentry, name.str)
return res != -1
}
@ -203,7 +203,7 @@ pub fn (zentry mut zip_ptr) write_entry(data []byte) bool {
* @return the return code - 0 on success, negative number (< 0) on error.
*/
pub fn (zentry mut zip_ptr) create_entry(name string) bool {
res := C.zip_entry_fwrite(zentry, name.cstr())
res := C.zip_entry_fwrite(zentry, name.str)
return res == 0
}
@ -241,11 +241,11 @@ pub fn (zentry mut zip_ptr) read_entry() ?voidptr {
* @return the return code - 0 on success, negative number (< 0) on error.
*/
pub fn (zentry mut zip_ptr) extract_entry(path string) /*?*/bool {
if C.access(path.cstr(), 0) == -1 {
if C.access(path.str, 0) == -1 {
return false
//return error('Cannot open file for extracting, file not exists')
}
res := C.zip_entry_fread(zentry, path.cstr())
res := C.zip_entry_fread(zentry, path.str)
return res == 0
}
@ -260,11 +260,11 @@ pub fn (zentry mut zip_ptr) extract_entry(path string) /*?*/bool {
* @return the return code - 0 on success, negative number (< 0) on error.
*/
/*fn (zentry mut zip_ptr) extract(path string) bool {
if C.access(path.cstr(), 0) == -1 {
if C.access(path.str, 0) == -1 {
return false
//return error('Cannot open directory for extracting, directory not exists')
}
res := C.zip_extract(zentry, path.cstr(), 0, 0)
res := C.zip_extract(zentry, path.str, 0, 0)
return res == 0
}*/