1
0
mirror of https://github.com/golang/go synced 2024-11-24 17:00:01 -07:00

runtime: append([]byte, string...)

Fixes #2274

R=rsc, gri, dsymonds, bradfitz, lvd
CC=golang-dev
https://golang.org/cl/5149045
This commit is contained in:
Luuk van Dijk 2011-10-12 15:59:23 +02:00
parent a0d335c31d
commit 77fac21e82
7 changed files with 95 additions and 56 deletions

View File

@ -1,5 +1,5 @@
<!-- title The Go Programming Language Specification --> <!-- title The Go Programming Language Specification -->
<!-- subtitle Version of August 31, 2011 --> <!-- subtitle Version of September 29, 2011 -->
<!-- <!--
TODO TODO
@ -4658,6 +4658,10 @@ The values <code>x</code> are passed to a parameter of type <code>...T</code>
where <code>T</code> is the <a href="#Slice_types">element type</a> of where <code>T</code> is the <a href="#Slice_types">element type</a> of
<code>S</code> and the respective <code>S</code> and the respective
<a href="#Passing_arguments_to_..._parameters">parameter passing rules</a> apply. <a href="#Passing_arguments_to_..._parameters">parameter passing rules</a> apply.
As a special case, <code>append</code> also accepts a first argument
assignable to type <code>[]byte</code> with a second argument of
string type followed by <code>...</code>. This form appends the
bytes of the string.
</p> </p>
<pre class="grammar"> <pre class="grammar">
@ -4668,7 +4672,7 @@ append(s S, x ...T) S // T is the element type of S
If the capacity of <code>s</code> is not large enough to fit the additional If the capacity of <code>s</code> is not large enough to fit the additional
values, <code>append</code> allocates a new, sufficiently large slice that fits values, <code>append</code> allocates a new, sufficiently large slice that fits
both the existing slice elements and the additional values. Thus, the returned both the existing slice elements and the additional values. Thus, the returned
slice may refer to a different underlying array. slice may refer to a different underlying array.
</p> </p>
<pre> <pre>
@ -4679,6 +4683,9 @@ s3 := append(s2, s0...) // append a slice s3 == []int{0, 0, 2, 3
var t []interface{} var t []interface{}
t = append(t, 42, 3.1415, "foo") t == []interface{}{42, 3.1415, "foo"} t = append(t, 42, 3.1415, "foo") t == []interface{}{42, 3.1415, "foo"}
var b []byte
b = append(b, "bar"...) // append string contents b == []byte{'b', 'a', 'r' }
</pre> </pre>
<p> <p>

View File

@ -25,6 +25,7 @@ char *runtimeimport =
"func @\"\".concatstring ()\n" "func @\"\".concatstring ()\n"
"func @\"\".append ()\n" "func @\"\".append ()\n"
"func @\"\".appendslice (typ *uint8, x any, y []any) any\n" "func @\"\".appendslice (typ *uint8, x any, y []any) any\n"
"func @\"\".appendstr (typ *uint8, x []uint8, y string) []uint8\n"
"func @\"\".cmpstring (? string, ? string) int\n" "func @\"\".cmpstring (? string, ? string) int\n"
"func @\"\".slicestring (? string, ? int, ? int) string\n" "func @\"\".slicestring (? string, ? int, ? int) string\n"
"func @\"\".slicestring1 (? string, ? int) string\n" "func @\"\".slicestring1 (? string, ? int) string\n"

View File

@ -40,6 +40,7 @@ func concatstring()
// filled in by compiler: Type*, int n, Slice, ... // filled in by compiler: Type*, int n, Slice, ...
func append() func append()
func appendslice(typ *byte, x any, y []any) any func appendslice(typ *byte, x any, y []any) any
func appendstr(typ *byte, x []byte, y string) []byte
func cmpstring(string, string) int func cmpstring(string, string) int
func slicestring(string, int, int) string func slicestring(string, int, int) string

View File

