Skip to content

Commit

Permalink
Merge branch 'master' into v_coverage
Browse files Browse the repository at this point in the history
* master:
  cgen: fix array fixed initialization on struct from call (vlang#21568)
  testing: implement a separate `-show-asserts` option, for cleaner test output (`-stats` still works, and still shows both the compilation stats and the asserts) (vlang#21578)
  toml: fix `@[toml: ]`, support `@[skip]` (vlang#21571)
  os: fix debugger_present() for non Windows OSes (vlang#21573)
  builtin: simplify splint_nth methods (vlang#21563)
  net.http: change default http.Server listening address to :9009, to avoid conflicts with tools, that start their own http servers on 8080 like bytehound (vlang#21570)
  os: make minior improvement to C function semantics and related code (vlang#21565)
  io: cleanup prefix_and_suffix/1 util function (vlang#21562)
  os: remove mut declarions for unchanged vars in `os_nix.c.v` (vlang#21564)
  builtin: reduce allocations in s.index_kmp/1 and s.replace/2 (vlang#21561)
  ci: shorten path used for unix domain socket tests (to fit in Windows path limits)
  • Loading branch information
spytheman committed May 27, 2024
2 parents a7c0570 + 6b2d527 commit 2f76f7b
Show file tree
Hide file tree
Showing 18 changed files with 374 additions and 232 deletions.
5 changes: 5 additions & 0 deletions cmd/tools/modules/testing/common.v
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ pub mut:
build_tools bool // builds only executables in cmd/tools; used by `v build-tools'
silent_mode bool
show_stats bool
show_asserts bool
progress_mode bool
root_relative bool // used by CI runs, so that the output is stable everywhere
nmessages chan LogMessage // many publishers, single consumer/printer
Expand Down Expand Up @@ -314,6 +315,7 @@ pub fn new_test_session(_vargs string, will_compile bool) TestSession {
skip_files: skip_files
fail_fast: testing.fail_fast
show_stats: '-stats' in vargs.split(' ')
show_asserts: '-show-asserts' in vargs.split(' ')
vargs: vargs
vtmp_dir: new_vtmp_dir
hash: hash
Expand Down Expand Up @@ -658,6 +660,9 @@ fn worker_trunner(mut p pool.PoolProcessor, idx int, thread_id int) voidptr {
mut r := os.execute(run_cmd)
cmd_duration = d_cmd.elapsed()
ts.append_message_with_duration(.cmd_end, r.output, cmd_duration, mtc)
if ts.show_asserts && r.exit_code == 0 {
println(r.output.split_into_lines().filter(it.contains(' assert')).join('\n'))
}
if r.exit_code != 0 {
mut details := get_test_details(file)
mut trimmed_output := r.output.trim_space()
Expand Down
165 changes: 75 additions & 90 deletions vlib/builtin/string.v
Original file line number Diff line number Diff line change
Expand Up @@ -345,62 +345,64 @@ pub fn (s string) replace_once(rep string, with string) string {
return s.substr(0, idx) + with + s.substr(idx + rep.len, s.len)
}

const replace_stack_buffer_size = 10
// replace replaces all occurrences of `rep` with the string passed in `with`.
@[direct_array_access]
@[direct_array_access; manualfree]
pub fn (s string) replace(rep string, with string) string {
if s.len == 0 || rep.len == 0 || rep.len > s.len {
return s.clone()
}
if !s.contains(rep) {
return s.clone()
}
// TODO: PERF Allocating ints is expensive. Should be a stack array
// Get locations of all reps within this string
mut idxs := []int{cap: s.len / rep.len}
mut pidxs_len := 0
pidxs_cap := s.len / rep.len
mut stack_idxs := [replace_stack_buffer_size]int{}
mut pidxs := unsafe { &stack_idxs[0] }
if pidxs_cap > replace_stack_buffer_size {
pidxs = unsafe { &int(malloc(sizeof(int) * pidxs_cap)) }
}
defer {
unsafe { idxs.free() }
if pidxs_cap > replace_stack_buffer_size {
unsafe { free(pidxs) }
}
}
mut idx := 0
for {
idx = s.index_after(rep, idx)
if idx == -1 {
break
}
idxs << idx
unsafe {
pidxs[pidxs_len] = idx
pidxs_len++
}
idx += rep.len
}
// Dont change the string if there's nothing to replace
if idxs.len == 0 {
if pidxs_len == 0 {
return s.clone()
}
// 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)
new_len := s.len + pidxs_len * (with.len - rep.len)
mut b := unsafe { malloc_noscan(new_len + 1) } // add space for the null byte at the end
// Fill the new string
mut b_i := 0
mut s_idx := 0
for _, rep_pos in idxs {
for i in s_idx .. rep_pos { // copy everything up to piece being replaced
unsafe {
b[b_i] = s[i]
}
b_i++
}
for j in 0 .. pidxs_len {
rep_pos := unsafe { pidxs[j] }
// copy everything up to piece being replaced
before_len := rep_pos - s_idx
unsafe { vmemcpy(&b[b_i], &s[s_idx], before_len) }
b_i += before_len
s_idx = rep_pos + rep.len // move string index past replacement
for i in 0 .. with.len { // copy replacement piece
unsafe {
b[b_i] = with[i]
}
b_i++
}
// copy replacement piece
unsafe { vmemcpy(&b[b_i], &with[0], with.len) }
b_i += with.len
}
if s_idx < s.len { // if any original after last replacement, copy it
for i in s_idx .. s.len {
unsafe {
b[b_i] = s[i]
}
b_i++
}
if s_idx < s.len {
// if any original after last replacement, copy it
unsafe { vmemcpy(&b[b_i], &s[s_idx], s.len - s_idx) }
}
unsafe {
b[new_len] = 0
Expand Down Expand Up @@ -445,7 +447,7 @@ pub fn (s string) replace_each(vals []string) string {
// The string already found is set to `/del`, to avoid duplicate searches.
for i in 0 .. rep.len {
unsafe {
s_.str[idx + i] = 127
s_.str[idx + i] = 0
}
}
// We need to remember both the position in the string,
Expand Down Expand Up @@ -920,71 +922,54 @@ pub fn (s string) rsplit_once(delim string) ?(string, string) {
@[direct_array_access]
pub fn (s string) split_nth(delim string, nth int) []string {
mut res := []string{}
mut i := 0

match delim.len {
0 {
i = 1
for ch in s {
if nth > 0 && i >= nth {
res << s[i - 1..]
for i, ch in s {
if nth > 0 && res.len == nth - 1 {
res << s[i..]
break
}
res << ch.ascii_str()
i++
}
return res
}
1 {
mut start := 0
delim_byte := delim[0]

for i < s.len {
if s[i] == delim_byte {
was_last := nth > 0 && res.len == nth - 1
if was_last {
mut start := 0
for i, ch in s {
if ch == delim_byte {
if nth > 0 && res.len == nth - 1 {
break
}
val := s.substr(start, i)
res << val
start = i + delim.len
i = start
} else {
i++
res << s.substr(start, i)
start = i + 1
}
}

// Then the remaining right part of the string
if nth < 1 || res.len < nth {
res << s[start..]
}
return res
}
else {
mut start := 0
// Take the left part for each delimiter occurrence
for i <= s.len {
is_delim := i + delim.len <= s.len && s.substr(i, i + delim.len) == delim
if is_delim {
was_last := nth > 0 && res.len == nth - 1
if was_last {
// Add up to `nth` segments left of every occurrence of the delimiter.
for i := 0; i + delim.len <= s.len; i++ {
if unsafe { s.substr_unsafe(i, i + delim.len) } == delim {
if nth > 0 && res.len == nth - 1 {
break
}
val := s.substr(start, i)
res << val
start = i + delim.len
i = start
} else {
i++
res << s.substr(start, i)
i += delim.len
start = i
}
}
// Then the remaining right part of the string
// Then add the remaining part of the string as the last segment.
if nth < 1 || res.len < nth {
res << s[start..]
}
return res
}
}

return res
}

// rsplit_nth splits the string based on the passed `delim` substring in revese order.
Expand All @@ -994,65 +979,53 @@ pub fn (s string) split_nth(delim string, nth int) []string {
@[direct_array_access]
pub fn (s string) rsplit_nth(delim string, nth int) []string {
mut res := []string{}
mut i := s.len - 1

match delim.len {
0 {
for i >= 0 {
for i := s.len - 1; i >= 0; i-- {
if nth > 0 && res.len == nth - 1 {
res << s[..i + 1]
break
}
res << s[i].ascii_str()
i--
}
return res
}
1 {
mut rbound := s.len
delim_byte := delim[0]

for i >= 0 {
mut rbound := s.len
for i := s.len - 1; i >= 0; i-- {
if s[i] == delim_byte {
if nth > 0 && res.len == nth - 1 {
break
}
res << s[i + 1..rbound]
rbound = i
i--
} else {
i--
}
}

if nth < 1 || res.len < nth {
res << s[..rbound]
}
return res
}
else {
mut rbound := s.len

for i >= 0 {
for i := s.len - 1; i >= 0; i-- {
is_delim := i - delim.len >= 0 && s[i - delim.len..i] == delim
if is_delim {
if nth > 0 && res.len == nth - 1 {
break
}
res << s[i..rbound]
rbound = i - delim.len
i -= delim.len
} else {
i--
rbound = i
}
}

if nth < 1 || res.len < nth {
res << s[..rbound]
}
return res
}
}

return res
}

// split_into_lines splits the string by newline characters.
Expand Down Expand Up @@ -1245,30 +1218,42 @@ pub fn (s string) last_index(needle string) ?int {
return idx
}

// index_kmp does KMP search.
const kmp_stack_buffer_size = 20

// index_kmp does KMP search inside the string `s` for the needle `p`.
// It returns the first found index where the string `p` is found.
// It returns -1, when the needle `p` is not present in `s`.
@[direct_array_access; manualfree]
fn (s string) index_kmp(p string) int {
if p.len > s.len {
return -1
}
mut prefix := []int{len: p.len}
mut stack_prefixes := [kmp_stack_buffer_size]int{}
mut p_prefixes := unsafe { &stack_prefixes[0] }
if p.len > kmp_stack_buffer_size {
p_prefixes = unsafe { &int(vcalloc(p.len * sizeof(int))) }
}
defer {
unsafe { prefix.free() }
if p.len > kmp_stack_buffer_size {
unsafe { free(p_prefixes) }
}
}
mut j := 0
for i := 1; i < p.len; i++ {
for unsafe { p.str[j] != p.str[i] } && j > 0 {
j = prefix[j - 1]
j = unsafe { p_prefixes[j - 1] }
}
if unsafe { p.str[j] == p.str[i] } {
j++
}
prefix[i] = j
unsafe {
p_prefixes[i] = j
}
}
j = 0
for i in 0 .. s.len {
for unsafe { p.str[j] != s.str[i] } && j > 0 {
j = prefix[j - 1]
j = unsafe { p_prefixes[j - 1] }
}
if unsafe { p.str[j] == s.str[i] } {
j++
Expand Down
13 changes: 2 additions & 11 deletions vlib/io/util/util.v
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,9 @@ fn random_number() string {
}

fn prefix_and_suffix(pattern string) !(string, string) {
mut pat := pattern
if pat.contains(os.path_separator) {
if pattern.contains(os.path_separator) {
return error('pattern cannot contain path separators (${os.path_separator}).')
}
pos := pat.index_u8_last(`*`)
mut prefix := ''
mut suffix := ''
if pos != -1 {
prefix = pat.substr(0, pos)
suffix = pat.substr(pos + 1, pat.len)
} else {
prefix = pat
}
prefix, suffix := pattern.rsplit_once('*') or { pattern, '' }
return prefix, suffix
}
8 changes: 5 additions & 3 deletions vlib/net/http/server.v
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ mut:
handle(Request) Response
}

pub const default_server_port = 9009

pub struct Server {
mut:
state ServerStatus = .closed
pub mut:
addr string = ':8080' // change to ':8080' when port is removed
port int = 8080 @[deprecated: 'use addr']
addr string = ':${http.default_server_port}'
port int = http.default_server_port @[deprecated: 'use addr']
handler Handler = DebugHandler{}
read_timeout time.Duration = 30 * time.second
write_timeout time.Duration = 30 * time.second
Expand All @@ -53,7 +55,7 @@ pub fn (mut s Server) listen_and_serve() {

// remove when s.port is removed
addr := s.addr.split(':')
if addr.len > 1 && s.port != 8080 {
if addr.len > 1 && s.port != http.default_server_port {
s.addr = '${addr[0]}:${s.port}'
}

Expand Down
2 changes: 1 addition & 1 deletion vlib/net/unix/unix_test.v
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os
import net.unix

const tfolder = os.join_path(os.vtmp_dir(), 'unix_test')
const tfolder = os.join_path(os.temp_dir(), 'unix_${os.getpid()}')
const socket_path = os.join_path(tfolder, 'v_unix.sock')

fn testsuite_begin() {
Expand Down
Loading

0 comments on commit 2f76f7b

Please sign in to comment.