|  | /* | 
|  | Copyright 2016 The Transicator Authors | 
|  |  | 
|  | 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 common | 
|  |  | 
|  | import ( | 
|  | "bytes" | 
|  | "encoding/binary" | 
|  | "errors" | 
|  | "fmt" | 
|  | "regexp" | 
|  | "strconv" | 
|  | ) | 
|  |  | 
|  | var sequenceRe = regexp.MustCompile("([a-fA-F0-9]+)\\.([a-fA-F0-9]+)\\.([a-fA-F0-9]+)") | 
|  | var networkByteOrder = binary.BigEndian | 
|  |  | 
|  | /* | 
|  | A Sequence represents a unique position in the list of changes. It consists | 
|  | of a postgres LSN that represents when a transaction was committed, and a | 
|  | index into the list of changes in that transaction. | 
|  | */ | 
|  | type Sequence struct { | 
|  | LSN   uint64 | 
|  | Index uint32 | 
|  | } | 
|  |  | 
|  | /* | 
|  | MakeSequence makes a new sequence from an LSN and an index. | 
|  | */ | 
|  | func MakeSequence(lsn uint64, index uint32) Sequence { | 
|  | return Sequence{ | 
|  | LSN:   lsn, | 
|  | Index: index, | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | ParseSequence parses a stringified sequence. | 
|  | */ | 
|  | func ParseSequence(s string) (Sequence, error) { | 
|  | parsed := sequenceRe.FindStringSubmatch(s) | 
|  | if parsed == nil { | 
|  | return Sequence{}, errors.New("Invalid sequence string") | 
|  | } | 
|  |  | 
|  | l1, err := strconv.ParseUint(parsed[1], 16, 32) | 
|  | if err != nil { | 
|  | return Sequence{}, fmt.Errorf("Invalid sequence: %s", err) | 
|  | } | 
|  |  | 
|  | l2, err := strconv.ParseUint(parsed[2], 16, 32) | 
|  | if err != nil { | 
|  | return Sequence{}, fmt.Errorf("Invalid sequence: %s", err) | 
|  | } | 
|  | ix, err := strconv.ParseUint(parsed[3], 16, 32) | 
|  | if err != nil { | 
|  | return Sequence{}, fmt.Errorf("Invalid sequence: %s", err) | 
|  | } | 
|  |  | 
|  | return Sequence{ | 
|  | LSN:   (l1 << 32) | l2, | 
|  | Index: uint32(ix), | 
|  | }, nil | 
|  | } | 
|  |  | 
|  | /* | 
|  | ParseSequenceBytes parses bytes written using Bytes(). | 
|  | As a convenience, an empty sequence parses to the default | 
|  | value for the Sequence type. | 
|  | */ | 
|  | func ParseSequenceBytes(b []byte) (Sequence, error) { | 
|  | if len(b) == 0 { | 
|  | return Sequence{}, nil | 
|  | } | 
|  | if len(b) != 12 { | 
|  | return Sequence{}, errors.New("Invalid byte sequence") | 
|  | } | 
|  |  | 
|  | buf := bytes.NewBuffer(b) | 
|  |  | 
|  | var lsn uint64 | 
|  | err := binary.Read(buf, networkByteOrder, &lsn) | 
|  | if err != nil { | 
|  | return Sequence{}, fmt.Errorf("Invalid byte sequence: %s", err) | 
|  | } | 
|  |  | 
|  | var ix uint32 | 
|  | err = binary.Read(buf, networkByteOrder, &ix) | 
|  | if err != nil { | 
|  | return Sequence{}, fmt.Errorf("Invalid byte sequence: %s", err) | 
|  | } | 
|  |  | 
|  | return Sequence{ | 
|  | LSN:   lsn, | 
|  | Index: ix, | 
|  | }, nil | 
|  | } | 
|  |  | 
|  | /* | 
|  | String turns the sequence into the canonical string form, which looks | 
|  | like this: "XXX.YYY.ZZZ," where each component is a hexadecimal number. | 
|  | This is different from the standard Postgres format, which includes a | 
|  | slash in the first part, in that it doesn't need URL encoding. | 
|  | */ | 
|  | func (s Sequence) String() string { | 
|  | return strconv.FormatUint(s.LSN>>32, 16) + "." + | 
|  | strconv.FormatUint(s.LSN&0xffffffff, 16) + "." + | 
|  | strconv.FormatUint(uint64(s.Index), 16) | 
|  | } | 
|  |  | 
|  | /* | 
|  | Bytes turns the sequence into an array of 12 bytes, in "network" (aka | 
|  | big-endian) byte order. | 
|  | */ | 
|  | func (s Sequence) Bytes() []byte { | 
|  | buf := &bytes.Buffer{} | 
|  | binary.Write(buf, networkByteOrder, s.LSN) | 
|  | binary.Write(buf, networkByteOrder, s.Index) | 
|  | return buf.Bytes() | 
|  | } | 
|  |  | 
|  | /* | 
|  | Compare returns if the current sequence is less than, greater to, or | 
|  | equal to the specified sequence. | 
|  | */ | 
|  | func (s Sequence) Compare(o Sequence) int { | 
|  | if s.LSN < o.LSN { | 
|  | return -1 | 
|  | } | 
|  | if s.LSN > o.LSN { | 
|  | return 1 | 
|  | } | 
|  | if s.Index < o.Index { | 
|  | return -1 | 
|  | } | 
|  | if s.Index > o.Index { | 
|  | return 1 | 
|  | } | 
|  | return 0 | 
|  | } |