@ -1008,6 +1008,10 @@ reswitch:
yyerror("too many arguments to append"); yyerror("too many arguments to append");
goto error; goto error;
} }
if(istype(t->type, TUINT8) && istype(args->next->n->type, TSTRING)) {
defaultlit(&args->next->n, types[TSTRING]);
goto ret;
}
args->next->n = assignconv(args->next->n, t->orig, "append"); args->next->n = assignconv(args->next->n, t->orig, "append");
goto ret; goto ret;
} }
@ -1039,7 +1043,7 @@ reswitch:
goto error; goto error;
defaultlit(&n->left, T); defaultlit(&n->left, T);
defaultlit(&n->right, T); defaultlit(&n->right, T);
// copy([]byte, string) // copy([]byte, string)
if(isslice(n->left->type) && n->right->type->etype == TSTRING) { if(isslice(n->left->type) && n->right->type->etype == TSTRING) {
if(n->left->type->type == types[TUINT8]) if(n->left->type->type == types[TUINT8])
@ -1047,7 +1051,7 @@ reswitch:
yyerror("arguments to copy have different element types: %lT and string", n->left->type); yyerror("arguments to copy have different element types: %lT and string", n->left->type);
goto error; goto error;
} }
if(!isslice(n->left->type) || !isslice(n->right->type)) { if(!isslice(n->left->type) || !isslice(n->right->type)) {
if(!isslice(n->left->type) && !isslice(n->right->type)) if(!isslice(n->left->type) && !isslice(n->right->type))
yyerror("arguments to copy must be slices; have %lT, %lT", n->left->type, n->right->type); yyerror("arguments to copy must be slices; have %lT, %lT", n->left->type, n->right->type);

View File

@ -121,7 +121,7 @@ static int
paramoutheap(Node *fn) paramoutheap(Node *fn)
{ {
NodeList *l; NodeList *l;
for(l=fn->dcl; l; l=l->next) { for(l=fn->dcl; l; l=l->next) {
switch(l->n->class) { switch(l->n->class) {
case PPARAMOUT|PHEAP: case PPARAMOUT|PHEAP:
@ -409,7 +409,7 @@ walkexpr(Node **np, NodeList **init)
case OLEN: case OLEN:
case OCAP: case OCAP:
walkexpr(&n->left, init); walkexpr(&n->left, init);
// replace len(*[10]int) with 10. // replace len(*[10]int) with 10.
// delayed until now to preserve side effects. // delayed until now to preserve side effects.
t = n->left->type; t = n->left->type;
@ -421,7 +421,7 @@ walkexpr(Node **np, NodeList **init)
n->typecheck = 1; n->typecheck = 1;
} }
goto ret; goto ret;
case OLSH: case OLSH:
case ORSH: case ORSH:
case OAND: case OAND:
@ -440,7 +440,7 @@ walkexpr(Node **np, NodeList **init)
walkexpr(&n->left, init); walkexpr(&n->left, init);
walkexpr(&n->right, init); walkexpr(&n->right, init);
goto ret; goto ret;
case OANDAND: case OANDAND:
case OOROR: case OOROR:
walkexpr(&n->left, init); walkexpr(&n->left, init);
@ -553,7 +553,7 @@ walkexpr(Node **np, NodeList **init)
walkexprlistsafe(n->list, init); walkexprlistsafe(n->list, init);
walkexpr(&r, init); walkexpr(&r, init);
l = n->list->n; l = n->list->n;
// all the really hard stuff - explicit function calls and so on - // all the really hard stuff - explicit function calls and so on -
// is gone, but map assignments remain. // is gone, but map assignments remain.
// if there are map assignments here, assign via // if there are map assignments here, assign via
@ -648,7 +648,7 @@ walkexpr(Node **np, NodeList **init)
if(n->op == ODOTTYPE2) if(n->op == ODOTTYPE2)
*p++ = '2'; *p++ = '2';
*p = '\0'; *p = '\0';
fn = syslook(buf, 1); fn = syslook(buf, 1);
ll = list1(typename(n->type)); ll = list1(typename(n->type));
ll = list(ll, n->left); ll = list(ll, n->left);
@ -679,7 +679,7 @@ walkexpr(Node **np, NodeList **init)
else else
*p++ = 'I'; *p++ = 'I';
*p = '\0'; *p = '\0';
fn = syslook(buf, 1); fn = syslook(buf, 1);
ll = nil; ll = nil;
if(!isinter(n->left->type)) if(!isinter(n->left->type))
@ -894,7 +894,7 @@ walkexpr(Node **np, NodeList **init)
} }
if(v1 >= 0 && v2 >= 0 && v1 > v2) if(v1 >= 0 && v2 >= 0 && v1 > v2)
yyerror("inverted slice range"); yyerror("inverted slice range");
if(n->op == OSLICEARR) if(n->op == OSLICEARR)
goto slicearray; goto slicearray;
@ -925,7 +925,7 @@ walkexpr(Node **np, NodeList **init)
l, l,
nodintconst(t->type->width)); nodintconst(t->type->width));
} }
n->etype = et; // preserve no-typecheck flag from OSLICE to the slice* call. n->etype = et; // preserve no-typecheck flag from OSLICE to the slice* call.
goto ret; goto ret;
slicearray: slicearray:
@ -1054,10 +1054,14 @@ walkexpr(Node **np, NodeList **init)
l); l);
} }
goto ret; goto ret;
case OAPPEND: case OAPPEND:
if(n->isddd) if(n->isddd) {
n = appendslice(n, init); if(istype(n->type->type, TUINT8) && istype(n->list->next->n->type, TSTRING))
n = mkcall("appendstr", n->type, init, typename(n->type), n->list->n, n->list->next->n);
else
n = appendslice(n, init);
}
else else
n = append(n, init); n = append(n, init);
goto ret; goto ret;
@ -1319,7 +1323,7 @@ mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init, int
{ {
Node *a, *n; Node *a, *n;
Type *tslice; Type *tslice;
tslice = typ(TARRAY); tslice = typ(TARRAY);
tslice->type = l->type->type; tslice->type = l->type->type;
tslice->bound = -1; tslice->bound = -1;
@ -1413,7 +1417,7 @@ ascompatte(int op, Node *call, int isddd, Type **nl, NodeList *lr, int fp, NodeL
if(lr) if(lr)
r = lr->n; r = lr->n;
nn = nil; nn = nil;
// f(g()) where g has multiple return values // f(g()) where g has multiple return values
if(r != N && lr->next == nil && r->type->etype == TSTRUCT && r->type->funarg) { if(r != N && lr->next == nil && r->type->etype == TSTRUCT && r->type->funarg) {
// optimization - can do block copy // optimization - can do block copy
@ -1423,7 +1427,7 @@ ascompatte(int op, Node *call, int isddd, Type **nl, NodeList *lr, int fp, NodeL
nn = list1(convas(nod(OAS, a, r), init)); nn = list1(convas(nod(OAS, a, r), init));
goto ret; goto ret;
} }
// conversions involved. // conversions involved.
// copy into temporaries. // copy into temporaries.
alist = nil; alist = nil;
@ -1714,10 +1718,10 @@ convas(Node *n, NodeList **init)
n->left->left, n->left->right, n->right); n->left->left, n->left->right, n->right);
goto out; goto out;
} }
if(eqtype(lt, rt)) if(eqtype(lt, rt))
goto out; goto out;
n->right = assignconv(n->right, lt, "assignment"); n->right = assignconv(n->right, lt, "assignment");
walkexpr(&n->right, init); walkexpr(&n->right, init);
@ -1952,7 +1956,7 @@ heapmoves(void)
{ {
NodeList *nn; NodeList *nn;
int32 lno; int32 lno;
lno = lineno; lno = lineno;
lineno = curfn->lineno; lineno = curfn->lineno;
nn = paramstoheap(getthis(curfn->type), 0); nn = paramstoheap(getthis(curfn->type), 0);
@ -2060,7 +2064,7 @@ addstr(Node *n, NodeList **init)
Node *r, *cat, *typstr; Node *r, *cat, *typstr;
NodeList *in, *args; NodeList *in, *args;
int i, count; int i, count;
count = 0; count = 0;
for(r=n; r->op == OADDSTR; r=r->left) for(r=n; r->op == OADDSTR; r=r->left)
count++; // r->right count++; // r->right
@ -2089,7 +2093,7 @@ addstr(Node *n, NodeList **init)
typecheck(&r, Erv); typecheck(&r, Erv);
walkexpr(&r, init); walkexpr(&r, init);
r->type = n->type; r->type = n->type;
return r; return r;
} }
@ -2097,7 +2101,7 @@ static Node*
appendslice(Node *n, NodeList **init) appendslice(Node *n, NodeList **init)
{ {
Node *f; Node *f;
f = syslook("appendslice", 1); f = syslook("appendslice", 1);
argtype(f, n->type); argtype(f, n->type);
argtype(f, n->type->type); argtype(f, n->type->type);
@ -2111,7 +2115,7 @@ appendslice(Node *n, NodeList **init)
// s := src // s := src
// const argc = len(args) - 1 // const argc = len(args) - 1
// if cap(s) - len(s) < argc { // if cap(s) - len(s) < argc {
// s = growslice(s, argc) // s = growslice(s, argc)
// } // }
// n := len(s) // n := len(s)
// s = s[:n+argc] // s = s[:n+argc]
@ -2140,13 +2144,13 @@ append(Node *n, NodeList **init)
ns = temp(nsrc->type); ns = temp(nsrc->type);
l = list(l, nod(OAS, ns, nsrc)); // s = src l = list(l, nod(OAS, ns, nsrc)); // s = src
na = nodintconst(argc); // const argc na = nodintconst(argc); // const argc
nx = nod(OIF, N, N); // if cap(s) - len(s) < argc nx = nod(OIF, N, N); // if cap(s) - len(s) < argc
nx->ntest = nod(OLT, nod(OSUB, nod(OCAP, ns, N), nod(OLEN, ns, N)), na); nx->ntest = nod(OLT, nod(OSUB, nod(OCAP, ns, N), nod(OLEN, ns, N)), na);
fn = syslook("growslice", 1); // growslice(<type>, old []T, n int64) (ret []T) fn = syslook("growslice", 1); // growslice(<type>, old []T, n int64) (ret []T)
argtype(fn, ns->type->type); // 1 old []any argtype(fn, ns->type->type); // 1 old []any
argtype(fn, ns->type->type); // 2 ret []any argtype(fn, ns->type->type); // 2 ret []any
nx->nbody = list1(nod(OAS, ns, mkcall1(fn, ns->type, &nx->ninit, nx->nbody = list1(nod(OAS, ns, mkcall1(fn, ns->type, &nx->ninit,
typename(ns->type), typename(ns->type),
@ -2155,16 +2159,16 @@ append(Node *n, NodeList **init)
l = list(l, nx); l = list(l, nx);
nn = temp(types[TINT]); nn = temp(types[TINT]);
l = list(l, nod(OAS, nn, nod(OLEN, ns, N))); // n = len(s) l = list(l, nod(OAS, nn, nod(OLEN, ns, N))); // n = len(s)
nx = nod(OSLICE, ns, nod(OKEY, N, nod(OADD, nn, na))); // ...s[:n+argc] nx = nod(OSLICE, ns, nod(OKEY, N, nod(OADD, nn, na))); // ...s[:n+argc]
nx->etype = 1; // disable bounds check nx->etype = 1; // disable bounds check
l = list(l, nod(OAS, ns, nx)); // s = s[:n+argc] l = list(l, nod(OAS, ns, nx)); // s = s[:n+argc]
for (a = n->list->next; a != nil; a = a->next) { for (a = n->list->next; a != nil; a = a->next) {
nx = nod(OINDEX, ns, nn); // s[n] ... nx = nod(OINDEX, ns, nn); // s[n] ...
nx->etype = 1; // disable bounds check nx->etype = 1; // disable bounds check
l = list(l, nod(OAS, nx, a->n)); // s[n] = arg l = list(l, nod(OAS, nx, a->n)); // s[n] = arg
if (a->next != nil) if (a->next != nil)
l = list(l, nod(OAS, nn, nod(OADD, nn, nodintconst(1)))); // n = n + 1 l = list(l, nod(OAS, nn, nod(OADD, nn, nodintconst(1)))); // n = n + 1
} }

View File

@ -11,7 +11,6 @@ static int32 debug = 0;
static void makeslice1(SliceType*, int32, int32, Slice*); static void makeslice1(SliceType*, int32, int32, Slice*);
static void growslice1(SliceType*, Slice, int32, Slice *); static void growslice1(SliceType*, Slice, int32, Slice *);
static void appendslice1(SliceType*, Slice, Slice, Slice*);
void runtime·slicecopy(Slice to, Slice fm, uintptr width, int32 ret); void runtime·slicecopy(Slice to, Slice fm, uintptr width, int32 ret);
// see also unsafe·NewArray // see also unsafe·NewArray
@ -29,13 +28,13 @@ runtime·makeslice(SliceType *t, int64 len, int64 cap, Slice ret)
if(debug) { if(debug) {
runtime·printf("makeslice(%S, %D, %D); ret=", runtime·printf("makeslice(%S, %D, %D); ret=",
*t->string, len, cap); *t->string, len, cap);
runtime·printslice(ret); runtime·printslice(ret);
} }
} }
static void static void
makeslice1(SliceType *t, int32 len, int32 cap, Slice *ret) makeslice1(SliceType *t, int32 len, int32 cap, Slice *ret)
{ {
uintptr size; uintptr size;
size = cap*t->elem->size; size = cap*t->elem->size;
@ -52,12 +51,6 @@ makeslice1(SliceType *t, int32 len, int32 cap, Slice *ret)
// appendslice(type *Type, x, y, []T) []T // appendslice(type *Type, x, y, []T) []T
void void
runtime·appendslice(SliceType *t, Slice x, Slice y, Slice ret) runtime·appendslice(SliceType *t, Slice x, Slice y, Slice ret)
{
appendslice1(t, x, y, &ret);
}
static void
appendslice1(SliceType *t, Slice x, Slice y, Slice *ret)
{ {
int32 m; int32 m;
uintptr w; uintptr w;
@ -68,15 +61,39 @@ appendslice1(SliceType *t, Slice x, Slice y, Slice *ret)
runtime·throw("append: slice overflow"); runtime·throw("append: slice overflow");
if(m > x.cap) if(m > x.cap)
growslice1(t, x, m, ret); growslice1(t, x, m, &ret);
else else
*ret = x; ret = x;
w = t->elem->size; w = t->elem->size;
runtime·memmove(ret->array + ret->len*w, y.array, y.len*w); runtime·memmove(ret.array + ret.len*w, y.array, y.len*w);
ret->len += y.len; ret.len += y.len;
FLUSH(&ret);
} }
// appendstr([]byte, string) []byte
void
runtime·appendstr(SliceType *t, Slice x, String y, Slice ret)
{
int32 m;
m = x.len+y.len;
if(m < x.len)
runtime·throw("append: slice overflow");
if(m > x.cap)
growslice1(t, x, m, &ret);
else
ret = x;
runtime·memmove(ret.array + ret.len, y.str, y.len);
ret.len += y.len;
FLUSH(&ret);
}
// growslice(type *Type, x, []T, n int64) []T // growslice(type *Type, x, []T, n int64) []T
void void
runtime·growslice(SliceType *t, Slice old, int64 n, Slice ret) runtime·growslice(SliceType *t, Slice old, int64 n, Slice ret)
@ -97,9 +114,9 @@ runtime·growslice(SliceType *t, Slice old, int64 n, Slice ret)
if(debug) { if(debug) {
runtime·printf("growslice(%S,", *t->string); runtime·printf("growslice(%S,", *t->string);
runtime·printslice(old); runtime·printslice(old);
runtime·printf(", new cap=%D) =", cap); runtime·printf(", new cap=%D) =", cap);
runtime·printslice(ret); runtime·printslice(ret);
} }
} }
@ -308,11 +325,11 @@ runtime·slicestringcopy(Slice to, String fm, int32 ret)
ret = 0; ret = 0;
goto out; goto out;
} }
ret = fm.len; ret = fm.len;
if(to.len < ret) if(to.len < ret)
ret = to.len; ret = to.len;
runtime·memmove(to.array, fm.str, ret); runtime·memmove(to.array, fm.str, ret);
out: out:

View File

@ -63,6 +63,11 @@ var tests = []struct {
{"byte i", append([]byte{0, 1, 2}, []byte{3}...), []byte{0, 1, 2, 3}}, {"byte i", append([]byte{0, 1, 2}, []byte{3}...), []byte{0, 1, 2, 3}},
{"byte j", append([]byte{0, 1, 2}, []byte{3, 4, 5}...), []byte{0, 1, 2, 3, 4, 5}}, {"byte j", append([]byte{0, 1, 2}, []byte{3, 4, 5}...), []byte{0, 1, 2, 3, 4, 5}},
{"bytestr a", append([]byte{}, "0"...), []byte("0")},
{"bytestr b", append([]byte{}, "0123"...), []byte("0123")},
{"bytestr c", append([]byte("012"), "3"...), []byte("0123")},
{"bytestr d", append([]byte("012"), "345"...), []byte("012345")},
{"int16 a", append([]int16{}), []int16{}}, {"int16 a", append([]int16{}), []int16{}},
{"int16 b", append([]int16{}, 0), []int16{0}}, {"int16 b", append([]int16{}, 0), []int16{0}},