Skip to content

Commit 5714e2c

Browse files
committed
libcore: optimize string joining routines.
This makes concat/connect/connect_slices about 20% faster, and takes `repeat` from O(n^2) to O(n), and lowers the constant factor.
1 parent afcb9e9 commit 5714e2c

File tree

1 file changed

+106
-12
lines changed

1 file changed

+106
-12
lines changed

src/libcore/str.rs

Lines changed: 106 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -241,38 +241,132 @@ pub fn append(lhs: ~str, rhs: &str) -> ~str {
241241

242242
/// Concatenate a vector of strings
243243
pub fn concat(v: &[~str]) -> ~str {
244-
let mut s: ~str = ~"";
245-
for vec::each(v) |ss| {
246-
push_str(&mut s, *ss);
244+
if v.is_empty() { return ~""; }
245+
246+
let mut len = 0;
247+
for v.each |ss| {
248+
len += ss.len();
249+
}
250+
let mut s = ~"";
251+
252+
reserve(&mut s, len);
253+
254+
unsafe {
255+
do as_buf(s) |buf, _len| {
256+
let mut buf = ::cast::transmute_mut_unsafe(buf);
257+
for v.each |ss| {
258+
do as_buf(*ss) |ssbuf, sslen| {
259+
let sslen = sslen - 1;
260+
ptr::copy_memory(buf, ssbuf, sslen);
261+
buf = buf.offset(sslen);
262+
}
263+
}
264+
}
265+
raw::set_len(&mut s, len);
247266
}
248267
s
249268
}
250269

251270
/// Concatenate a vector of strings, placing a given separator between each
252271
pub fn connect(v: &[~str], sep: &str) -> ~str {
272+
if v.is_empty() { return ~""; }
273+
274+
// concat is faster
275+
if sep.is_empty() { return concat(v); }
276+
277+
// this is wrong without the guarantee that v is non-empty
278+
let mut len = sep.len() * (v.len() - 1);
279+
for v.each |ss| {
280+
len += ss.len();
281+
}
253282
let mut s = ~"", first = true;
254-
for vec::each(v) |ss| {
255-
if first { first = false; } else { push_str(&mut s, sep); }
256-
push_str(&mut s, *ss);
283+
284+
reserve(&mut s, len);
285+
286+
unsafe {
287+
do as_buf(s) |buf, _len| {
288+
do as_buf(sep) |sepbuf, seplen| {
289+
let seplen = seplen - 1;
290+
let mut buf = ::cast::transmute_mut_unsafe(buf);
291+
for v.each |ss| {
292+
do as_buf(*ss) |ssbuf, sslen| {
293+
let sslen = sslen - 1;
294+
if first {
295+
first = false;
296+
} else {
297+
ptr::copy_memory(buf, sepbuf, seplen);
298+
buf = buf.offset(seplen);
299+
}
300+
ptr::copy_memory(buf, ssbuf, sslen);
301+
buf = buf.offset(sslen);
302+
}
303+
}
304+
}
305+
}
306+
raw::set_len(&mut s, len);
257307
}
258308
s
259309
}
260310

261311
/// Concatenate a vector of strings, placing a given separator between each
262312
pub fn connect_slices(v: &[&str], sep: &str) -> ~str {
313+
if v.is_empty() { return ~""; }
314+
315+
// this is wrong without the guarantee that v is non-empty
316+
let mut len = sep.len() * (v.len() - 1);
317+
for v.each |ss| {
318+
len += ss.len();
319+
}
263320
let mut s = ~"", first = true;
264-
for vec::each(v) |ss| {
265-
if first { first = false; } else { push_str(&mut s, sep); }
266-
push_str(&mut s, *ss);
321+
322+
reserve(&mut s, len);
323+
324+
unsafe {
325+
do as_buf(s) |buf, _len| {
326+
do as_buf(sep) |sepbuf, seplen| {
327+
let seplen = seplen - 1;
328+
let mut buf = ::cast::transmute_mut_unsafe(buf);
329+
for vec::each(v) |ss| {
330+
do as_buf(*ss) |ssbuf, sslen| {
331+
let sslen = sslen - 1;
332+
if first {
333+
first = false;
334+
} else if seplen > 0 {
335+
ptr::copy_memory(buf, sepbuf, seplen);
336+
buf = buf.offset(seplen);
337+
}
338+
ptr::copy_memory(buf, ssbuf, sslen);
339+
buf = buf.offset(sslen);
340+
}
341+
}
342+
}
343+
}
344+
raw::set_len(&mut s, len);
267345
}
268346
s
269347
}
270348

271349
/// Given a string, make a new string with repeated copies of it
272350
pub fn repeat(ss: &str, nn: uint) -> ~str {
273-
let mut acc = ~"";
274-
for nn.times { acc += ss; }
275-
acc
351+
do as_buf(ss) |buf, len| {
352+
let mut ret = ~"";
353+
// ignore the NULL terminator
354+
let len = len - 1;
355+
reserve(&mut ret, nn * len);
356+
357+
unsafe {
358+
do as_buf(ret) |rbuf, _len| {
359+
let mut rbuf = ::cast::transmute_mut_unsafe(rbuf);
360+
361+
for nn.times {
362+
ptr::copy_memory(rbuf, buf, len);
363+
rbuf = rbuf.offset(len);
364+
}
365+
}
366+
raw::set_len(&mut ret, nn * len);
367+
}
368+
ret
369+
}
276370
}
277371

278372
/*

0 commit comments

Comments
 (0)