github.com/m3db/m3@v1.5.0/src/query/api/v1/handler/graphite/pickle/pickle_writer.go (about) 1 // Copyright (c) 2019 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package pickle 22 23 import ( 24 "bufio" 25 "encoding/binary" 26 "io" 27 "math" 28 ) 29 30 var ( 31 programStart = []uint8{opProto, 0x2} 32 programEnd = []uint8{opStop} 33 listStart = []uint8{opEmptyList, opMark} 34 dictStart = []uint8{opEmptyDict, opMark} 35 ) 36 37 // A Writer is capable writing out the opcodes required by the pickle format. 38 // Note that this is a very limited implementation of pickling; just enough for 39 // us to implement the opcodes required by graphite /render. 40 type Writer struct { 41 w *bufio.Writer 42 buf [8]byte 43 err error 44 } 45 46 // NewWriter creates a new pickle writer. 47 func NewWriter(w io.Writer) *Writer { 48 pw := &Writer{ 49 w: bufio.NewWriter(w), 50 } 51 52 _, pw.err = pw.w.Write(programStart) 53 return pw 54 } 55 56 // BeginDict starts marshalling a python dict. 57 func (p *Writer) BeginDict() { 58 if p.err != nil { 59 return 60 } 61 62 if _, p.err = p.w.Write(dictStart); p.err != nil { 63 return 64 } 65 } 66 67 // WriteDictKey writes a dictionary key. 68 func (p *Writer) WriteDictKey(s string) { 69 p.WriteString(s) 70 } 71 72 // EndDict ends marshalling a python dict. 73 func (p *Writer) EndDict() { 74 if p.err != nil { 75 return 76 } 77 78 p.err = p.w.WriteByte(opSetItems) 79 } 80 81 // BeginList begins writing a new python list. 82 func (p *Writer) BeginList() { 83 if p.err != nil { 84 return 85 } 86 87 _, p.err = p.w.Write(listStart) 88 } 89 90 // EndList ends writing a python list. 91 func (p *Writer) EndList() { 92 if p.err != nil { 93 return 94 } 95 96 p.w.WriteByte(opAppends) 97 } 98 99 // WriteNone writes a python `None`. 100 func (p *Writer) WriteNone() { 101 if p.err != nil { 102 return 103 } 104 105 p.err = p.w.WriteByte(opNone) 106 } 107 108 // WriteFloat64 writes a float64 value. NaNs are converted in `None`. 109 func (p *Writer) WriteFloat64(v float64) { 110 if math.IsNaN(v) { 111 p.WriteNone() 112 return 113 } 114 115 if p.err != nil { 116 return 117 } 118 119 if p.err = p.w.WriteByte(opBinFloat); p.err != nil { 120 return 121 } 122 123 binary.BigEndian.PutUint64(p.buf[:], math.Float64bits(v)) 124 _, p.err = p.w.Write(p.buf[:]) 125 } 126 127 // WriteString writes a python string. 128 func (p *Writer) WriteString(s string) { 129 if p.err != nil { 130 return 131 } 132 133 if p.err = p.w.WriteByte(opBinUnicode); p.err != nil { 134 return 135 } 136 137 binary.LittleEndian.PutUint32(p.buf[:4], uint32(len(s))) 138 if _, p.err = p.w.Write(p.buf[:4]); p.err != nil { 139 return 140 } 141 142 _, p.err = p.w.WriteString(s) 143 } 144 145 // WriteInt writes an int value. 146 func (p *Writer) WriteInt(n int) { 147 if p.err != nil { 148 return 149 } 150 151 if p.err = p.w.WriteByte(opBinInt); p.err != nil { 152 return 153 } 154 155 binary.LittleEndian.PutUint32(p.buf[:4], uint32(n)) 156 _, p.err = p.w.Write(p.buf[:4]) 157 } 158 159 // Close closes the writer, marking the end of the stream and flushing any 160 // pending values. 161 func (p *Writer) Close() error { 162 if p.err != nil { 163 return p.err 164 } 165 166 if _, p.err = p.w.Write(programEnd); p.err != nil { 167 return p.err 168 } 169 170 if p.err = p.w.Flush(); p.err != nil { 171 return p.err 172 } 173 174 return nil 175 }