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 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
package gatt
import "encoding/binary"
// l2capWriter helps create l2cap responses.
// It is not meant to be used with large writes.
// TODO: benchmark the number of allocs here.
// Reduce by letting WriteByteFit, WriteUint16Fit, etc.
// extend b/chunk and write into it directly.
type l2capWriter struct {
mtu int
b []byte
chunk []byte
chunked bool
}
func newL2capWriter(mtu uint16) *l2capWriter {
return &l2capWriter{mtu: int(mtu), b: make([]byte, 0, mtu)}
}
// Chunk starts writing a new chunk. This chunk
// is not committed until Commit is called.
// Chunk panics if another chunk has already been
// started and not committed.
func (w *l2capWriter) Chunk() {
if w.chunked {
panic("l2capWriter: chunk called twice without committing")
}
w.chunked = true
if w.chunk == nil {
w.chunk = make([]byte, 0, w.mtu)
}
}
// Commit writes the current chunk and reports whether the
// write succeeded. The write succeeds iff there is enough room.
// Commit panics if no chunk has been started.
func (w *l2capWriter) Commit() bool {
if !w.chunked {
panic("l2capWriter: commit without starting a chunk")
}
var success bool
if len(w.b)+len(w.chunk) <= w.mtu {
success = true
w.b = append(w.b, w.chunk...)
}
w.chunk = w.chunk[:0]
w.chunked = false
return success
}
// CommitFit writes as much of the current chunk as possible,
// truncating as needed.
// CommitFit panics if no chunk has been started.
func (w *l2capWriter) CommitFit() {
if !w.chunked {
panic("l2capWriter: CommitFit without starting a chunk")
}
writeable := w.mtu - len(w.b)
if writeable > len(w.chunk) {
writeable = len(w.chunk)
}
w.b = append(w.b, w.chunk[:writeable]...)
w.chunk = w.chunk[:0]
w.chunked = false
}
// WriteByteFit writes b.
// It reports whether the write succeeded,
// using the criteria of WriteFit.
func (w *l2capWriter) WriteByteFit(b byte) bool {
return w.WriteFit([]byte{b})
}
// WriteUint16Fit writes v using BLE (LittleEndian) encoding.
// It reports whether the write succeeded, using the
// criteria of WriteFit.
func (w *l2capWriter) WriteUint16Fit(v uint16) bool {
b := make([]byte, 2)
binary.LittleEndian.PutUint16(b, v)
return w.WriteFit(b)
}
// WriteUUIDFit writes uuid using BLE (reversed) encoding.
// It reports whether the write succeeded, using the
// criteria of WriteFit.
func (w *l2capWriter) WriteUUIDFit(u UUID) bool {
return w.WriteFit(u.b)
}
// Writeable returns the number of bytes from b
// that would be written if pad bytes were written,
// then as much of b as fits were written. When
// writing to a chunk, any amount of bytes may be
// written.
func (w *l2capWriter) Writeable(pad int, b []byte) int {
if w.chunked {
return len(b)
}
avail := w.mtu - len(w.b) - pad
if avail > len(b) {
return len(b)
}
if avail < 0 {
return 0
}
return avail
}
// WriteFit writes as much of b as fits.
// It reports whether the write succeeded without
// truncation. A write succeeds without truncation
// iff a chunk write is in progress or the entire
// contents were written (without exceeding the mtu).
func (w *l2capWriter) WriteFit(b []byte) bool {
if w.chunked {
w.chunk = append(w.chunk, b...)
return true
}
avail := w.mtu - len(w.b)
if avail >= len(b) {
w.b = append(w.b, b...)
return true
}
w.b = append(w.b, b[:avail]...)
return false
}
// ChunkSeek discards the first offset bytes from the
// current chunk. It reports whether there were at least
// offset bytes available to discard.
// It panics if a chunked write is not in progress.
func (w *l2capWriter) ChunkSeek(offset uint16) bool {
if !w.chunked {
panic("l2capWriter: ChunkSeek requested without chunked write in progress")
}
if len(w.chunk) < int(offset) {
w.chunk = w.chunk[:0]
return false
}
w.chunk = w.chunk[offset:]
return true
}
// Bytes returns the written bytes.
// It will panic if a chunked write
// is in progress.
// It is meant to be used when writing
// is completed. It does not return a copy.
// Don't abuse this, it's not worth it.
func (w *l2capWriter) Bytes() []byte {
if w.chunked {
panic("l2capWriter: Bytes requested while chunked write in progress")
}
return w.b
}
|