github.com/go-xe2/third@v1.0.3/golang.org/x/text/internal/catmsg/codec.go (about) 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 "github.com/go-xe2/third/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(First) 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 }