Example #1
0
func (dec *sioDecoder) Decode() (messages []Message, err error) {
	messages = make([]Message, 0, 1)
	var c rune

L:
	for {
		if dec.state == sioDecodeStateBegin {
			dec.msg = &sioMessage{}
			dec.state = sioDecodeStateHeaderBegin
			dec.buf.Reset()
		}

		c, _, err = dec.src.ReadRune()
		if err != nil {
			break
		}

		switch dec.state {
		case sioDecodeStateHeaderBegin:
			dec.buf.WriteRune(c)
			if dec.buf.Len() == len(sioFrameDelim) {
				if !bytes.Equal(dec.buf.Bytes(), sioFrameDelim) {
					dec.Reset()
					return nil, errors.New("Malformed header")
				}

				dec.state = sioDecodeStateLength
				dec.buf.Reset()
			}
			continue

		case sioDecodeStateLength:
			if c >= '0' && c <= '9' {
				dec.buf.WriteRune(c)
				continue
			}

			if dec.length, err = strconv.Atoi(dec.buf.String()); err != nil {
				dec.Reset()
				return nil, err
			}

			dec.buf.Reset()
			dec.state = sioDecodeStateHeaderEnd
			fallthrough

		case sioDecodeStateHeaderEnd:
			dec.buf.WriteRune(c)
			if dec.buf.Len() < len(sioFrameDelim) {
				continue
			}

			if !bytes.Equal(dec.buf.Bytes(), sioFrameDelim) {
				dec.Reset()
				return nil, errors.New("Malformed header")
			}

			dec.state = sioDecodeStateData
			dec.buf.Reset()
			if dec.length > 0 {
				continue
			}
			fallthrough

		case sioDecodeStateData:
			if dec.length > 0 {
				dec.buf.WriteRune(c)
				dec.length--

				utf8str := utf8string.NewString(dec.src.String())

				if utf8str.RuneCount() >= dec.length {
					str := utf8str.Slice(0, dec.length)
					dec.buf.WriteString(str)
					dec.src.Next(len(str))
					dec.length = 0
				} else {
					break L
				}
			}

			data := dec.buf.Bytes()
			dec.msg.typ = sioMessageTypeMessage

			if bytes.HasPrefix(data, sioFrameDelimJSON) {
				dec.msg.annotations = make(map[string]string)
				dec.msg.annotations[SIOAnnotationJSON] = ""
				data = data[len(sioFrameDelimJSON):]
			} else if bytes.HasPrefix(data, sioFrameDelimHeartbeat) {
				dec.msg.typ = sioMessageTypeHeartbeat
				data = data[len(sioFrameDelimHeartbeat):]
			}
			dec.msg.data = make([]byte, len(data))
			copy(dec.msg.data, data)

			messages = append(messages, dec.msg)

			dec.state = sioDecodeStateBegin
			dec.buf.Reset()
			continue
		}

		dec.buf.WriteRune(c)
	}

	if err == io.EOF {
		err = nil
	}

	return
}
func (dec *sioStreamingDecoder) Decode() (messages []Message, err error) {
	messages = make([]Message, 0, 1)
	var c rune
	var typ uint64

L:
	for {
		c, _, err = dec.src.ReadRune()
		if err != nil {
			break
		}

		if dec.state == sioStreamingDecodeStateBegin {
			dec.msg = &sioMessage{}
			dec.state = sioStreamingDecodeStateType
			dec.buf.Reset()
		}

		switch dec.state {
		case sioStreamingDecodeStateType:
			if c == ':' {
				if typ, err = strconv.ParseUint(dec.buf.String(), 10, 0); err != nil {
					dec.Reset()
					return nil, err
				}
				dec.msg.typ = uint8(typ)
				dec.buf.Reset()
				dec.state = sioStreamingDecodeStateLength
				continue
			}

		case sioStreamingDecodeStateLength:
			if c == ':' {
				if dec.length, err = strconv.Atoi(dec.buf.String()); err != nil {
					dec.Reset()
					return nil, err
				}
				dec.buf.Reset()

				switch dec.msg.typ {
				case sioMessageTypeMessage:
					dec.state = sioStreamingDecodeStateAnnotationKey

				case sioMessageTypeDisconnect:
					dec.state = sioStreamingDecodeStateTrailer

				default:
					dec.state = sioStreamingDecodeStateData
				}

				continue
			}

		case sioStreamingDecodeStateAnnotationKey:
			dec.length--

			switch c {
			case ':':
				if dec.buf.Len() == 0 {
					dec.state = sioStreamingDecodeStateData
				} else {
					dec.key = dec.buf.String()
					dec.buf.Reset()
					dec.state = sioStreamingDecodeStateAnnotationValue
				}

				continue

			case '\n':
				if dec.buf.Len() == 0 {
					dec.Reset()
					return nil, errors.New("expecting key, but got...")
				}
				dec.key = dec.buf.String()
				if dec.msg.annotations == nil {
					dec.msg.annotations = make(map[string]string)
				}

				dec.msg.annotations[dec.key] = ""
				dec.buf.Reset()

				continue
			}

		case sioStreamingDecodeStateAnnotationValue:
			dec.length--

			if c == '\n' || c == ':' {
				dec.value = dec.buf.String()
				if dec.msg.annotations == nil {
					dec.msg.annotations = make(map[string]string)
				}

				dec.msg.annotations[dec.key] = dec.value
				dec.buf.Reset()

				if c == '\n' {
					dec.state = sioStreamingDecodeStateAnnotationKey
				} else {
					dec.state = sioStreamingDecodeStateData
				}
				continue
			}

		case sioStreamingDecodeStateData:
			if dec.length > 0 {
				dec.buf.WriteRune(c)
				dec.length--

				utf8str := utf8string.NewString(dec.src.String())
				if utf8str.RuneCount() >= dec.length {
					str := utf8str.Slice(0, dec.length)
					dec.buf.WriteString(str)
					dec.src.Next(len(str))
					dec.length = 0
					continue
				} else {
					break L
				}
			}

			data := dec.buf.Bytes()
			dec.msg.data = make([]byte, len(data))
			copy(dec.msg.data, data)

			dec.buf.Reset()
			dec.state = sioStreamingDecodeStateTrailer
			fallthrough

		case sioStreamingDecodeStateTrailer:
			if c == ',' {
				messages = append(messages, dec.msg)
				dec.state = sioStreamingDecodeStateBegin
				continue
			} else {
				dec.Reset()
				return nil, errors.New("Expecting trailer but got... " + string(c))
			}
		}

		dec.buf.WriteRune(c)
	}

	if err == io.EOF {
		err = nil
	}

	return
}