blob: 1ab80d2969f60949fa92357c77ec82aa197d62b3 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package osp
import (
"encoding/binary"
"errors"
"io"
)
const (
maxInt6 = 1<<5 - 1
maxInt14 = 1<<13 - 1
maxInt30 = 1<<29 - 1
maxInt62 = 1<<63 - 1
)
// https://tools.ietf.org/html/draft-ietf-quic-transport-16#section-16
func ReadVaruint(r io.Reader) (uint64, error) {
b0, err := readByte(r)
if err != nil {
return 0, err
}
var a [8]byte
b := a[:]
e := binary.BigEndian
b[0] = last6Bits(b0)
switch first2Bits(b0) {
case 0:
return uint64(b[0]), nil
case 1:
err = readBytes(r, b[1:2])
return uint64(e.Uint16(b)), err
case 2:
err = readBytes(r, b[1:4])
return uint64(e.Uint32(b)), err
case 3:
err = readBytes(r, b[1:8])
return uint64(e.Uint64(b)), err
}
return 0, nil
}
// https://tools.ietf.org/html/draft-ietf-quic-transport-16#section-16
func WriteVaruint(v uint64, w io.Writer) error {
var a [8]byte
b := a[:]
e := binary.BigEndian
if v <= maxInt6 {
b[0] = byte(v)
setFirst2Bits(0, b)
return writeBytes(b[:1], w)
} else if v <= maxInt14 {
e.PutUint16(b, uint16(v))
setFirst2Bits(1, b)
return writeBytes(b[:2], w)
} else if v <= maxInt30 {
e.PutUint32(b, uint32(v))
setFirst2Bits(2, b)
return writeBytes(b[:4], w)
} else if v <= maxInt62 {
e.PutUint64(b, v)
setFirst2Bits(3, b)
return writeBytes(b[:8], w)
}
return errors.New("Too big")
}
func first2Bits(b byte) byte {
return b >> 6
}
func last6Bits(b byte) byte {
return b & 0x3f // 0b00111111
}
func setFirst2Bits(first byte, b []byte) {
b[0] = (first << 6) | b[0]
}
func readByte(r io.Reader) (byte, error) {
var b [1]byte
err := readBytes(r, b[:])
return b[0], err
}
// Read len(b) bytes from r into b
// If you hit an error (of the end), propogate the error from r.Read
func readBytes(r io.Reader, b []byte) error {
start := 0
end := len(b)
for start < end {
n, err := r.Read(b[start:end])
if err != nil {
return err
}
start += n
}
return nil
}
// Write len(b) bytes from b to w
// If you hit an error, propogate the error from w.Write
func writeBytes(b []byte, w io.Writer) error {
start := 0
end := len(b)
for start < end {
n, err := w.Write(b[start:end])
if err != nil {
return err
}
start += n
}
return nil
}