strings,builder: reduce V cgen and parser memory consumption, fix strings.Builder leak (#12342)

pull/12341/head
Delyan Angelov 2021-10-31 12:58:55 +02:00 committed by GitHub
parent ce9f26c589
commit 6937074e7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 72 additions and 15 deletions

View File

@ -238,6 +238,11 @@ pub fn (mut a array) delete_many(i int, size int) {
vmemcpy(&byte(a.data) + i * a.element_size, &byte(old_data) + (i + size) * a.element_size, vmemcpy(&byte(a.data) + i * a.element_size, &byte(old_data) + (i + size) * a.element_size,
(a.len - i - size) * a.element_size) (a.len - i - size) * a.element_size)
} }
if a.flags.has(.noslices) {
unsafe {
free(old_data)
}
}
a.len = new_size a.len = new_size
a.cap = new_cap a.cap = new_cap
} }

View File

@ -11,7 +11,9 @@ pub type Builder = []byte
// new_builder returns a new string builder, with an initial capacity of `initial_size` // new_builder returns a new string builder, with an initial capacity of `initial_size`
pub fn new_builder(initial_size int) Builder { pub fn new_builder(initial_size int) Builder {
return Builder([]byte{cap: initial_size}) mut res := Builder([]byte{cap: initial_size})
unsafe { res.flags.set(.noslices) }
return res
} }
// write_ptr writes `len` bytes provided byteptr to the accumulated buffer // write_ptr writes `len` bytes provided byteptr to the accumulated buffer
@ -161,9 +163,8 @@ pub fn (mut b Builder) str() string {
pub fn (mut b Builder) free() { pub fn (mut b Builder) free() {
if b.data != 0 { if b.data != 0 {
unsafe { free(b.data) } unsafe { free(b.data) }
mut pd := &b.data
unsafe { unsafe {
(*pd) = voidptr(0) b.data = voidptr(0)
} }
} }
} }

View File

@ -25,6 +25,12 @@ mut:
data map[string][]string data map[string][]string
} }
pub fn new_ordered_dependency_map() OrderedDepMap {
mut res := OrderedDepMap{}
unsafe { res.keys.flags.set(.noslices) }
return res
}
pub fn (mut o OrderedDepMap) set(name string, deps []string) { pub fn (mut o OrderedDepMap) set(name string, deps []string) {
if name !in o.data { if name !in o.data {
o.keys << name o.keys << name
@ -92,8 +98,8 @@ pub fn (mut graph DepGraph) add(mod string, deps []string) {
} }
pub fn (graph &DepGraph) resolve() &DepGraph { pub fn (graph &DepGraph) resolve() &DepGraph {
mut node_names := OrderedDepMap{} mut node_names := new_ordered_dependency_map()
mut node_deps := OrderedDepMap{} mut node_deps := new_ordered_dependency_map()
for node in graph.nodes { for node in graph.nodes {
node_names.add(node.name, node.deps) node_names.add(node.name, node.deps)
node_deps.add(node.name, node.deps) node_deps.add(node.name, node.deps)

View File

@ -111,7 +111,9 @@ pub fn parse_comptime(text string, table &ast.Table, pref &pref.Preferences, sco
errors: []errors.Error{} errors: []errors.Error{}
warnings: []errors.Warning{} warnings: []errors.Warning{}
} }
return p.parse() res := p.parse()
unsafe { p.free_scanner() }
return res
} }
pub fn parse_text(text string, path string, table &ast.Table, comments_mode scanner.CommentsMode, pref &pref.Preferences) &ast.File { pub fn parse_text(text string, path string, table &ast.Table, comments_mode scanner.CommentsMode, pref &pref.Preferences) &ast.File {
@ -128,13 +130,23 @@ pub fn parse_text(text string, path string, table &ast.Table, comments_mode scan
warnings: []errors.Warning{} warnings: []errors.Warning{}
} }
p.set_path(path) p.set_path(path)
return p.parse() res := p.parse()
unsafe { p.free_scanner() }
return res
} }
[unsafe] [unsafe]
pub fn (mut p Parser) free() { pub fn (mut p Parser) free() {
unsafe { p.free_scanner() }
}
[unsafe]
pub fn (mut p Parser) free_scanner() {
unsafe { unsafe {
p.scanner.free() if p.scanner != 0 {
p.scanner.free()
p.scanner = &scanner.Scanner(0)
}
} }
} }
@ -190,7 +202,9 @@ pub fn parse_file(path string, table &ast.Table, comments_mode scanner.CommentsM
warnings: []errors.Warning{} warnings: []errors.Warning{}
} }
p.set_path(path) p.set_path(path)
return p.parse() res := p.parse()
unsafe { p.free_scanner() }
return res
} }
pub fn parse_vet_file(path string, table_ &ast.Table, pref &pref.Preferences) (&ast.File, []vet.Error) { pub fn parse_vet_file(path string, table_ &ast.Table, pref &pref.Preferences) (&ast.File, []vet.Error) {
@ -225,6 +239,7 @@ pub fn parse_vet_file(path string, table_ &ast.Table, pref &pref.Preferences) (&
} }
p.vet_errors << p.scanner.vet_errors p.vet_errors << p.scanner.vet_errors
file := p.parse() file := p.parse()
unsafe { p.free_scanner() }
return file, p.vet_errors return file, p.vet_errors
} }
@ -367,7 +382,7 @@ pub fn parse_files(paths []string, table &ast.Table, pref &pref.Preferences) []&
} }
*/ */
} }
mut files := []&ast.File{} mut files := []&ast.File{cap: paths.len}
for path in paths { for path in paths {
timers.start('parse_file $path') timers.start('parse_file $path')
files << parse_file(path, table, .skip_comments, pref) files << parse_file(path, table, .skip_comments, pref)

View File

@ -151,7 +151,11 @@ fn (mut s Scanner) init_scanner() {
[unsafe] [unsafe]
pub fn (mut s Scanner) free() { pub fn (mut s Scanner) free() {
unsafe { unsafe {
s.text.free() // NB: s.text is not freed here, because it is shared with all other util.read_file instances,
// and strings are not reference counted yet:
// s.text.free()
// .all_tokens however are not shared with anything, and can be freed:
s.all_tokens.free()
} }
} }
@ -1455,15 +1459,19 @@ pub fn verror(s string) {
util.verror('scanner error', s) util.verror('scanner error', s)
} }
// codegen allows you to generate V code, so that it can be parsed,
// checked, markused, cgen-ed etc further, just like user's V code.
pub fn (mut s Scanner) codegen(newtext string) { pub fn (mut s Scanner) codegen(newtext string) {
$if debug_codegen ? { $if debug_codegen ? {
eprintln('scanner.codegen:\n $newtext') eprintln('scanner.codegen:\n $newtext')
} }
// codegen makes sense only during normal compilation
// feeding code generated V code to vfmt or vdoc will
// cause them to output/document ephemeral stuff.
if s.comments_mode == .skip_comments { if s.comments_mode == .skip_comments {
s.all_tokens.delete_last() // remove .eof from end of .all_tokens // Calling codegen makes sense only during normal compilation, since
// feeding code generated V code to vfmt or vdoc will cause them to
// output/document ephemeral stuff.
for s.all_tokens.len > 0 && s.all_tokens.last().kind == .eof {
s.all_tokens.delete_last()
}
s.text += newtext s.text += newtext
old_tidx := s.tidx old_tidx := s.tidx
s.tidx = s.all_tokens.len s.tidx = s.all_tokens.len

View File

@ -0,0 +1,22 @@
import strings
fn main() {
mut sb := strings.new_builder(4)
sb.write_string('abcd')
sb.write_string('abcd')
sb.write_string('abcd')
sb.write_string('abcd')
sb.write_string('abcd')
sb.write_string('abcd')
sb.write_string('abcd')
sb.write_string('abcd')
sb.write_string('abcd')
sb.write_string('abcd')
sb.write_string('abcd')
sb.write_string('abcd')
sb.write_string('abcd')
sb.write_string('abcd')
sb.write_string('abcd')
sb.write_string('abcd')
println(sb.str())
}