blob: b06f4031e7b11af352241c07db559963347fcf97 [file] [log] [blame]
Rob Pikeaaa3a622010-03-20 22:32:34 -07001// Go support for Protocol Buffers - Google's data interchange format
2//
3// Copyright 2010 Google Inc. All rights reserved.
4// http://code.google.com/p/goprotobuf/
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are
8// met:
9//
10// * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12// * Redistributions in binary form must reproduce the above
13// copyright notice, this list of conditions and the following disclaimer
14// in the documentation and/or other materials provided with the
15// distribution.
16// * Neither the name of Google Inc. nor the names of its
17// contributors may be used to endorse or promote products derived from
18// this software without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32package proto
33
34// Functions for parsing the Text protocol buffer format.
David Symonds54531052011-12-08 12:00:31 +110035// TODO: message sets.
Rob Pikeaaa3a622010-03-20 22:32:34 -070036
37import (
38 "fmt"
Rob Pikeaaa3a622010-03-20 22:32:34 -070039 "reflect"
40 "strconv"
David Symonds183124e2012-03-23 13:20:23 +110041 "strings"
Rob Pikeaaa3a622010-03-20 22:32:34 -070042)
43
Rob Pikeaaa3a622010-03-20 22:32:34 -070044type ParseError struct {
45 Message string
46 Line int // 1-based line number
47 Offset int // 0-based byte offset from start of input
48}
49
Rob Pikea17fdd92011-11-02 12:43:05 -070050func (p *ParseError) Error() string {
Rob Pikeaaa3a622010-03-20 22:32:34 -070051 if p.Line == 1 {
52 // show offset only for first line
53 return fmt.Sprintf("line 1.%d: %v", p.Offset, p.Message)
54 }
55 return fmt.Sprintf("line %d: %v", p.Line, p.Message)
56}
57
58type token struct {
59 value string
60 err *ParseError
61 line int // line number
62 offset int // byte number from start of input, not start of line
63 unquoted string // the unquoted version of value, if it was a quoted string
64}
65
66func (t *token) String() string {
67 if t.err == nil {
68 return fmt.Sprintf("%q (line=%d, offset=%d)", t.value, t.line, t.offset)
69 }
70 return fmt.Sprintf("parse error: %v", t.err)
71}
72
73type textParser struct {
74 s string // remaining input
75 done bool // whether the parsing is finished (success or error)
76 backed bool // whether back() was called
77 offset, line int
78 cur token
79}
80
81func newTextParser(s string) *textParser {
82 p := new(textParser)
83 p.s = s
84 p.line = 1
85 p.cur.line = 1
86 return p
87}
88
Rob Piked6420b82011-04-13 16:37:04 -070089func (p *textParser) errorf(format string, a ...interface{}) *ParseError {
Rob Pikead7cac72010-09-29 12:29:26 -070090 pe := &ParseError{fmt.Sprintf(format, a...), p.cur.line, p.cur.offset}
Rob Pikeaaa3a622010-03-20 22:32:34 -070091 p.cur.err = pe
92 p.done = true
93 return pe
94}
95
96// Numbers and identifiers are matched by [-+._A-Za-z0-9]
97func isIdentOrNumberChar(c byte) bool {
98 switch {
99 case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z':
100 return true
101 case '0' <= c && c <= '9':
102 return true
103 }
104 switch c {
105 case '-', '+', '.', '_':
106 return true
107 }
108 return false
109}
110
111func isWhitespace(c byte) bool {
112 switch c {
113 case ' ', '\t', '\n', '\r':
114 return true
115 }
116 return false
117}
118
119func (p *textParser) skipWhitespace() {
120 i := 0
121 for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') {
122 if p.s[i] == '#' {
123 // comment; skip to end of line or input
124 for i < len(p.s) && p.s[i] != '\n' {
125 i++
126 }
127 if i == len(p.s) {
128 break
129 }
130 }
131 if p.s[i] == '\n' {
132 p.line++
133 }
134 i++
135 }
136 p.offset += i
137 p.s = p.s[i:len(p.s)]
138 if len(p.s) == 0 {
139 p.done = true
140 }
141}
142
143func (p *textParser) advance() {
144 // Skip whitespace
145 p.skipWhitespace()
146 if p.done {
147 return
148 }
149
150 // Start of non-whitespace
151 p.cur.err = nil
152 p.cur.offset, p.cur.line = p.offset, p.line
153 p.cur.unquoted = ""
154 switch p.s[0] {
David Symonds54531052011-12-08 12:00:31 +1100155 case '<', '>', '{', '}', ':', '[', ']':
Rob Pikeaaa3a622010-03-20 22:32:34 -0700156 // Single symbol
157 p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)]
David Symonds162d0032012-06-28 09:44:46 -0700158 case '"', '\'':
Rob Pikeaaa3a622010-03-20 22:32:34 -0700159 // Quoted string
160 i := 1
David Symonds162d0032012-06-28 09:44:46 -0700161 for i < len(p.s) && p.s[i] != p.s[0] && p.s[i] != '\n' {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700162 if p.s[i] == '\\' && i+1 < len(p.s) {
163 // skip escaped char
164 i++
165 }
166 i++
167 }
David Symonds162d0032012-06-28 09:44:46 -0700168 if i >= len(p.s) || p.s[i] != p.s[0] {
Rob Piked6420b82011-04-13 16:37:04 -0700169 p.errorf("unmatched quote")
Rob Pikeaaa3a622010-03-20 22:32:34 -0700170 return
171 }
David Symonds183124e2012-03-23 13:20:23 +1100172 unq, err := unquoteC(p.s[0 : i+1])
Rob Pikeaaa3a622010-03-20 22:32:34 -0700173 if err != nil {
Rob Piked6420b82011-04-13 16:37:04 -0700174 p.errorf("invalid quoted string %v", p.s[0:i+1])
Rob Pikeaaa3a622010-03-20 22:32:34 -0700175 return
176 }
177 p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)]
178 p.cur.unquoted = unq
179 default:
180 i := 0
181 for i < len(p.s) && isIdentOrNumberChar(p.s[i]) {
182 i++
183 }
184 if i == 0 {
Rob Piked6420b82011-04-13 16:37:04 -0700185 p.errorf("unexpected byte %#x", p.s[0])
Rob Pikeaaa3a622010-03-20 22:32:34 -0700186 return
187 }
188 p.cur.value, p.s = p.s[0:i], p.s[i:len(p.s)]
189 }
190 p.offset += len(p.cur.value)
191}
192
David Symonds162d0032012-06-28 09:44:46 -0700193// quoteSwap returns a single quote for a double quote, and vice versa.
194// It is intended to be used with strings.Map.
195func quoteSwap(r rune) rune {
196 switch r {
197 case '\'':
198 return '"'
199 case '"':
200 return '\''
201 }
202 return r
203}
204
David Symonds183124e2012-03-23 13:20:23 +1100205func unquoteC(s string) (string, error) {
David Symonds162d0032012-06-28 09:44:46 -0700206 // TODO: This is getting hacky. We should replace it work a self-contained parser.
207
208 // strconv.Unquote is for Go strings, but text format strings may use
209 // single *or* double quotes.
210 if s[0] == '\'' {
211 s = strings.Map(quoteSwap, s)
212 s, err := unquoteC(s)
213 s = strings.Map(quoteSwap, s)
214 return s, err
215 }
216
David Symonds183124e2012-03-23 13:20:23 +1100217 // A notable divergence between quoted string literals in Go
218 // and what is acceptable for text format protocol buffers:
219 // the former considers \' invalid, but the latter considers it valid.
220 s = strings.Replace(s, `\'`, "'", -1)
221 return strconv.Unquote(s)
222}
223
Rob Pikeaaa3a622010-03-20 22:32:34 -0700224// Back off the parser by one token. Can only be done between calls to next().
225// It makes the next advance() a no-op.
226func (p *textParser) back() { p.backed = true }
227
228// Advances the parser and returns the new current token.
229func (p *textParser) next() *token {
230 if p.backed || p.done {
231 p.backed = false
232 return &p.cur
233 }
234 p.advance()
235 if p.done {
236 p.cur.value = ""
237 } else if len(p.cur.value) > 0 && p.cur.value[0] == '"' {
238 // Look for multiple quoted strings separated by whitespace,
239 // and concatenate them.
240 cat := p.cur
241 for {
242 p.skipWhitespace()
243 if p.done || p.s[0] != '"' {
244 break
245 }
246 p.advance()
247 if p.cur.err != nil {
248 return &p.cur
249 }
250 cat.value += " " + p.cur.value
251 cat.unquoted += p.cur.unquoted
252 }
253 p.done = false // parser may have seen EOF, but we want to return cat
254 p.cur = cat
255 }
256 return &p.cur
257}
258
Rob Pikeaaa3a622010-03-20 22:32:34 -0700259// Return an error indicating which required field was not set.
Rob Pike97e934d2011-04-11 12:52:49 -0700260func (p *textParser) missingRequiredFieldError(sv reflect.Value) *ParseError {
261 st := sv.Type()
Rob Pikeaaa3a622010-03-20 22:32:34 -0700262 sprops := GetProperties(st)
263 for i := 0; i < st.NumField(); i++ {
Rob Pike97e934d2011-04-11 12:52:49 -0700264 if !isNil(sv.Field(i)) {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700265 continue
266 }
267
268 props := sprops.Prop[i]
269 if props.Required {
Rob Piked6420b82011-04-13 16:37:04 -0700270 return p.errorf("message %v missing required field %q", st, props.OrigName)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700271 }
272 }
Rob Piked6420b82011-04-13 16:37:04 -0700273 return p.errorf("message %v missing required field", st) // should not happen
Rob Pikeaaa3a622010-03-20 22:32:34 -0700274}
275
276// Returns the index in the struct for the named field, as well as the parsed tag properties.
Rob Pike97e934d2011-04-11 12:52:49 -0700277func structFieldByName(st reflect.Type, name string) (int, *Properties, bool) {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700278 sprops := GetProperties(st)
David Symonds79eae332010-10-16 11:33:20 +1100279 i, ok := sprops.origNames[name]
280 if ok {
281 return i, sprops.Prop[i], true
Rob Pikeaaa3a622010-03-20 22:32:34 -0700282 }
283 return -1, nil, false
284}
285
David Symonds54531052011-12-08 12:00:31 +1100286// Consume a ':' from the input stream (if the next token is a colon),
287// returning an error if a colon is needed but not present.
288func (p *textParser) checkForColon(props *Properties, typ reflect.Type) *ParseError {
289 tok := p.next()
290 if tok.err != nil {
291 return tok.err
292 }
293 if tok.value != ":" {
294 // Colon is optional when the field is a group or message.
295 needColon := true
296 switch props.Wire {
297 case "group":
298 needColon = false
299 case "bytes":
300 // A "bytes" field is either a message, a string, or a repeated field;
301 // those three become *T, *string and []T respectively, so we can check for
302 // this field being a pointer to a non-string.
303 if typ.Kind() == reflect.Ptr {
304 // *T or *string
305 if typ.Elem().Kind() == reflect.String {
306 break
307 }
308 } else if typ.Kind() == reflect.Slice {
309 // []T or []*T
310 if typ.Elem().Kind() != reflect.Ptr {
311 break
312 }
313 }
314 needColon = false
315 }
316 if needColon {
317 return p.errorf("expected ':', found %q", tok.value)
318 }
319 p.back()
320 }
321 return nil
322}
323
Rob Pike97e934d2011-04-11 12:52:49 -0700324func (p *textParser) readStruct(sv reflect.Value, terminator string) *ParseError {
325 st := sv.Type()
Rob Pikeaaa3a622010-03-20 22:32:34 -0700326 reqCount := GetProperties(st).reqCount
327 // A struct is a sequence of "name: value", terminated by one of
David Symonds54531052011-12-08 12:00:31 +1100328 // '>' or '}', or the end of the input. A name may also be
329 // "[extension]".
Rob Pikeaaa3a622010-03-20 22:32:34 -0700330 for {
331 tok := p.next()
332 if tok.err != nil {
333 return tok.err
334 }
335 if tok.value == terminator {
336 break
337 }
David Symonds54531052011-12-08 12:00:31 +1100338 if tok.value == "[" {
339 // Looks like an extension.
340 //
341 // TODO: Check whether we need to handle
342 // namespace rooted names (e.g. ".something.Foo").
343 tok = p.next()
344 if tok.err != nil {
345 return tok.err
346 }
347 var desc *ExtensionDesc
348 // This could be faster, but it's functional.
349 // TODO: Do something smarter than a linear scan.
David Symonds9f60f432012-06-14 09:45:25 +1000350 for _, d := range RegisteredExtensions(reflect.New(st).Interface().(Message)) {
David Symonds54531052011-12-08 12:00:31 +1100351 if d.Name == tok.value {
352 desc = d
353 break
Rob Pikeaaa3a622010-03-20 22:32:34 -0700354 }
Rob Pikeaaa3a622010-03-20 22:32:34 -0700355 }
David Symonds54531052011-12-08 12:00:31 +1100356 if desc == nil {
357 return p.errorf("unrecognized extension %q", tok.value)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700358 }
David Symonds54531052011-12-08 12:00:31 +1100359 // Check the extension terminator.
360 tok = p.next()
361 if tok.err != nil {
362 return tok.err
363 }
364 if tok.value != "]" {
365 return p.errorf("unrecognized extension terminator %q", tok.value)
366 }
Rob Pikeaaa3a622010-03-20 22:32:34 -0700367
David Symonds54531052011-12-08 12:00:31 +1100368 props := &Properties{}
369 props.Parse(desc.Tag)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700370
David Symonds54531052011-12-08 12:00:31 +1100371 typ := reflect.TypeOf(desc.ExtensionType)
372 if err := p.checkForColon(props, typ); err != nil {
373 return err
374 }
375
David Symonds61826da2012-05-05 09:31:28 +1000376 rep := desc.repeated()
377
David Symonds54531052011-12-08 12:00:31 +1100378 // Read the extension structure, and set it in
379 // the value we're constructing.
David Symonds61826da2012-05-05 09:31:28 +1000380 var ext reflect.Value
381 if !rep {
382 ext = reflect.New(typ).Elem()
383 } else {
384 ext = reflect.New(typ.Elem()).Elem()
385 }
David Symonds54531052011-12-08 12:00:31 +1100386 if err := p.readAny(ext, props); err != nil {
387 return err
388 }
David Symonds61826da2012-05-05 09:31:28 +1000389 ep := sv.Addr().Interface().(extendableProto)
390 if !rep {
391 SetExtension(ep, desc, ext.Interface())
392 } else {
393 old, err := GetExtension(ep, desc)
394 var sl reflect.Value
395 if err == nil {
396 sl = reflect.ValueOf(old) // existing slice
397 } else {
398 sl = reflect.MakeSlice(typ, 0, 1)
399 }
400 sl = reflect.Append(sl, ext)
401 SetExtension(ep, desc, sl.Interface())
402 }
David Symonds54531052011-12-08 12:00:31 +1100403 } else {
404 // This is a normal, non-extension field.
405 fi, props, ok := structFieldByName(st, tok.value)
406 if !ok {
407 return p.errorf("unknown field name %q in %v", tok.value, st)
408 }
409
410 // Check that it's not already set if it's not a repeated field.
411 if !props.Repeated && !isNil(sv.Field(fi)) {
412 return p.errorf("non-repeated field %q was repeated", tok.value)
413 }
414
415 if err := p.checkForColon(props, st.Field(fi).Type); err != nil {
416 return err
417 }
418
David Symonds007ed9d2012-07-24 10:59:36 +1000419 dst := sv.Field(fi)
420
David Symonds54531052011-12-08 12:00:31 +1100421 // Parse into the field.
David Symonds007ed9d2012-07-24 10:59:36 +1000422 if err := p.readAny(dst, props); err != nil {
David Symonds54531052011-12-08 12:00:31 +1100423 return err
424 }
425
426 if props.Required {
427 reqCount--
428 }
Rob Pikeaaa3a622010-03-20 22:32:34 -0700429 }
430 }
431
432 if reqCount > 0 {
433 return p.missingRequiredFieldError(sv)
434 }
435 return nil
436}
437
Rob Pikeaaa3a622010-03-20 22:32:34 -0700438func (p *textParser) readAny(v reflect.Value, props *Properties) *ParseError {
439 tok := p.next()
440 if tok.err != nil {
441 return tok.err
442 }
443 if tok.value == "" {
Rob Piked6420b82011-04-13 16:37:04 -0700444 return p.errorf("unexpected EOF")
Rob Pikeaaa3a622010-03-20 22:32:34 -0700445 }
446
Rob Pike97e934d2011-04-11 12:52:49 -0700447 switch fv := v; fv.Kind() {
448 case reflect.Slice:
449 at := v.Type()
Rob Pikeab5b8022010-06-21 17:47:58 -0700450 if at.Elem().Kind() == reflect.Uint8 {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700451 // Special case for []byte
David Symonds162d0032012-06-28 09:44:46 -0700452 if tok.value[0] != '"' && tok.value[0] != '\'' {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700453 // Deliberately written out here, as the error after
454 // this switch statement would write "invalid []byte: ...",
455 // which is not as user-friendly.
Rob Piked6420b82011-04-13 16:37:04 -0700456 return p.errorf("invalid string: %v", tok.value)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700457 }
458 bytes := []byte(tok.unquoted)
Nigel Tao4ede8452011-04-28 11:27:25 +1000459 fv.Set(reflect.ValueOf(bytes))
Rob Pikeaaa3a622010-03-20 22:32:34 -0700460 return nil
461 }
462 // Repeated field. May already exist.
David Symonds79eae332010-10-16 11:33:20 +1100463 flen := fv.Len()
464 if flen == fv.Cap() {
465 nav := reflect.MakeSlice(at, flen, 2*flen+1)
Rob Pike48fd4a42010-12-14 23:40:41 -0800466 reflect.Copy(nav, fv)
David Symonds79eae332010-10-16 11:33:20 +1100467 fv.Set(nav)
468 }
469 fv.SetLen(flen + 1)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700470
471 // Read one.
472 p.back()
David Symondsef8f0e82011-10-13 12:57:34 +1100473 return p.readAny(fv.Index(flen), props)
Rob Pike97e934d2011-04-11 12:52:49 -0700474 case reflect.Bool:
Rob Pikeaaa3a622010-03-20 22:32:34 -0700475 // Either "true", "false", 1 or 0.
476 switch tok.value {
477 case "true", "1":
Rob Pike97e934d2011-04-11 12:52:49 -0700478 fv.SetBool(true)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700479 return nil
480 case "false", "0":
Rob Pike97e934d2011-04-11 12:52:49 -0700481 fv.SetBool(false)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700482 return nil
483 }
Rob Pike97e934d2011-04-11 12:52:49 -0700484 case reflect.Float32, reflect.Float64:
David Symonds6bd081e2012-06-28 10:46:25 -0700485 v := tok.value
486 if strings.HasSuffix(v, "f") {
487 // Ignore 'f' for compatibility with output generated by C++.
488 v = v[:len(v)-1]
489 }
490 if f, err := strconv.ParseFloat(v, fv.Type().Bits()); err == nil {
Rob Pike97e934d2011-04-11 12:52:49 -0700491 fv.SetFloat(f)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700492 return nil
493 }
Rob Pike19b2dbb2011-04-11 16:49:15 -0700494 case reflect.Int32:
David Symonds32612dd2012-06-15 07:59:05 -0700495 if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil {
Rob Pike19b2dbb2011-04-11 16:49:15 -0700496 fv.SetInt(x)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700497 return nil
Rob Pike19b2dbb2011-04-11 16:49:15 -0700498 }
499 if len(props.Enum) == 0 {
500 break
501 }
502 m, ok := enumValueMaps[props.Enum]
503 if !ok {
504 break
505 }
506 x, ok := m[tok.value]
507 if !ok {
508 break
509 }
510 fv.SetInt(int64(x))
511 return nil
512 case reflect.Int64:
David Symonds32612dd2012-06-15 07:59:05 -0700513 if x, err := strconv.ParseInt(tok.value, 0, 64); err == nil {
Rob Pike19b2dbb2011-04-11 16:49:15 -0700514 fv.SetInt(x)
515 return nil
Rob Pikeaaa3a622010-03-20 22:32:34 -0700516 }
Rob Pike97e934d2011-04-11 12:52:49 -0700517 case reflect.Ptr:
Rob Pikeaaa3a622010-03-20 22:32:34 -0700518 // A basic field (indirected through pointer), or a repeated message/group
519 p.back()
Rob Pikeccd260c2011-04-18 13:13:04 -0700520 fv.Set(reflect.New(fv.Type().Elem()))
Rob Pikeaaa3a622010-03-20 22:32:34 -0700521 return p.readAny(fv.Elem(), props)
Rob Pike97e934d2011-04-11 12:52:49 -0700522 case reflect.String:
David Symonds162d0032012-06-28 09:44:46 -0700523 if tok.value[0] == '"' || tok.value[0] == '\'' {
Rob Pike97e934d2011-04-11 12:52:49 -0700524 fv.SetString(tok.unquoted)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700525 return nil
526 }
Rob Pike97e934d2011-04-11 12:52:49 -0700527 case reflect.Struct:
Rob Pikeaaa3a622010-03-20 22:32:34 -0700528 var terminator string
529 switch tok.value {
530 case "{":
531 terminator = "}"
532 case "<":
533 terminator = ">"
534 default:
Rob Piked6420b82011-04-13 16:37:04 -0700535 return p.errorf("expected '{' or '<', found %q", tok.value)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700536 }
537 return p.readStruct(fv, terminator)
Rob Pike19b2dbb2011-04-11 16:49:15 -0700538 case reflect.Uint32:
David Symonds32612dd2012-06-15 07:59:05 -0700539 if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil {
Rob Pike19b2dbb2011-04-11 16:49:15 -0700540 fv.SetUint(uint64(x))
541 return nil
542 }
543 case reflect.Uint64:
David Symonds32612dd2012-06-15 07:59:05 -0700544 if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil {
Rob Pike19b2dbb2011-04-11 16:49:15 -0700545 fv.SetUint(x)
546 return nil
Rob Pikeaaa3a622010-03-20 22:32:34 -0700547 }
548 }
Rob Piked6420b82011-04-13 16:37:04 -0700549 return p.errorf("invalid %v: %v", v.Type(), tok.value)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700550}
551
David Symonds9f60f432012-06-14 09:45:25 +1000552// UnmarshalText reads a protocol buffer in Text format.
553func UnmarshalText(s string, pb Message) error {
Nigel Tao4ede8452011-04-28 11:27:25 +1000554 v := reflect.ValueOf(pb)
David Symondsa9cda212011-04-15 01:23:17 -0700555 if pe := newTextParser(s).readStruct(v.Elem(), ""); pe != nil {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700556 return pe
557 }
558 return nil
559}