Skip to content

Commit 82c88b8

Browse files
committed
internal/godoc/codec: add a code for nil
It's clearer to have a separate code for nil, rather than use a zero. Also, zero won't work for pointers to numbers, which we may encounter some day. Also, reserve some codes for future use. If we don't do that now, then adding them later could break encoded values and we'd have to reprocess everything. Change-Id: I2aba2f106a544607ba8437811a1ae77f4ea35bdb Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/264917 Trust: Jonathan Amsterdam <[email protected]> Run-TryBot: Jonathan Amsterdam <[email protected]> Reviewed-by: Julie Qiu <[email protected]>
1 parent e3b1de9 commit 82c88b8

File tree

5 files changed

+52
-30
lines changed

5 files changed

+52
-30
lines changed

internal/godoc/codec/codec.go

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,15 @@ func (d *Decoder) readUint64() uint64 {
182182
//
183183
// Most of the values of that initial byte can be devoted to small unsigned
184184
// integers. For example, the number 17 is represented by the single byte 17.
185-
// Only five byte values have special meaning.
185+
// Only a few byte values have special meaning.
186186
//
187+
// The nil code indicates that the value is nil. We don't absolutely need this:
188+
// we could always represent the nil value for a type as something that couldn't
189+
// be mistaken for an encoded value of that type. For instance, we could use 0
190+
// for nil in the case of slices (which always begin with the nValues code), and
191+
// for pointers to numbers like *int, we could use something like "nBytes 0".
192+
// But it is simpler to have a reserved value for nil.
193+
187194
// The nBytes code indicates that an unsigned integer N is encoded next,
188195
// followed by N bytes of data. This is used to represent strings and byte
189196
// slices, as well numbers bigger than can fit into the initial byte. For
@@ -207,11 +214,20 @@ func (d *Decoder) readUint64() uint64 {
207214
// The start and end codes delimit a value whose length is unknown beforehand.
208215
// It is used for structs.
209216
const (
210-
nBytesCode = 255 - iota // uint n follows, then n bytes
211-
nValuesCode // uint n follows, then n values
212-
refCode // uint n follows, referring to a previous value
213-
startCode // start of a value of indeterminate length
214-
endCode // end of a value that began with with start
217+
nilCode = 255 - iota // a nil value
218+
// reserve a few values for future use
219+
reserved1
220+
reserved2
221+
reserved3
222+
reserved4
223+
reserved5
224+
reserved6
225+
reserved7
226+
nBytesCode // uint n follows, then n bytes
227+
nValuesCode // uint n follows, then n values
228+
refCode // uint n follows, referring to a previous value
229+
startCode // start of a value of indeterminate length
230+
endCode // end of a value that began with start
215231
// Bytes less than endCode represent themselves.
216232
)
217233

@@ -349,6 +365,10 @@ func (d *Decoder) DecodeFloat() float64 {
349365
return math.Float64frombits(d.DecodeUint())
350366
}
351367

368+
func (e *Encoder) EncodeNil() {
369+
e.writeByte(nilCode)
370+
}
371+
352372
// StartList should be called before encoding any sequence of variable-length
353373
// values.
354374
func (e *Encoder) StartList(len int) {
@@ -360,15 +380,15 @@ func (e *Encoder) StartList(len int) {
360380
// values. It returns -1 if the encoded list was nil. Otherwise, it returns the
361381
// length of the sequence.
362382
func (d *Decoder) StartList() int {
363-
b := d.readByte()
364-
if b == 0 { // used for nil
383+
switch b := d.readByte(); b {
384+
case nilCode:
365385
return -1
366-
}
367-
if b != nValuesCode {
386+
case nValuesCode:
387+
return int(d.DecodeUint())
388+
default:
368389
d.badcode(b)
369390
return 0
370391
}
371-
return int(d.DecodeUint())
372392
}
373393

374394
//////////////// Struct Support
@@ -378,7 +398,7 @@ func (d *Decoder) StartList() int {
378398
// pointer. If StartStruct returns false, encoding should not proceed.
379399
func (e *Encoder) StartStruct(isNil bool, p interface{}) bool {
380400
if isNil {
381-
e.EncodeUint(0)
401+
e.EncodeNil()
382402
return false
383403
}
384404
if u, ok := e.seen[p]; ok {
@@ -402,7 +422,7 @@ func (e *Encoder) StartStruct(isNil bool, p interface{}) bool {
402422
func (d *Decoder) StartStruct() (bool, interface{}) {
403423
b := d.readByte()
404424
switch b {
405-
case 0: // nil; do not set the pointer
425+
case nilCode: // do not set the pointer
406426
return false, nil
407427
case refCode:
408428
u := d.DecodeUint()
@@ -451,6 +471,8 @@ func (d *Decoder) skip() {
451471
return
452472
}
453473
switch b {
474+
case nilCode:
475+
// Nothing follows.
454476
case nBytesCode:
455477
// A uint n and n bytes follow. It is efficient to call readBytes here
456478
// because it does no allocation.

internal/godoc/codec/generate.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ const sliceBody = `
449449
« $goName := goName .Type »
450450
func encode_«$funcName»(e *codec.Encoder, s «$goName») {
451451
if s == nil {
452-
e.EncodeUint(0)
452+
e.EncodeNil()
453453
return
454454
}
455455
e.StartList(len(s))
@@ -488,7 +488,7 @@ const mapBody = `
488488
« $goName := goName .Type »
489489
func encode_«$funcName»(e *codec.Encoder, m «$goName») {
490490
if m == nil {
491-
e.EncodeUint(0)
491+
e.EncodeNil()
492492
return
493493
}
494494
e.StartList(2*len(m))

internal/godoc/codec/testdata/map.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/godoc/codec/testdata/slice.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/godoc/encode_ast.go

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)