-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #49 from rsocket/bugfix/composite_metadata_compati…
…bility Add new implementation of CompositeMetadata.
- Loading branch information
Showing
2 changed files
with
109 additions
and
118 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,128 +1,139 @@ | ||
package extension | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"io" | ||
"math" | ||
|
||
"github.com/rsocket/rsocket-go/internal/common" | ||
) | ||
|
||
// CompositeMetadata provides multi Metadata payloads with different MIME types. | ||
type CompositeMetadata interface { | ||
io.WriterTo | ||
// MIME returns MIME type. | ||
MIME() string | ||
// Payload returns bytes of Metadata payload. | ||
Payload() []byte | ||
|
||
encode() ([]byte, error) | ||
type CompositeMetadata []byte | ||
|
||
// CompositeMetadataScanner can be used to scan entry in CompositeMetadata. | ||
type CompositeMetadataScanner struct { | ||
offset int | ||
raw []byte | ||
} | ||
|
||
// DecodeCompositeMetadata decode bytes to composite metadata. | ||
func DecodeCompositeMetadata(raw []byte) ([]CompositeMetadata, error) { | ||
ret := make([]CompositeMetadata, 0) | ||
offset := 0 | ||
for offset < len(raw) { | ||
l, cm, err := decodeCompositeMetadataOnce(raw[offset:]) | ||
if err != nil { | ||
return nil, err | ||
} | ||
ret = append(ret, cm) | ||
offset += l | ||
// CompositeMetadataBuilder can be used to build a CompositeMetadata. | ||
type CompositeMetadataBuilder struct { | ||
k []interface{} | ||
v [][]byte | ||
} | ||
|
||
// Scanner returns a entry scanner. | ||
func (c CompositeMetadata) Scanner() *CompositeMetadataScanner { | ||
return &CompositeMetadataScanner{ | ||
offset: 0, | ||
raw: c, | ||
} | ||
return ret, nil | ||
} | ||
|
||
// NewCompositeMetadata returns a new composite metadata. | ||
func NewCompositeMetadata(mime string, metadataPayload []byte) CompositeMetadata { | ||
if found, ok := mimeTypesR[mime]; ok { | ||
return &implCompositeMetadata{ | ||
mime: found, | ||
data: metadataPayload, | ||
} | ||
// Scan returns true when scanner has more content. | ||
func (c *CompositeMetadataScanner) Scan() bool { | ||
return c.offset < len(c.raw) | ||
} | ||
|
||
// MetadataUTF8 returns current metadata in utf8 string. | ||
func (c *CompositeMetadataScanner) MetadataUTF8() (mimeType string, metadata string, err error) { | ||
mimeType, metadataRaw, err := c.Metadata() | ||
if err != nil { | ||
return | ||
} | ||
return &implCompositeMetadata{ | ||
mime: mime, | ||
data: metadataPayload, | ||
metadata = string(metadataRaw) | ||
return | ||
} | ||
|
||
// MetadataUTF8 returns current metadata bytes. | ||
func (c *CompositeMetadataScanner) Metadata() (mimeType string, metadata []byte, err error) { | ||
l, mimeType, metadata, err := c.decodeCompositeMetadataOnce(c.raw[c.offset:]) | ||
if err != nil { | ||
return | ||
} | ||
c.offset += l | ||
return | ||
} | ||
|
||
func decodeCompositeMetadataOnce(raw []byte) (l int, cm CompositeMetadata, err error) { | ||
func (c *CompositeMetadataScanner) decodeCompositeMetadataOnce(raw []byte) (length int, mimeType string, metadata []byte, err error) { | ||
m := raw[0] | ||
size := 1 | ||
idOrLen := (m << 1) >> 1 | ||
ret := &implCompositeMetadata{} | ||
if m&0x80 == 0x80 { | ||
ret.mime = MIME(idOrLen) | ||
mimeType = MIME(idOrLen).String() | ||
} else { | ||
size += int(idOrLen) | ||
ret.mime = string(raw[1 : 1+idOrLen]) | ||
mimeTypeLen := int(idOrLen) + 1 | ||
size += mimeTypeLen | ||
mimeType = string(raw[1 : 1+mimeTypeLen]) | ||
} | ||
metadataLen := common.NewUint24Bytes(raw[size : size+3]).AsInt() | ||
end := size + 3 + metadataLen | ||
ret.data = raw[size+3 : end] | ||
return end, ret, nil | ||
length = size + 3 + metadataLen | ||
metadata = raw[size+3 : length] | ||
return | ||
} | ||
|
||
type implCompositeMetadata struct { | ||
mime interface{} | ||
data []byte | ||
// PushWellKnown push a WellKnownMimeType and metadata bytes. | ||
func (c *CompositeMetadataBuilder) PushWellKnown(mimeType MIME, metadata []byte) *CompositeMetadataBuilder { | ||
c.k = append(c.k, mimeType) | ||
c.v = append(c.v, metadata) | ||
return c | ||
} | ||
|
||
func (p *implCompositeMetadata) WriteTo(w io.Writer) (n int64, err error) { | ||
var bs []byte | ||
bs, err = p.encode() | ||
if err != nil { | ||
return | ||
} | ||
var wrote int | ||
wrote, err = w.Write(bs) | ||
if err != nil { | ||
return | ||
// PushWellKnownString push a WellKnownMimeType and metadata string. | ||
func (c *CompositeMetadataBuilder) PushWellKnownString(mimeType MIME, metadata string) *CompositeMetadataBuilder { | ||
return c.PushWellKnown(mimeType, []byte(metadata)) | ||
} | ||
|
||
// Push push a custom MimeType and metadata bytes. | ||
func (c *CompositeMetadataBuilder) Push(mimeType string, metadata []byte) *CompositeMetadataBuilder { | ||
if well, ok := ParseMIME(mimeType); ok { | ||
c.k = append(c.k, well) | ||
} else { | ||
c.k = append(c.k, mimeType) | ||
} | ||
n = int64(wrote) | ||
return | ||
c.v = append(c.v, metadata) | ||
return c | ||
} | ||
|
||
func (p *implCompositeMetadata) String() string { | ||
return fmt.Sprintf("CompositeMetadata{MIME=%s,payload=%s}", p.MIME(), p.Payload()) | ||
// PushString push a custom MimeType and metadata string. | ||
func (c *CompositeMetadataBuilder) PushString(mimeType string, metadata string) *CompositeMetadataBuilder { | ||
return c.Push(mimeType, []byte(metadata)) | ||
} | ||
|
||
func (p *implCompositeMetadata) MIME() string { | ||
switch mime := p.mime.(type) { | ||
case MIME: | ||
found, ok := mimeTypes[mime] | ||
if !ok { | ||
panic(fmt.Errorf("invalid MIME ID: %d", mime)) | ||
// Build build a new CompositeMetadata. | ||
func (c *CompositeMetadataBuilder) Build() (CompositeMetadata, error) { | ||
bf := bytes.Buffer{} | ||
for i := 0; i < len(c.k); i++ { | ||
switch mimeType := c.k[i].(type) { | ||
case MIME: | ||
bf.WriteByte(0x80 | byte(mimeType)) | ||
case string: | ||
mimeTypeLen := len(mimeType) | ||
if mimeTypeLen > math.MaxInt8 { | ||
return nil, fmt.Errorf("length of MIME type is over %d", math.MaxInt8) | ||
} | ||
bf.WriteByte(byte(mimeTypeLen - 1)) | ||
bf.Write([]byte(mimeType)) | ||
default: | ||
panic("unreachable") | ||
} | ||
metadata := c.v[i] | ||
metadataLen := len(metadata) | ||
bf.Write(common.NewUint24(metadataLen).Bytes()) | ||
if metadataLen > 0 { | ||
bf.Write(metadata) | ||
} | ||
return found | ||
case string: | ||
return mime | ||
} | ||
panic("unreachable") | ||
return bf.Bytes(), nil | ||
} | ||
|
||
func (p *implCompositeMetadata) Payload() []byte { | ||
return p.data | ||
// NewCompositeMetadataBuilder returns a CompositeMetadata builder. | ||
func NewCompositeMetadataBuilder() *CompositeMetadataBuilder { | ||
return &CompositeMetadataBuilder{} | ||
} | ||
|
||
func (p *implCompositeMetadata) encode() ([]byte, error) { | ||
bs := make([]byte, 0) | ||
switch v := p.mime.(type) { | ||
case MIME: | ||
bs = append(bs, 0x80|byte(v)) | ||
case string: | ||
l := len(v) | ||
if l > math.MaxInt8 { | ||
return nil, fmt.Errorf("length of MIME type is over %d", math.MaxInt8) | ||
} | ||
bs = append(bs, byte(l)) | ||
bs = append(bs, []byte(v)...) | ||
default: | ||
panic("unreachable") | ||
} | ||
bs = append(bs, common.NewUint24(len(p.data)).Bytes()...) | ||
bs = append(bs, p.data...) | ||
return bs, nil | ||
// NewCompositeMetadataBytes returns a CompositeMetadata. | ||
func NewCompositeMetadataBytes(raw []byte) CompositeMetadata { | ||
return raw | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters