Go support for protocol buffers.
Consists of a compiler plugin and the support library, all written in Go.
This is a complete implementation except for:
- Extensions in the plugin
- coming soon
- support is already in the library
- Services (RPC)
- needs an external definition to honor before supporting.
- Insertion points in the plugin
- may come
R=rsc, dsymonds1, ken2
CC=golang-dev
http://codereview.appspot.com/676041
diff --git a/proto/text.go b/proto/text.go
new file mode 100644
index 0000000..ccb8ae5
--- /dev/null
+++ b/proto/text.go
@@ -0,0 +1,221 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 Google Inc. All rights reserved.
+// http://code.google.com/p/goprotobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package proto
+
+// Functions for writing the Text protocol buffer format.
+// TODO:
+// - groups.
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+// An io.Writer wrapper that tracks its indentation level.
+type textWriter struct {
+ indent_level int
+ complete bool // if the current position is a complete line
+ compact bool // whether to write out as a one-liner
+ writer io.Writer
+}
+
+func (w *textWriter) Write(p []byte) (n int, err os.Error) {
+ n, err = len(p), nil
+
+ frags := strings.Split(string(p), "\n", 0)
+ if w.compact {
+ w.writer.Write([]byte(strings.Join(frags, " ")))
+ return
+ }
+
+ for i := 0; i < len(frags); i++ {
+ if w.complete {
+ for j := 0; j < w.indent_level; j++ {
+ w.writer.Write([]byte{' ', ' '})
+ }
+ w.complete = false
+ }
+
+ w.writer.Write([]byte(frags[i]))
+ if i+1 < len(frags) {
+ w.writer.Write([]byte{'\n'})
+ }
+ }
+ w.complete = len(frags[len(frags)-1]) == 0
+
+ return
+}
+
+func (w *textWriter) indent() { w.indent_level++ }
+
+func (w *textWriter) unindent() {
+ if w.indent_level == 0 {
+ fmt.Fprintln(os.Stderr, "proto: textWriter unindented too far!")
+ } else {
+ w.indent_level--
+ }
+}
+
+func writeStruct(w *textWriter, sv *reflect.StructValue) {
+ st := sv.Type().(*reflect.StructType)
+ sprops := GetProperties(st)
+ for i := 0; i < sv.NumField(); i++ {
+ if strings.HasPrefix(st.Field(i).Name, "XXX_") {
+ continue
+ }
+ props := sprops.Prop[i]
+ fv := sv.Field(i)
+ if pv, ok := fv.(*reflect.PtrValue); ok && pv.IsNil() {
+ // Field not filled in. This could be an optional field or
+ // a required field that wasn't filled in. Either way, there
+ // isn't anything we can show for it.
+ continue
+ }
+ if av, ok := fv.(*reflect.SliceValue); ok && av.IsNil() {
+ // Repeated field that is empty, or a bytes field that is unused.
+ continue
+ }
+
+ if props.Repeated {
+ if av, ok := fv.(*reflect.SliceValue); ok {
+ // Repeated field.
+ for j := 0; j < av.Len(); j++ {
+ fmt.Fprintf(w, "%v:", props.OrigName)
+ if !w.compact {
+ w.Write([]byte{' '})
+ }
+ writeAny(w, av.Elem(j))
+ fmt.Fprint(w, "\n")
+ }
+ continue
+ }
+ }
+
+ fmt.Fprintf(w, "%v:", props.OrigName)
+ if !w.compact {
+ w.Write([]byte{' '})
+ }
+ if len(props.Enum) == 0 || !tryWriteEnum(w, props.Enum, fv) {
+ writeAny(w, fv)
+ }
+ fmt.Fprint(w, "\n")
+ }
+}
+
+func tryWriteEnum(w *textWriter, enum string, v reflect.Value) bool {
+ val, ok := reflect.Indirect(v).(*reflect.Int32Value)
+ if !ok {
+ return false
+ }
+ m, ok := enumNameMaps[enum]
+ if !ok {
+ return false
+ }
+ str, ok := m[val.Get()]
+ if !ok {
+ return false
+ }
+ fmt.Fprintf(w, str)
+ return true
+}
+
+func writeAny(w *textWriter, v reflect.Value) {
+ v = reflect.Indirect(v)
+
+ // We don't attempt to serialise every possible value type; only those
+ // that can occur in protocol buffers, plus a few extra that were easy.
+ switch val := v.(type) {
+ case *reflect.SliceValue:
+ // Should only be a []byte; repeated fields are handled in writeStruct.
+ // TODO: Handle other cases cleaner.
+ bytes := make([]byte, val.Len())
+ for i := 0; i < val.Len(); i++ {
+ bytes[i] = val.Elem(i).(*reflect.Uint8Value).Get()
+ }
+ // TODO: Should be strconv.QuoteC, which doesn't exist yet
+ fmt.Fprint(w, strconv.Quote(string(bytes)))
+ case *reflect.StringValue:
+ // TODO: Should be strconv.QuoteC, which doesn't exist yet
+ fmt.Fprint(w, strconv.Quote(val.Get()))
+ case *reflect.StructValue:
+ // Required/optional group/message.
+ // TODO: groups use { } instead of < >, and no colon.
+ if !w.compact {
+ fmt.Fprint(w, "<\n")
+ } else {
+ fmt.Fprint(w, "<")
+ }
+ w.indent()
+ writeStruct(w, val)
+ w.unindent()
+ fmt.Fprint(w, ">")
+ default:
+ fmt.Fprint(w, val.Interface())
+ }
+}
+
+func marshalText(w io.Writer, pb interface{}, compact bool) {
+ aw := new(textWriter)
+ aw.writer = w
+ aw.complete = true
+ aw.compact = compact
+
+ v := reflect.NewValue(pb)
+ // We should normally be passed a struct, or a pointer to a struct,
+ // and we don't want the outer < and > in that case.
+ v = reflect.Indirect(v)
+ if sv, ok := v.(*reflect.StructValue); ok {
+ writeStruct(aw, sv)
+ } else {
+ writeAny(aw, v)
+ }
+}
+
+// MarshalText writes a given protobuffer in Text format.
+// Non-protobuffers can also be written, but their formatting is not guaranteed.
+func MarshalText(w io.Writer, pb interface{}) { marshalText(w, pb, false) }
+
+// CompactText writes a given protobuffer in compact Text format (one line).
+// Non-protobuffers can also be written, but their formatting is not guaranteed.
+func CompactText(w io.Writer, pb interface{}) { marshalText(w, pb, true) }
+
+// CompactTextString is the same as CompactText, but returns the string directly.
+func CompactTextString(pb interface{}) string {
+ buf := new(bytes.Buffer)
+ marshalText(buf, pb, true)
+ return buf.String()
+}