From 1e092ec62cf591e57f0b0bfb80b4484dd90c9c8c Mon Sep 17 00:00:00 2001
From: Charlie Stanton <charlie@shtanton.xyz>
Date: Mon, 24 Apr 2023 19:02:23 +0100
Subject: Improves performance of reading JSON strings

---
 walk/walk.go | 81 +++++++++++++++++++++++++++++-------------------------------
 1 file changed, 39 insertions(+), 42 deletions(-)

diff --git a/walk/walk.go b/walk/walk.go
index 008713b..0719d1c 100644
--- a/walk/walk.go
+++ b/walk/walk.go
@@ -228,7 +228,7 @@ type JSONIn struct {
 
 func NewJSONIn(reader *bufio.Reader) JSONIn {
 	return JSONIn {
-		path: nil,
+		path: make([]Atom, 0, 256),
 		reader: reader,
 		structure: []JSONInStructure{JSONInRoot},
 	}
@@ -297,6 +297,41 @@ func (in *JSONIn) require(criterion rune) {
 	}
 }
 
+func (in *JSONIn) readString(out []Atom) []Atom {
+	// TODO: improve
+	out = append(out, NewAtomStringTerminal())
+	for {
+		r, _, err := in.reader.ReadRune()
+		if err != nil {
+			panic("Missing closing terminal in string input: " + err.Error())
+		}
+		if r == '"' {
+			break
+		}
+		if r == '\\' {
+			r, _, err = in.reader.ReadRune()
+			if err != nil {
+				panic("Missing rune after \\")
+			}
+			if len(out) == cap(out) {
+				newOut := make([]Atom, len(out), cap(out) * 2)
+				copy(newOut, out)
+				out = newOut
+			}
+			out = append(out, NewAtomStringRune(r))
+			continue
+		}
+		if len(out) == cap(out) {
+			newOut := make([]Atom, len(out), cap(out) * 2)
+			copy(newOut, out)
+			out = newOut
+		}
+		out = append(out, NewAtomStringRune(r))
+	}
+	out = append(out, NewAtomStringTerminal())
+	return out
+}
+
 func (in *JSONIn) Read() (WalkItem, error) {
 	item, err := in.read()
 	if err != nil {
@@ -331,26 +366,7 @@ func (in *JSONIn) read() (WalkItem, error) {
 			if r != '"' {
 				panic("Expected key, found something else")
 			}
-			in.path = append(in.path, NewAtomStringTerminal())
-			for {
-				r, _, err = in.reader.ReadRune()
-				if err != nil {
-					return WalkItem {}, err
-				}
-				if r == '"' {
-					break
-				}
-				if r == '\\' {
-					r, _, err = in.reader.ReadRune()
-					if err != nil {
-						panic("Missing rune after \\")
-					}
-					in.path = append(in.path, NewAtomStringRune(r))
-					continue
-				}
-				in.path = append(in.path, NewAtomStringRune(r))
-			}
-			in.path = append(in.path, NewAtomStringTerminal())
+			in.path = in.readString(in.path)
 			r, err = in.nextNonWsRune()
 			if err != nil {
 				panic("Expected : got: " + err.Error())
@@ -432,27 +448,8 @@ func (in *JSONIn) read() (WalkItem, error) {
 				Path: in.path,
 			}, nil
 		case '"':
-			value := make([]Atom, 0, 2)
-			value = append(value, NewAtomStringTerminal())
-			for {
-				r, _, err = in.reader.ReadRune()
-				if err != nil {
-					panic("Missing closing terminal in string input: " + err.Error())
-				}
-				if r == '"' {
-					break
-				}
-				if r == '\\' {
-					r, _, err = in.reader.ReadRune()
-					if err != nil {
-						panic("Missing rune after \\")
-					}
-					value = append(value, NewAtomStringRune(r))
-					continue
-				}
-				value = append(value, NewAtomStringRune(r))
-			}
-			value = append(value, NewAtomStringTerminal())
+			value := make([]Atom, 0, 64)
+			value = in.readString(value)
 			in.structure = append(in.structure, JSONInValueEnd)
 			return WalkItem {
 				Value: value,
-- 
cgit v1.2.3