taldir

Directory service to resolve wallet mailboxes by messenger addresses
Log | Files | Refs | Submodules | README | LICENSE

codec.go (10626B)


      1 // Copyright 2017 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package catmsg
      6 
      7 import (
      8 	"errors"
      9 	"fmt"
     10 
     11 	"golang.org/x/text/language"
     12 )
     13 
     14 // A Renderer renders a Message.
     15 type Renderer interface {
     16 	// Render renders the given string. The given string may be interpreted as a
     17 	// format string, such as the one used by the fmt package or a template.
     18 	Render(s string)
     19 
     20 	// Arg returns the i-th argument passed to format a message. This method
     21 	// should return nil if there is no such argument. Messages need access to
     22 	// arguments to allow selecting a message based on linguistic features of
     23 	// those arguments.
     24 	Arg(i int) interface{}
     25 }
     26 
     27 // A Dictionary specifies a source of messages, including variables or macros.
     28 type Dictionary interface {
     29 	// Lookup returns the message for the given key. It returns false for ok if
     30 	// such a message could not be found.
     31 	Lookup(key string) (data string, ok bool)
     32 
     33 	// TODO: consider returning an interface, instead of a string. This will
     34 	// allow implementations to do their own message type decoding.
     35 }
     36 
     37 // An Encoder serializes a Message to a string.
     38 type Encoder struct {
     39 	// The root encoder is used for storing encoded variables.
     40 	root *Encoder
     41 	// The parent encoder provides the surrounding scopes for resolving variable
     42 	// names.
     43 	parent *Encoder
     44 
     45 	tag language.Tag
     46 
     47 	// buf holds the encoded message so far. After a message completes encoding,
     48 	// the contents of buf, prefixed by the encoded length, are flushed to the
     49 	// parent buffer.
     50 	buf []byte
     51 
     52 	// vars is the lookup table of variables in the current scope.
     53 	vars []keyVal
     54 
     55 	err    error
     56 	inBody bool // if false next call must be EncodeMessageType
     57 }
     58 
     59 type keyVal struct {
     60 	key    string
     61 	offset int
     62 }
     63 
     64 // Language reports the language for which the encoded message will be stored
     65 // in the Catalog.
     66 func (e *Encoder) Language() language.Tag { return e.tag }
     67 
     68 func (e *Encoder) setError(err error) {
     69 	if e.root.err == nil {
     70 		e.root.err = err
     71 	}
     72 }
     73 
     74 // EncodeUint encodes x.
     75 func (e *Encoder) EncodeUint(x uint64) {
     76 	e.checkInBody()
     77 	var buf [maxVarintBytes]byte
     78 	n := encodeUint(buf[:], x)
     79 	e.buf = append(e.buf, buf[:n]...)
     80 }
     81 
     82 // EncodeString encodes s.
     83 func (e *Encoder) EncodeString(s string) {
     84 	e.checkInBody()
     85 	e.EncodeUint(uint64(len(s)))
     86 	e.buf = append(e.buf, s...)
     87 }
     88 
     89 // EncodeMessageType marks the current message to be of type h.
     90 //
     91 // It must be the first call of a Message's Compile method.
     92 func (e *Encoder) EncodeMessageType(h Handle) {
     93 	if e.inBody {
     94 		panic("catmsg: EncodeMessageType not the first method called")
     95 	}
     96 	e.inBody = true
     97 	e.EncodeUint(uint64(h))
     98 }
     99 
    100 // EncodeMessage serializes the given message inline at the current position.
    101 func (e *Encoder) EncodeMessage(m Message) error {
    102 	e = &Encoder{root: e.root, parent: e, tag: e.tag}
    103 	err := m.Compile(e)
    104 	if _, ok := m.(*Var); !ok {
    105 		e.flushTo(e.parent)
    106 	}
    107 	return err
    108 }
    109 
    110 func (e *Encoder) checkInBody() {
    111 	if !e.inBody {
    112 		panic("catmsg: expected prior call to EncodeMessageType")
    113 	}
    114 }
    115 
    116 // stripPrefix indicates the number of prefix bytes that must be stripped to
    117 // turn a single-element sequence into a message that is just this single member
    118 // without its size prefix. If the message can be stripped, b[1:n] contains the
    119 // size prefix.
    120 func stripPrefix(b []byte) (n int) {
    121 	if len(b) > 0 && Handle(b[0]) == msgFirst {
    122 		x, n, _ := decodeUint(b[1:])
    123 		if 1+n+int(x) == len(b) {
    124 			return 1 + n
    125 		}
    126 	}
    127 	return 0
    128 }
    129 
    130 func (e *Encoder) flushTo(dst *Encoder) {
    131 	data := e.buf
    132 	p := stripPrefix(data)
    133 	if p > 0 {
    134 		data = data[1:]
    135 	} else {
    136 		// Prefix the size.
    137 		dst.EncodeUint(uint64(len(data)))
    138 	}
    139 	dst.buf = append(dst.buf, data...)
    140 }
    141 
    142 func (e *Encoder) addVar(key string, m Message) error {
    143 	for _, v := range e.parent.vars {
    144 		if v.key == key {
    145 			err := fmt.Errorf("catmsg: duplicate variable %q", key)
    146 			e.setError(err)
    147 			return err
    148 		}
    149 	}
    150 	scope := e.parent
    151 	// If a variable message is Incomplete, and does not evaluate to a message
    152 	// during execution, we fall back to the variable name. We encode this by
    153 	// appending the variable name if the message reports it's incomplete.
    154 
    155 	err := m.Compile(e)
    156 	if err != ErrIncomplete {
    157 		e.setError(err)
    158 	}
    159 	switch {
    160 	case len(e.buf) == 1 && Handle(e.buf[0]) == msgFirst: // empty sequence
    161 		e.buf = e.buf[:0]
    162 		e.inBody = false
    163 		fallthrough
    164 	case len(e.buf) == 0:
    165 		// Empty message.
    166 		if err := String(key).Compile(e); err != nil {
    167 			e.setError(err)
    168 		}
    169 	case err == ErrIncomplete:
    170 		if Handle(e.buf[0]) != msgFirst {
    171 			seq := &Encoder{root: e.root, parent: e}
    172 			seq.EncodeMessageType(msgFirst)
    173 			e.flushTo(seq)
    174 			e = seq
    175 		}
    176 		// e contains a sequence; append the fallback string.
    177 		e.EncodeMessage(String(key))
    178 	}
    179 
    180 	// Flush result to variable heap.
    181 	offset := len(e.root.buf)
    182 	e.flushTo(e.root)
    183 	e.buf = e.buf[:0]
    184 
    185 	// Record variable offset in current scope.
    186 	scope.vars = append(scope.vars, keyVal{key: key, offset: offset})
    187 	return err
    188 }
    189 
    190 const (
    191 	substituteVar = iota
    192 	substituteMacro
    193 	substituteError
    194 )
    195 
    196 // EncodeSubstitution inserts a resolved reference to a variable or macro.
    197 //
    198 // This call must be matched with a call to ExecuteSubstitution at decoding
    199 // time.
    200 func (e *Encoder) EncodeSubstitution(name string, arguments ...int) {
    201 	if arity := len(arguments); arity > 0 {
    202 		// TODO: also resolve macros.
    203 		e.EncodeUint(substituteMacro)
    204 		e.EncodeString(name)
    205 		for _, a := range arguments {
    206 			e.EncodeUint(uint64(a))
    207 		}
    208 		return
    209 	}
    210 	for scope := e; scope != nil; scope = scope.parent {
    211 		for _, v := range scope.vars {
    212 			if v.key != name {
    213 				continue
    214 			}
    215 			e.EncodeUint(substituteVar) // TODO: support arity > 0
    216 			e.EncodeUint(uint64(v.offset))
    217 			return
    218 		}
    219 	}
    220 	// TODO: refer to dictionary-wide scoped variables.
    221 	e.EncodeUint(substituteError)
    222 	e.EncodeString(name)
    223 	e.setError(fmt.Errorf("catmsg: unknown var %q", name))
    224 }
    225 
    226 // A Decoder deserializes and evaluates messages that are encoded by an encoder.
    227 type Decoder struct {
    228 	tag    language.Tag
    229 	dst    Renderer
    230 	macros Dictionary
    231 
    232 	err  error
    233 	vars string
    234 	data string
    235 
    236 	macroArg int // TODO: allow more than one argument
    237 }
    238 
    239 // NewDecoder returns a new Decoder.
    240 //
    241 // Decoders are designed to be reused for multiple invocations of Execute.
    242 // Only one goroutine may call Execute concurrently.
    243 func NewDecoder(tag language.Tag, r Renderer, macros Dictionary) *Decoder {
    244 	return &Decoder{
    245 		tag:    tag,
    246 		dst:    r,
    247 		macros: macros,
    248 	}
    249 }
    250 
    251 func (d *Decoder) setError(err error) {
    252 	if d.err == nil {
    253 		d.err = err
    254 	}
    255 }
    256 
    257 // Language returns the language in which the message is being rendered.
    258 //
    259 // The destination language may be a child language of the language used for
    260 // encoding. For instance, a decoding language of "pt-PT" is consistent with an
    261 // encoding language of "pt".
    262 func (d *Decoder) Language() language.Tag { return d.tag }
    263 
    264 // Done reports whether there are more bytes to process in this message.
    265 func (d *Decoder) Done() bool { return len(d.data) == 0 }
    266 
    267 // Render implements Renderer.
    268 func (d *Decoder) Render(s string) { d.dst.Render(s) }
    269 
    270 // Arg implements Renderer.
    271 //
    272 // During evaluation of macros, the argument positions may be mapped to
    273 // arguments that differ from the original call.
    274 func (d *Decoder) Arg(i int) interface{} {
    275 	if d.macroArg != 0 {
    276 		if i != 1 {
    277 			panic("catmsg: only macros with single argument supported")
    278 		}
    279 		i = d.macroArg
    280 	}
    281 	return d.dst.Arg(i)
    282 }
    283 
    284 // DecodeUint decodes a number that was encoded with EncodeUint and advances the
    285 // position.
    286 func (d *Decoder) DecodeUint() uint64 {
    287 	x, n, err := decodeUintString(d.data)
    288 	d.data = d.data[n:]
    289 	if err != nil {
    290 		d.setError(err)
    291 	}
    292 	return x
    293 }
    294 
    295 // DecodeString decodes a string that was encoded with EncodeString and advances
    296 // the position.
    297 func (d *Decoder) DecodeString() string {
    298 	size := d.DecodeUint()
    299 	s := d.data[:size]
    300 	d.data = d.data[size:]
    301 	return s
    302 }
    303 
    304 // SkipMessage skips the message at the current location and advances the
    305 // position.
    306 func (d *Decoder) SkipMessage() {
    307 	n := int(d.DecodeUint())
    308 	d.data = d.data[n:]
    309 }
    310 
    311 // Execute decodes and evaluates msg.
    312 //
    313 // Only one goroutine may call execute.
    314 func (d *Decoder) Execute(msg string) error {
    315 	d.err = nil
    316 	if !d.execute(msg) {
    317 		return ErrNoMatch
    318 	}
    319 	return d.err
    320 }
    321 
    322 func (d *Decoder) execute(msg string) bool {
    323 	saved := d.data
    324 	d.data = msg
    325 	ok := d.executeMessage()
    326 	d.data = saved
    327 	return ok
    328 }
    329 
    330 // executeMessageFromData is like execute, but also decodes a leading message
    331 // size and clips the given string accordingly.
    332 //
    333 // It reports the number of bytes consumed and whether a message was selected.
    334 func (d *Decoder) executeMessageFromData(s string) (n int, ok bool) {
    335 	saved := d.data
    336 	d.data = s
    337 	size := int(d.DecodeUint())
    338 	n = len(s) - len(d.data)
    339 	// Sanitize the setting. This allows skipping a size argument for
    340 	// RawString and method Done.
    341 	d.data = d.data[:size]
    342 	ok = d.executeMessage()
    343 	n += size - len(d.data)
    344 	d.data = saved
    345 	return n, ok
    346 }
    347 
    348 var errUnknownHandler = errors.New("catmsg: string contains unsupported handler")
    349 
    350 // executeMessage reads the handle id, initializes the decoder and executes the
    351 // message. It is assumed that all of d.data[d.p:] is the single message.
    352 func (d *Decoder) executeMessage() bool {
    353 	if d.Done() {
    354 		// We interpret no data as a valid empty message.
    355 		return true
    356 	}
    357 	handle := d.DecodeUint()
    358 
    359 	var fn Handler
    360 	mutex.Lock()
    361 	if int(handle) < len(handlers) {
    362 		fn = handlers[handle]
    363 	}
    364 	mutex.Unlock()
    365 	if fn == nil {
    366 		d.setError(errUnknownHandler)
    367 		d.execute(fmt.Sprintf("\x02$!(UNKNOWNMSGHANDLER=%#x)", handle))
    368 		return true
    369 	}
    370 	return fn(d)
    371 }
    372 
    373 // ExecuteMessage decodes and executes the message at the current position.
    374 func (d *Decoder) ExecuteMessage() bool {
    375 	n, ok := d.executeMessageFromData(d.data)
    376 	d.data = d.data[n:]
    377 	return ok
    378 }
    379 
    380 // ExecuteSubstitution executes the message corresponding to the substitution
    381 // as encoded by EncodeSubstitution.
    382 func (d *Decoder) ExecuteSubstitution() {
    383 	switch x := d.DecodeUint(); x {
    384 	case substituteVar:
    385 		offset := d.DecodeUint()
    386 		d.executeMessageFromData(d.vars[offset:])
    387 	case substituteMacro:
    388 		name := d.DecodeString()
    389 		data, ok := d.macros.Lookup(name)
    390 		old := d.macroArg
    391 		// TODO: support macros of arity other than 1.
    392 		d.macroArg = int(d.DecodeUint())
    393 		switch {
    394 		case !ok:
    395 			// TODO: detect this at creation time.
    396 			d.setError(fmt.Errorf("catmsg: undefined macro %q", name))
    397 			fallthrough
    398 		case !d.execute(data):
    399 			d.dst.Render(name) // fall back to macro name.
    400 		}
    401 		d.macroArg = old
    402 	case substituteError:
    403 		d.dst.Render(d.DecodeString())
    404 	default:
    405 		panic("catmsg: unreachable")
    406 	}
    407 }