-
Notifications
You must be signed in to change notification settings - Fork 9
/
encode.go
73 lines (62 loc) · 1.84 KB
/
encode.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package gpp
import (
"errors"
"fmt"
"github.com/prebid/go-gpp/constants"
"github.com/prebid/go-gpp/util"
"sort"
"strings"
)
const (
// the first 6 bits of the header must always evaluate to the integer '3'.
gppType byte = 0x3
gppVersion byte = 0x1
// the range of SectionID must start with 1 and end with the maximum value represented by uint16.
minSectionId constants.SectionID = 1
maxSectionId constants.SectionID = 0xffff
)
var (
sectionIdOutOfRangeErr = errors.New("section ID out of range")
duplicatedSectionErr = errors.New("duplicated sections")
)
func Encode(sections []Section) (string, error) {
bs := util.NewBitStreamForWrite()
builder := strings.Builder{}
bs.WriteByte6(gppType)
bs.WriteByte6(gppVersion)
sort.Slice(sections, func(i, j int) bool {
return sections[i].GetID() < sections[j].GetID()
})
if len(sections) > 0 && (sections[0].GetID() < minSectionId ||
sections[len(sections)-1].GetID() > maxSectionId) {
return "", sectionIdOutOfRangeErr
}
// Generate int range object.
intRange := new(util.IntRange)
// Since the minimum sectionID is 1, the previous one should start with -1, which makes it not continuous.
var prevID constants.SectionID = -1
for _, sec := range sections {
id := sec.GetID()
if id == prevID {
return "", duplicatedSectionErr
}
if prevID+1 == id {
intRange.Range[len(intRange.Range)-1].EndID = uint16(id)
} else {
intRange.Range = append(intRange.Range, util.IRange{StartID: uint16(id), EndID: uint16(id)})
}
prevID = id
}
intRange.Size = uint16(len(intRange.Range))
err := bs.WriteIntRange(intRange)
if err != nil {
return "", fmt.Errorf("write int range error: %v", err)
}
builder.Write(bs.Base64Encode())
for _, sec := range sections {
builder.WriteByte('~')
// By default, GPP is included.
builder.Write(sec.Encode(true))
}
return builder.String(), nil
}