| /* |
| Copyright 2012 Google Inc. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| package groupcache |
| |
| import ( |
| "errors" |
| |
| "github.com/golang/protobuf/proto" |
| ) |
| |
| // A Sink receives data from a Get call. |
| // |
| // Implementation of Getter must call exactly one of the Set methods |
| // on success. |
| type Sink interface { |
| // SetString sets the value to s. |
| SetString(s string) error |
| |
| // SetBytes sets the value to the contents of v. |
| // The caller retains ownership of v. |
| SetBytes(v []byte) error |
| |
| // SetProto sets the value to the encoded version of m. |
| // The caller retains ownership of m. |
| SetProto(m proto.Message) error |
| |
| // view returns a frozen view of the bytes for caching. |
| view() (ByteView, error) |
| } |
| |
| func cloneBytes(b []byte) []byte { |
| c := make([]byte, len(b)) |
| copy(c, b) |
| return c |
| } |
| |
| func setSinkView(s Sink, v ByteView) error { |
| // A viewSetter is a Sink that can also receive its value from |
| // a ByteView. This is a fast path to minimize copies when the |
| // item was already cached locally in memory (where it's |
| // cached as a ByteView) |
| type viewSetter interface { |
| setView(v ByteView) error |
| } |
| if vs, ok := s.(viewSetter); ok { |
| return vs.setView(v) |
| } |
| if v.b != nil { |
| return s.SetBytes(v.b) |
| } |
| return s.SetString(v.s) |
| } |
| |
| // StringSink returns a Sink that populates the provided string pointer. |
| func StringSink(sp *string) Sink { |
| return &stringSink{sp: sp} |
| } |
| |
| type stringSink struct { |
| sp *string |
| v ByteView |
| // TODO(bradfitz): track whether any Sets were called. |
| } |
| |
| func (s *stringSink) view() (ByteView, error) { |
| // TODO(bradfitz): return an error if no Set was called |
| return s.v, nil |
| } |
| |
| func (s *stringSink) SetString(v string) error { |
| s.v.b = nil |
| s.v.s = v |
| *s.sp = v |
| return nil |
| } |
| |
| func (s *stringSink) SetBytes(v []byte) error { |
| return s.SetString(string(v)) |
| } |
| |
| func (s *stringSink) SetProto(m proto.Message) error { |
| b, err := proto.Marshal(m) |
| if err != nil { |
| return err |
| } |
| s.v.b = b |
| *s.sp = string(b) |
| return nil |
| } |
| |
| // ByteViewSink returns a Sink that populates a ByteView. |
| func ByteViewSink(dst *ByteView) Sink { |
| if dst == nil { |
| panic("nil dst") |
| } |
| return &byteViewSink{dst: dst} |
| } |
| |
| type byteViewSink struct { |
| dst *ByteView |
| |
| // if this code ever ends up tracking that at least one set* |
| // method was called, don't make it an error to call set |
| // methods multiple times. Lorry's payload.go does that, and |
| // it makes sense. The comment at the top of this file about |
| // "exactly one of the Set methods" is overly strict. We |
| // really care about at least once (in a handler), but if |
| // multiple handlers fail (or multiple functions in a program |
| // using a Sink), it's okay to re-use the same one. |
| } |
| |
| func (s *byteViewSink) setView(v ByteView) error { |
| *s.dst = v |
| return nil |
| } |
| |
| func (s *byteViewSink) view() (ByteView, error) { |
| return *s.dst, nil |
| } |
| |
| func (s *byteViewSink) SetProto(m proto.Message) error { |
| b, err := proto.Marshal(m) |
| if err != nil { |
| return err |
| } |
| *s.dst = ByteView{b: b} |
| return nil |
| } |
| |
| func (s *byteViewSink) SetBytes(b []byte) error { |
| *s.dst = ByteView{b: cloneBytes(b)} |
| return nil |
| } |
| |
| func (s *byteViewSink) SetString(v string) error { |
| *s.dst = ByteView{s: v} |
| return nil |
| } |
| |
| // ProtoSink returns a sink that unmarshals binary proto values into m. |
| func ProtoSink(m proto.Message) Sink { |
| return &protoSink{ |
| dst: m, |
| } |
| } |
| |
| type protoSink struct { |
| dst proto.Message // authoritative value |
| typ string |
| |
| v ByteView // encoded |
| } |
| |
| func (s *protoSink) view() (ByteView, error) { |
| return s.v, nil |
| } |
| |
| func (s *protoSink) SetBytes(b []byte) error { |
| err := proto.Unmarshal(b, s.dst) |
| if err != nil { |
| return err |
| } |
| s.v.b = cloneBytes(b) |
| s.v.s = "" |
| return nil |
| } |
| |
| func (s *protoSink) SetString(v string) error { |
| b := []byte(v) |
| err := proto.Unmarshal(b, s.dst) |
| if err != nil { |
| return err |
| } |
| s.v.b = b |
| s.v.s = "" |
| return nil |
| } |
| |
| func (s *protoSink) SetProto(m proto.Message) error { |
| b, err := proto.Marshal(m) |
| if err != nil { |
| return err |
| } |
| // TODO(bradfitz): optimize for same-task case more and write |
| // right through? would need to document ownership rules at |
| // the same time. but then we could just assign *dst = *m |
| // here. This works for now: |
| err = proto.Unmarshal(b, s.dst) |
| if err != nil { |
| return err |
| } |
| s.v.b = b |
| s.v.s = "" |
| return nil |
| } |
| |
| // AllocatingByteSliceSink returns a Sink that allocates |
| // a byte slice to hold the received value and assigns |
| // it to *dst. The memory is not retained by groupcache. |
| func AllocatingByteSliceSink(dst *[]byte) Sink { |
| return &allocBytesSink{dst: dst} |
| } |
| |
| type allocBytesSink struct { |
| dst *[]byte |
| v ByteView |
| } |
| |
| func (s *allocBytesSink) view() (ByteView, error) { |
| return s.v, nil |
| } |
| |
| func (s *allocBytesSink) setView(v ByteView) error { |
| if v.b != nil { |
| *s.dst = cloneBytes(v.b) |
| } else { |
| *s.dst = []byte(v.s) |
| } |
| s.v = v |
| return nil |
| } |
| |
| func (s *allocBytesSink) SetProto(m proto.Message) error { |
| b, err := proto.Marshal(m) |
| if err != nil { |
| return err |
| } |
| return s.setBytesOwned(b) |
| } |
| |
| func (s *allocBytesSink) SetBytes(b []byte) error { |
| return s.setBytesOwned(cloneBytes(b)) |
| } |
| |
| func (s *allocBytesSink) setBytesOwned(b []byte) error { |
| if s.dst == nil { |
| return errors.New("nil AllocatingByteSliceSink *[]byte dst") |
| } |
| *s.dst = cloneBytes(b) // another copy, protecting the read-only s.v.b view |
| s.v.b = b |
| s.v.s = "" |
| return nil |
| } |
| |
| func (s *allocBytesSink) SetString(v string) error { |
| if s.dst == nil { |
| return errors.New("nil AllocatingByteSliceSink *[]byte dst") |
| } |
| *s.dst = []byte(v) |
| s.v.b = nil |
| s.v.s = v |
| return nil |
| } |
| |
| // TruncatingByteSliceSink returns a Sink that writes up to len(*dst) |
| // bytes to *dst. If more bytes are available, they're silently |
| // truncated. If fewer bytes are available than len(*dst), *dst |
| // is shrunk to fit the number of bytes available. |
| func TruncatingByteSliceSink(dst *[]byte) Sink { |
| return &truncBytesSink{dst: dst} |
| } |
| |
| type truncBytesSink struct { |
| dst *[]byte |
| v ByteView |
| } |
| |
| func (s *truncBytesSink) view() (ByteView, error) { |
| return s.v, nil |
| } |
| |
| func (s *truncBytesSink) SetProto(m proto.Message) error { |
| b, err := proto.Marshal(m) |
| if err != nil { |
| return err |
| } |
| return s.setBytesOwned(b) |
| } |
| |
| func (s *truncBytesSink) SetBytes(b []byte) error { |
| return s.setBytesOwned(cloneBytes(b)) |
| } |
| |
| func (s *truncBytesSink) setBytesOwned(b []byte) error { |
| if s.dst == nil { |
| return errors.New("nil TruncatingByteSliceSink *[]byte dst") |
| } |
| n := copy(*s.dst, b) |
| if n < len(*s.dst) { |
| *s.dst = (*s.dst)[:n] |
| } |
| s.v.b = b |
| s.v.s = "" |
| return nil |
| } |
| |
| func (s *truncBytesSink) SetString(v string) error { |
| if s.dst == nil { |
| return errors.New("nil TruncatingByteSliceSink *[]byte dst") |
| } |
| n := copy(*s.dst, v) |
| if n < len(*s.dst) { |
| *s.dst = (*s.dst)[:n] |
| } |
| s.v.b = nil |
| s.v.s = v |
| return nil |
| } |