Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(288)

Side by Side Diff: client/internal/logdog/butler/bundler/sizer_fast.go

Issue 1276923003: logdog: Add bundler library. (Closed) Base URL: https://github.com/luci/luci-go@logdog-review-streamserver
Patch Set: Addressed code review comments. Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package bundler
6
7 import (
8 "errors"
9 "fmt"
10 "reflect"
11 "strconv"
12 "strings"
13
14 "github.com/golang/protobuf/proto"
15 "github.com/luci/luci-go/common/logdog/protocol"
16 "github.com/luci/luci-go/common/logdog/protocol/protoutil"
17 "github.com/luci/luci-go/common/logdog/types"
18 )
19
20 var (
21 bundleEntryTagSize int
22 logEntryTagSize int
23
24 errMalformedProtobufField = errors.New("malformed protobuf field")
25 )
26
27 func init() {
28 bundleEntryTagSize = mustCalculateTagSize(&protocol.ButlerLogBundle{}, " Entries")
29 logEntryTagSize = mustCalculateTagSize(&protocol.ButlerLogBundle_Entry{} , "Logs")
30 }
31
32 type fastSizer struct {
33 size int64
34
35 // As we add LogEntry to a ButlerLogBundle_Entry, the bundle entry's siz e (and
36 // therefore it's protobuf size prefix) will grow. We account for the gr owth
37 // by tracking the size of each ButlerLogBundle_Entry and updating it wh en
38 // we add a LogEntry to it. This is stored independently from the cumula tive
39 // size and factored in when Size() is calculated.
40 beSize map[types.StreamPath]int64
41 }
42
43 // NewFastSizer is a Sizer that is optimized for LogDog protobufs.
44 //
45 // In exchange for rapid size calculation, it performs worst-case estimates on
46 // the unknown protocol overheads, leading to potential size overestimation.
47 func NewFastSizer(b *protocol.ButlerLogBundle) Sizer {
48 return &fastSizer{
49 size: int64(proto.Size(b)),
50 beSize: map[types.StreamPath]int64{},
51 }
52 }
53
54 func (b *fastSizer) Size() int64 {
55 size := b.size
56 for _, v := range b.beSize {
57 size += int64(varintLength(uint64(v)))
58 }
59 return size
60 }
61
62 func (b *fastSizer) AppendBundleEntry(e *protocol.ButlerLogBundle_Entry) {
63 ps := proto.Size(e)
64 s := bundleEntryTagSize + ps
65 b.size += int64(s)
66 b.addBundleEntrySize(e, s)
67 }
68
69 func (b *fastSizer) AppendLogEntry(be *protocol.ButlerLogBundle_Entry, e *protoc ol.LogEntry) {
70 ps := proto.Size(e)
71 s := logEntryTagSize + varintLength(uint64(ps)) + ps
72 b.size += int64(s)
73 b.addBundleEntrySize(be, s)
74 }
75
76 func (b *fastSizer) addBundleEntrySize(e *protocol.ButlerLogBundle_Entry, count int) {
77 path := protoutil.DescriptorPath(e.GetDesc())
78 b.beSize[path] += int64(count)
79 }
80
81 func mustCalculateTagSize(i interface{}, field string) int {
82 value, err := calculateTagSize(i, field)
83 if err != nil {
84 panic(err)
85 }
86 return value
87 }
88
89 func calculateTagSize(i interface{}, field string) (int, error) {
90 v := reflect.TypeOf(i)
91 if v.Kind() == reflect.Ptr {
92 v = v.Elem()
93 }
94 if v.Kind() != reflect.Struct {
95 return 0, fmt.Errorf("sizer: %s is not a struct", v)
96 }
97
98 f, ok := v.FieldByName(field)
99 if !ok {
100 return 0, fmt.Errorf("sizer: could not find field %s.%s", v, fie ld)
101 }
102
103 tag, err := protobufTag(f)
104 if err != nil {
105 return 0, fmt.Errorf("sizer: field %s.%s has no protobuf tag: %s ", v, field, err)
106 }
107
108 // Protobuf encodes the tag and wire type in the same varint. It does th is
109 // by allocating three bits for wire type at the base of the tag.
110 //
111 // https://developers.google.com/protocol-buffers/docs/encoding#structur e
112 return varintLength(uint64(tag) << 3), nil
113 }
114
115 func varintLength(val uint64) int {
116 switch {
117 case val < 0x80:
118 return 1
119 case val < 0x4000:
120 return 2
121 case val < 0x200000:
122 return 3
123 case val < 0x10000000:
124 return 4
125 case val < 0x800000000:
126 return 5
127 case val < 0x40000000000:
128 return 6
129 case val < 0x2000000000000:
130 return 7
131 case val < 0x100000000000000:
132 return 8
133 case val < 0x8000000000000000:
134 return 9
135 default:
136 // Maximum uvarint size.
137 return 10
138 }
139 }
140
141 func protobufTag(f reflect.StructField) (int, error) {
142 // If this field doesn't have a "protobuf" tag, ignore it.
143 value := f.Tag.Get("protobuf")
144 parts := strings.Split(value, ",")
145 if len(parts) < 2 {
146 return 0, errMalformedProtobufField
147 }
148 tag, err := strconv.Atoi(parts[1])
149 if err != nil {
150 return 0, errMalformedProtobufField
151 }
152 return tag, nil
153 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698