···11# CHANGELOG
2233+## v1.2.0 - 2025/07/18
44+55+- Improved performance of escaping on the Erlang target: escaping can now be up
66+ to x2.5 faster when escaping strings with little carachters to escape, and up
77+ to x1.3 faster when escaping strings with many characters to escape.
88+39## v1.1.0 - 2025/03/29
410511- Improve performance of escaping.
+57-4
src/houdini/internal/escape_erl.gleam
···66666767 <<>> -> acc
68686969- _ -> panic as "non byte aligned string, all strings should be byte aligned"
6969+ _ ->
7070+ panic as "do_escape: non byte aligned string, all strings should be byte aligned"
7071 }
7172}
7273···126127 do_escape(rest, skip + len + 1, original, acc)
127128 }
128129129129- // If a byte doesn't need escaping we keep increasing the length of the
130130- // slice we're going to take.
130130+ // Otherwise we know that the first byte doesn't need any escape. The easy
131131+ // thing to do would be to just advance by that one byte and keep going over
132132+ // over the string. As you might notice here we're doing something a bit
133133+ // more involved: we look at the following 7 bytes, and if none of those
134134+ // needs escaping, then we skip this whole chunk of bytes entirely.
135135+ //
136136+ // This doesn't change the behaviour of the program, but it can make it a
137137+ // whole load faster to go over the entire string. Especially if there's
138138+ // fewer characters that need escaping!
139139+ //
140140+ // This idea comes from the amazing talk "Engineering json - Achieving Top
141141+ // Performance on the BEAM" by Michał Muskala at Code BEAM Europe 2024:
142142+ // https://www.youtube.com/watch?v=Z0swkSXAPBE
143143+ <<_, b, c, d, e, f, g, h, rest:bits>>
144144+ if b != 34
145145+ && b != 38
146146+ && b != 39
147147+ && b != 60
148148+ && b != 62
149149+ && c != 34
150150+ && c != 38
151151+ && c != 39
152152+ && c != 60
153153+ && c != 62
154154+ && d != 34
155155+ && d != 38
156156+ && d != 39
157157+ && d != 60
158158+ && d != 62
159159+ && e != 34
160160+ && e != 38
161161+ && e != 39
162162+ && e != 60
163163+ && e != 62
164164+ && f != 34
165165+ && f != 38
166166+ && f != 39
167167+ && f != 60
168168+ && f != 62
169169+ && g != 34
170170+ && g != 38
171171+ && g != 39
172172+ && g != 60
173173+ && g != 62
174174+ && h != 34
175175+ && h != 38
176176+ && h != 39
177177+ && h != 60
178178+ && h != 62
179179+ -> do_escape_normal(rest, skip, original, acc, len + 8)
180180+181181+ // However, if any of the following bytes needs escaping, we skip over just
182182+ // the first byte!
131183 <<_, rest:bits>> -> do_escape_normal(rest, skip, original, acc, len + 1)
132184133185 <<>> ->
···139191 _ -> <<acc:bits, slice(original, skip, len):bits>>
140192 }
141193142142- _ -> panic as "non byte aligned string, all strings should be byte aligned"
194194+ _ ->
195195+ panic as "do_escape_normal: non byte aligned string, all strings should be byte aligned"
143196 }
144197}
145198