Merge pull request #9065 from gyuho/watch-id-2

*: allow user-provided watch ID to mvcc
This commit is contained in:
Gyuho Lee 2018-01-05 15:08:10 -08:00 committed by GitHub
commit 65464838ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 305 additions and 198 deletions

View File

@ -832,6 +832,7 @@ From google paxosdb paper: Our implementation hinges around a powerful primitive
| progress_notify | progress_notify is set so that the etcd server will periodically send a WatchResponse with no events to the new watcher if there are no recent events. It is useful when clients wish to recover a disconnected watcher starting from a recent known revision. The etcd server may decide how often it will send notifications based on current load. | bool |
| filters | filters filter the events at server side before it sends back to the watcher. | (slice of) FilterType |
| prev_kv | If prev_kv is set, created watcher gets the previous KV before the event happens. If the previous KV is already compacted, nothing will be returned. | bool |
| watch_id | If watch_id is provided and non-zero, it will be assigned to this watcher. Since creating a watcher in etcd is not a synchronous operation, this can be used ensure that ordering is correct when creating multiple watchers on the same stream. Creating a watcher with an ID already in use on the stream will cause an error to be returned. | int64 |

View File

@ -2283,6 +2283,11 @@
"description": "start_revision is an optional revision to watch from (inclusive). No start_revision is \"now\".",
"type": "string",
"format": "int64"
},
"watch_id": {
"description": "If watch_id is provided and non-zero, it will be assigned to this watcher.\nSince creating a watcher in etcd is not a synchronous operation,\nthis can be used ensure that ordering is correct when creating multiple\nwatchers on the same stream. Creating a watcher with an ID already in\nuse on the stream will cause an error to be returned.",
"type": "string",
"format": "int64"
}
}
},

View File

@ -367,7 +367,8 @@ func (w *watcher) closeStream(wgs *watchGrpcStream) {
}
func (w *watchGrpcStream) addSubstream(resp *pb.WatchResponse, ws *watcherStream) {
if resp.WatchId == -1 {
// check watch ID for backward compatibility (<= v3.3)
if resp.WatchId == -1 || (resp.Canceled && resp.CancelReason != "") {
// failed; no channel
close(ws.recvc)
return
@ -453,6 +454,7 @@ func (w *watchGrpcStream) run() {
// Watch() requested
case wreq := <-w.reqc:
outc := make(chan WatchResponse, 1)
// TODO: pass custom watch ID?
ws := &watcherStream{
initReq: *wreq,
id: -1,
@ -553,6 +555,7 @@ func (w *watchGrpcStream) dispatchEvent(pbresp *pb.WatchResponse) bool {
for i, ev := range pbresp.Events {
events[i] = (*Event)(ev)
}
// TODO: return watch ID?
wr := &WatchResponse{
Header: *pbresp.Header,
Events: events,

View File

@ -205,7 +205,7 @@ func (sws *serverWatchStream) recvLoop() error {
if !sws.isWatchPermitted(creq) {
wr := &pb.WatchResponse{
Header: sws.newResponseHeader(sws.watchStream.Rev()),
WatchId: -1,
WatchId: creq.WatchId,
Canceled: true,
Created: true,
CancelReason: rpctypes.ErrGRPCPermissionDenied.Error(),
@ -225,8 +225,8 @@ func (sws *serverWatchStream) recvLoop() error {
if rev == 0 {
rev = wsrev + 1
}
id := sws.watchStream.Watch(creq.Key, creq.RangeEnd, rev, filters...)
if id != -1 {
id, err := sws.watchStream.Watch(mvcc.WatchID(creq.WatchId), creq.Key, creq.RangeEnd, rev, filters...)
if err == nil {
sws.mu.Lock()
if creq.ProgressNotify {
sws.progress[id] = true
@ -240,7 +240,10 @@ func (sws *serverWatchStream) recvLoop() error {
Header: sws.newResponseHeader(wsrev),
WatchId: int64(id),
Created: true,
Canceled: id == -1,
Canceled: err != nil,
}
if err != nil {
wr.CancelReason = err.Error()
}
select {
case sws.ctrlStream <- wr:

View File

@ -1620,6 +1620,12 @@ type WatchCreateRequest struct {
// If prev_kv is set, created watcher gets the previous KV before the event happens.
// If the previous KV is already compacted, nothing will be returned.
PrevKv bool `protobuf:"varint,6,opt,name=prev_kv,json=prevKv,proto3" json:"prev_kv,omitempty"`
// If watch_id is provided and non-zero, it will be assigned to this watcher.
// Since creating a watcher in etcd is not a synchronous operation,
// this can be used ensure that ordering is correct when creating multiple
// watchers on the same stream. Creating a watcher with an ID already in
// use on the stream will cause an error to be returned.
WatchId int64 `protobuf:"varint,7,opt,name=watch_id,json=watchId,proto3" json:"watch_id,omitempty"`
}
func (m *WatchCreateRequest) Reset() { *m = WatchCreateRequest{} }
@ -1669,6 +1675,13 @@ func (m *WatchCreateRequest) GetPrevKv() bool {
return false
}
func (m *WatchCreateRequest) GetWatchId() int64 {
if m != nil {
return m.WatchId
}
return 0
}
type WatchCancelRequest struct {
// watch_id is the watcher id to cancel so that no more events are transmitted.
WatchId int64 `protobuf:"varint,1,opt,name=watch_id,json=watchId,proto3" json:"watch_id,omitempty"`
@ -5919,6 +5932,11 @@ func (m *WatchCreateRequest) MarshalTo(dAtA []byte) (int, error) {
}
i++
}
if m.WatchId != 0 {
dAtA[i] = 0x38
i++
i = encodeVarintRpc(dAtA, i, uint64(m.WatchId))
}
return i, nil
}
@ -8408,6 +8426,9 @@ func (m *WatchCreateRequest) Size() (n int) {
if m.PrevKv {
n += 2
}
if m.WatchId != 0 {
n += 1 + sovRpc(uint64(m.WatchId))
}
return n
}
@ -12309,6 +12330,25 @@ func (m *WatchCreateRequest) Unmarshal(dAtA []byte) error {
}
}
m.PrevKv = bool(v != 0)
case 7:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field WatchId", wireType)
}
m.WatchId = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowRpc
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.WatchId |= (int64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipRpc(dAtA[iNdEx:])
@ -18431,13 +18471,13 @@ var (
func init() { proto.RegisterFile("rpc.proto", fileDescriptorRpc) }
var fileDescriptorRpc = []byte{
// 3669 bytes of a gzipped FileDescriptorProto
// 3675 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x5b, 0x5b, 0x6f, 0x23, 0xc7,
0x72, 0xd6, 0x90, 0x22, 0x29, 0x16, 0x2f, 0xe2, 0xb6, 0xb4, 0xbb, 0x14, 0x77, 0x57, 0xab, 0xed,
0xbd, 0x69, 0x2f, 0x16, 0x6d, 0xd9, 0xc9, 0xc3, 0x26, 0x30, 0xac, 0x95, 0xe8, 0x95, 0x2c, 0xad,
0x24, 0x8f, 0xa8, 0xb5, 0x03, 0x38, 0x11, 0x46, 0x64, 0x4b, 0x62, 0x44, 0xce, 0x30, 0x33, 0x43,
0xae, 0xb4, 0x31, 0x12, 0xc0, 0x71, 0x82, 0xbc, 0xe4, 0x25, 0x06, 0x82, 0xc4, 0xaf, 0x41, 0x60,
0xf8, 0x07, 0x04, 0xf9, 0x0b, 0x41, 0x5e, 0x12, 0x20, 0x7f, 0xe0, 0xc0, 0xe7, 0xbc, 0x9c, 0x5f,
0xae, 0xb4, 0x31, 0x12, 0xc0, 0x71, 0x82, 0xbc, 0xe4, 0x25, 0x06, 0x82, 0x24, 0xaf, 0x41, 0x60,
0xf8, 0x07, 0x18, 0xf9, 0x0b, 0x41, 0x5e, 0x12, 0x20, 0x7f, 0xe0, 0xc0, 0xe7, 0xbc, 0x9c, 0x5f,
0x70, 0x2e, 0x4f, 0x07, 0x7d, 0x9b, 0xe9, 0xb9, 0x51, 0xb2, 0x69, 0xfb, 0x45, 0x3b, 0x5d, 0x5d,
0x5d, 0x55, 0x5d, 0xdd, 0x55, 0xd5, 0xfd, 0x35, 0x17, 0xf2, 0x76, 0xbf, 0xb5, 0xd4, 0xb7, 0x2d,
0xd7, 0x42, 0x45, 0xe2, 0xb6, 0xda, 0x0e, 0xb1, 0x87, 0xc4, 0xee, 0x1f, 0xd6, 0x66, 0x8f, 0xad,
@ -18473,7 +18513,7 @@ var fileDescriptorRpc = []byte{
0xf6, 0x84, 0xc5, 0x05, 0xdb, 0x8a, 0x85, 0xe5, 0x9b, 0xa1, 0xc5, 0x0d, 0xc4, 0x8e, 0x2e, 0x78,
0x11, 0x86, 0xf4, 0xe9, 0xd0, 0xa9, 0xa6, 0x16, 0xd2, 0x8b, 0x85, 0xe5, 0xca, 0x12, 0x0f, 0xd8,
0xa5, 0x4d, 0x72, 0xfe, 0xca, 0xe8, 0x0e, 0x88, 0x4e, 0x3b, 0x11, 0x82, 0xc9, 0x9e, 0x65, 0x13,
0xb6, 0x63, 0xa7, 0x74, 0xf6, 0x4d, 0xb7, 0x31, 0x5b, 0x34, 0xb1, 0x5b, 0x79, 0x03, 0x7f, 0xab,
0xb6, 0x63, 0xa7, 0x74, 0xf6, 0x4d, 0xb7, 0x31, 0x5b, 0x34, 0xb1, 0x5b, 0x79, 0x03, 0x7f, 0xa3,
0x01, 0xec, 0x0e, 0xdc, 0xe4, 0xd0, 0x98, 0x85, 0xcc, 0x90, 0x0a, 0x16, 0x61, 0xc1, 0x1b, 0x2c,
0x26, 0x88, 0xe1, 0x10, 0x2f, 0x26, 0x68, 0x03, 0x5d, 0x87, 0x5c, 0xdf, 0x26, 0xc3, 0x83, 0xd3,
0x21, 0x53, 0x32, 0xa5, 0x67, 0x69, 0x73, 0x73, 0x88, 0xee, 0x40, 0xb1, 0x73, 0x6c, 0x5a, 0x36,
@ -18482,7 +18522,7 @@ var fileDescriptorRpc = []byte{
0x75, 0xa1, 0xb0, 0x1a, 0x7f, 0x06, 0x68, 0x8d, 0x74, 0x89, 0x4b, 0xc6, 0xc9, 0x1e, 0x8a, 0x4f,
0xd2, 0xaa, 0x4f, 0xf0, 0x3f, 0x6b, 0x30, 0x13, 0x10, 0x3f, 0xd6, 0xb4, 0xaa, 0x90, 0x6b, 0x33,
0x61, 0xdc, 0x82, 0xb4, 0x2e, 0x9b, 0xe8, 0x09, 0x4c, 0x09, 0x03, 0x9c, 0x6a, 0x3a, 0x61, 0xd3,
0xe4, 0xb8, 0x4d, 0x0e, 0xfe, 0x36, 0x05, 0x79, 0x31, 0xd1, 0x9d, 0x3e, 0x5a, 0x81, 0x92, 0xcd,
0xe4, 0xb8, 0x4d, 0x0e, 0xfe, 0x26, 0x05, 0x79, 0x31, 0xd1, 0x9d, 0x3e, 0x5a, 0x81, 0x92, 0xcd,
0x1b, 0x07, 0x6c, 0x3e, 0xc2, 0xa2, 0x5a, 0x72, 0x12, 0x5a, 0x9f, 0xd0, 0x8b, 0x62, 0x08, 0x23,
0xa3, 0x3f, 0x81, 0x82, 0x14, 0xd1, 0x1f, 0xb8, 0xc2, 0xe5, 0xd5, 0xa0, 0x00, 0x7f, 0xff, 0xad,
0x4f, 0xe8, 0x20, 0xd8, 0x77, 0x07, 0x2e, 0x6a, 0xc2, 0xac, 0x1c, 0xcc, 0x67, 0x23, 0xcc, 0x48,
@ -18504,7 +18544,7 @@ var fileDescriptorRpc = []byte{
0xbd, 0xa2, 0xd1, 0x5a, 0xb7, 0xd5, 0xd8, 0xdb, 0xab, 0xa4, 0x50, 0x09, 0xf2, 0xdb, 0x3b, 0xcd,
0x03, 0xce, 0x95, 0xc6, 0x2f, 0x3c, 0x09, 0xa2, 0xc8, 0x29, 0xb5, 0x6d, 0x42, 0xa9, 0x6d, 0x9a,
0xac, 0x6d, 0x29, 0xbf, 0xb6, 0xb1, 0x32, 0xb7, 0xd5, 0x58, 0xd9, 0x6b, 0x54, 0x26, 0x9f, 0x97,
0xa1, 0xc8, 0xfd, 0x7b, 0x30, 0x30, 0x69, 0xa9, 0xfd, 0x77, 0x0d, 0xc0, 0x8f, 0x26, 0x54, 0x87,
0xa1, 0xc8, 0xfd, 0x7b, 0x30, 0x30, 0x69, 0xa9, 0xfd, 0x0f, 0x0d, 0xc0, 0x8f, 0x26, 0x54, 0x87,
0x5c, 0x8b, 0xeb, 0xa9, 0x6a, 0x2c, 0x19, 0x5d, 0x8d, 0x5d, 0x32, 0x5d, 0x72, 0xa1, 0x77, 0x20,
0xe7, 0x0c, 0x5a, 0x2d, 0xe2, 0xc8, 0x92, 0x77, 0x3d, 0x9c, 0x0f, 0x45, 0xb6, 0xd2, 0x25, 0x1f,
0x1d, 0x72, 0x64, 0x74, 0xba, 0x03, 0x56, 0x00, 0x47, 0x0f, 0x11, 0x7c, 0xf8, 0xdf, 0x34, 0x28,
@ -18519,147 +18559,147 @@ var fileDescriptorRpc = []byte{
0xaf, 0xc0, 0xf4, 0x9e, 0x69, 0xf4, 0x9d, 0x13, 0x4b, 0x56, 0x37, 0x3a, 0xe9, 0x8a, 0x4f, 0x1b,
0x4b, 0xe3, 0x43, 0x98, 0xb6, 0x49, 0xcf, 0xe8, 0x98, 0x1d, 0xf3, 0xf8, 0xe0, 0xf0, 0xdc, 0x25,
0x8e, 0xb8, 0x30, 0x95, 0x3d, 0xf2, 0x73, 0x4a, 0xa5, 0xa6, 0x1d, 0x76, 0xad, 0x43, 0x91, 0xe6,
0xd8, 0x37, 0xfe, 0x4f, 0x0d, 0x8a, 0x9f, 0x18, 0x6e, 0x4b, 0x2e, 0x1d, 0xda, 0x80, 0xb2, 0x97,
0xdc, 0x18, 0x45, 0xd8, 0x12, 0x2a, 0xb1, 0x6c, 0x8c, 0x3c, 0x4a, 0xcb, 0xea, 0x58, 0x6a, 0xa9,
0x04, 0x26, 0xca, 0x30, 0x5b, 0xa4, 0xeb, 0x89, 0x4a, 0x25, 0x8b, 0x62, 0x8c, 0xaa, 0x28, 0x95,
0xf0, 0x7c, 0xda, 0x3f, 0x7e, 0xf0, 0x5c, 0xf2, 0x75, 0x0a, 0x50, 0xd4, 0x86, 0xef, 0x7b, 0x22,
0xbb, 0x0f, 0x65, 0xc7, 0x35, 0xec, 0xc8, 0xde, 0x28, 0x31, 0xaa, 0x97, 0xa0, 0x1f, 0xc2, 0x74,
0xdf, 0xb6, 0x8e, 0x6d, 0xe2, 0x38, 0x07, 0xa6, 0xe5, 0x76, 0x8e, 0xce, 0xc5, 0xa1, 0xb6, 0x2c,
0xc9, 0xdb, 0x8c, 0x8a, 0x1a, 0x90, 0x3b, 0xea, 0x74, 0x5d, 0x62, 0x3b, 0xd5, 0xcc, 0x42, 0x7a,
0xb1, 0xbc, 0xfc, 0xe4, 0x22, 0xaf, 0x2d, 0x7d, 0xc8, 0xf8, 0x9b, 0xe7, 0x7d, 0xa2, 0xcb, 0xb1,
0xea, 0x41, 0x31, 0x1b, 0x38, 0x28, 0xde, 0x07, 0xf0, 0xf9, 0x69, 0xaa, 0xdd, 0xde, 0xd9, 0xdd,
0x6f, 0x56, 0x26, 0x50, 0x11, 0xa6, 0xb6, 0x77, 0xd6, 0x1a, 0x5b, 0x0d, 0x9a, 0x97, 0x71, 0x5d,
0xfa, 0x46, 0xf5, 0x21, 0x9a, 0x83, 0xa9, 0xd7, 0x94, 0x2a, 0xef, 0xdb, 0x69, 0x3d, 0xc7, 0xda,
0x1b, 0x6d, 0xfc, 0x4f, 0x29, 0x28, 0x89, 0x5d, 0x30, 0xd6, 0x56, 0x54, 0x55, 0xa4, 0x02, 0x2a,
0xe8, 0xa9, 0x94, 0xef, 0x8e, 0xb6, 0x38, 0xfc, 0xca, 0x26, 0xcd, 0x0d, 0x7c, 0xb1, 0x49, 0x5b,
0xb8, 0xd5, 0x6b, 0xc7, 0x86, 0x6f, 0x26, 0x36, 0x7c, 0xd1, 0x5d, 0x28, 0x79, 0xbb, 0xcd, 0x70,
0x44, 0xad, 0xcd, 0xeb, 0x45, 0xb9, 0x91, 0x28, 0x0d, 0xdd, 0x87, 0x2c, 0x19, 0x12, 0xd3, 0x75,
0xaa, 0x05, 0x96, 0x75, 0x4b, 0xf2, 0xfc, 0xdb, 0xa0, 0x54, 0x5d, 0x74, 0xe2, 0x3f, 0x82, 0x2b,
0xec, 0x9e, 0xf1, 0xc2, 0x36, 0x4c, 0xf5, 0x42, 0xd4, 0x6c, 0x6e, 0x09, 0xd7, 0xd1, 0x4f, 0x54,
0x86, 0xd4, 0xc6, 0x9a, 0x98, 0x68, 0x6a, 0x63, 0x0d, 0x7f, 0xa1, 0x01, 0x52, 0xc7, 0x8d, 0xe5,
0xcb, 0x90, 0x70, 0xa9, 0x3e, 0xed, 0xab, 0x9f, 0x85, 0x0c, 0xb1, 0x6d, 0xcb, 0x66, 0x5e, 0xcb,
0xeb, 0xbc, 0x81, 0xef, 0x09, 0x1b, 0x74, 0x32, 0xb4, 0x4e, 0xbd, 0xc0, 0xe0, 0xd2, 0x34, 0xcf,
0xd4, 0x4d, 0x98, 0x09, 0x70, 0x8d, 0x95, 0xfd, 0x1f, 0xc2, 0x55, 0x26, 0x6c, 0x93, 0x90, 0xfe,
0x4a, 0xb7, 0x33, 0x4c, 0xd4, 0xda, 0x87, 0x6b, 0x61, 0xc6, 0x9f, 0xd6, 0x47, 0xf8, 0x4f, 0x85,
0xc6, 0x66, 0xa7, 0x47, 0x9a, 0xd6, 0x56, 0xb2, 0x6d, 0x34, 0x3b, 0x9e, 0x92, 0x73, 0x47, 0x94,
0x49, 0xf6, 0x8d, 0xff, 0x43, 0x83, 0xeb, 0x91, 0xe1, 0x3f, 0xf1, 0xaa, 0xce, 0x03, 0x1c, 0xd3,
0xed, 0x43, 0xda, 0xb4, 0x83, 0xdf, 0xd0, 0x15, 0x8a, 0x67, 0x27, 0x4d, 0x30, 0x45, 0x61, 0xe7,
0xac, 0x58, 0x73, 0xf6, 0xc7, 0x91, 0x35, 0xe6, 0x16, 0x14, 0x18, 0x61, 0xcf, 0x35, 0xdc, 0x81,
0x13, 0x59, 0x8c, 0xbf, 0x11, 0x5b, 0x40, 0x0e, 0x1a, 0x6b, 0x5e, 0xef, 0x40, 0x96, 0x1d, 0x4e,
0xe5, 0xd1, 0x2c, 0x74, 0x1b, 0x50, 0xec, 0xd0, 0x05, 0x23, 0x3e, 0x81, 0xec, 0x4b, 0x86, 0xe8,
0x29, 0x96, 0x4d, 0xca, 0xa5, 0x30, 0x8d, 0x1e, 0xc7, 0x19, 0xf2, 0x3a, 0xfb, 0x66, 0x27, 0x19,
0x42, 0xec, 0x7d, 0x7d, 0x8b, 0x9f, 0x98, 0xf2, 0xba, 0xd7, 0xa6, 0x2e, 0x6b, 0x75, 0x3b, 0xc4,
0x74, 0x59, 0xef, 0x24, 0xeb, 0x55, 0x28, 0x78, 0x09, 0x2a, 0x5c, 0xd3, 0x4a, 0xbb, 0xad, 0x9c,
0x48, 0x3c, 0x79, 0x5a, 0x50, 0x1e, 0xfe, 0x46, 0x83, 0x2b, 0xca, 0x80, 0xb1, 0x1c, 0xf3, 0x14,
0xb2, 0x1c, 0xb7, 0x14, 0xc5, 0x6f, 0x36, 0x38, 0x8a, 0xab, 0xd1, 0x05, 0x0f, 0x5a, 0x82, 0x1c,
0xff, 0x92, 0xc7, 0xc2, 0x78, 0x76, 0xc9, 0x84, 0xef, 0xc3, 0x8c, 0x20, 0x91, 0x9e, 0x15, 0xb7,
0xb7, 0x99, 0x43, 0xf1, 0xe7, 0x30, 0x1b, 0x64, 0x1b, 0x6b, 0x4a, 0x8a, 0x91, 0xa9, 0xcb, 0x18,
0xb9, 0x22, 0x8d, 0xdc, 0xef, 0xb7, 0x95, 0x5a, 0x1d, 0x5e, 0x75, 0x75, 0x45, 0x52, 0xa1, 0x15,
0xf1, 0x26, 0x20, 0x45, 0xfc, 0xac, 0x13, 0x98, 0x91, 0xdb, 0x61, 0xab, 0xe3, 0x78, 0x27, 0xb8,
0x37, 0x80, 0x54, 0xe2, 0xcf, 0x6d, 0xd0, 0x1a, 0x39, 0xb2, 0x8d, 0xe3, 0x1e, 0xf1, 0xea, 0x13,
0x3d, 0xcf, 0xab, 0xc4, 0xb1, 0x32, 0x7a, 0x1d, 0xae, 0xbc, 0xb4, 0x86, 0x34, 0x35, 0x50, 0xaa,
0x1f, 0x32, 0xfc, 0x3e, 0xe7, 0x2d, 0x9b, 0xd7, 0xa6, 0xca, 0xd5, 0x01, 0x63, 0x29, 0xff, 0x5f,
0x0d, 0x8a, 0x2b, 0x5d, 0xc3, 0xee, 0x49, 0xc5, 0xef, 0x43, 0x96, 0xdf, 0x52, 0x04, 0x30, 0xf0,
0x20, 0x28, 0x46, 0xe5, 0xe5, 0x8d, 0x15, 0x7e, 0xa7, 0x11, 0xa3, 0xa8, 0xe1, 0xe2, 0xed, 0x60,
0x2d, 0xf4, 0x96, 0xb0, 0x86, 0xde, 0x82, 0x8c, 0x41, 0x87, 0xb0, 0x14, 0x5c, 0x0e, 0xdf, 0x0f,
0x99, 0x34, 0x76, 0x38, 0xe3, 0x5c, 0xf8, 0x3d, 0x28, 0x28, 0x1a, 0xe8, 0x0d, 0xf8, 0x45, 0x43,
0x1c, 0xc0, 0x56, 0x56, 0x9b, 0x1b, 0xaf, 0xf8, 0xc5, 0xb8, 0x0c, 0xb0, 0xd6, 0xf0, 0xda, 0x29,
0xfc, 0xa9, 0x18, 0x25, 0xf2, 0x9d, 0x6a, 0x8f, 0x96, 0x64, 0x4f, 0xea, 0x52, 0xf6, 0x9c, 0x41,
0x49, 0x4c, 0x7f, 0xdc, 0xf4, 0xcd, 0xe4, 0x25, 0xa4, 0x6f, 0xc5, 0x78, 0x5d, 0x30, 0xe2, 0x69,
0x28, 0x89, 0x84, 0x2e, 0xf6, 0xdf, 0xff, 0x68, 0x50, 0x96, 0x94, 0x71, 0x01, 0x4c, 0x89, 0xbd,
0xf0, 0x0a, 0xe0, 0x21, 0x2f, 0xd7, 0x20, 0xdb, 0x3e, 0xdc, 0xeb, 0xbc, 0x91, 0x60, 0xb3, 0x68,
0x51, 0x7a, 0x97, 0xeb, 0xe1, 0x2f, 0x3e, 0xa2, 0x45, 0x6f, 0xe1, 0xb6, 0x71, 0xe4, 0x6e, 0x98,
0x6d, 0x72, 0xc6, 0xce, 0x8d, 0x93, 0xba, 0x4f, 0x60, 0x97, 0x52, 0xf1, 0x32, 0xc4, 0x0e, 0x8b,
0xea, 0x4b, 0xd1, 0x0c, 0x5c, 0x59, 0x19, 0xb8, 0x27, 0x0d, 0xd3, 0x38, 0xec, 0xca, 0x8c, 0x45,
0xcb, 0x2c, 0x25, 0xae, 0x75, 0x1c, 0x95, 0xda, 0x80, 0x19, 0x4a, 0x25, 0xa6, 0xdb, 0x69, 0x29,
0xe9, 0x4d, 0x16, 0x31, 0x2d, 0x54, 0xc4, 0x0c, 0xc7, 0x79, 0x6d, 0xd9, 0x6d, 0x31, 0x35, 0xaf,
0x8d, 0xd7, 0xb8, 0xf0, 0x7d, 0x27, 0x50, 0xa6, 0xbe, 0xaf, 0x94, 0x45, 0x5f, 0xca, 0x0b, 0xe2,
0x8e, 0x90, 0x82, 0x9f, 0xc0, 0x55, 0xc9, 0x29, 0xc0, 0xbd, 0x11, 0xcc, 0x3b, 0x70, 0x4b, 0x32,
0xaf, 0x9e, 0xd0, 0xdb, 0xd3, 0xae, 0x50, 0xf8, 0x43, 0xed, 0x7c, 0x0e, 0x55, 0xcf, 0x4e, 0x76,
0x58, 0xb6, 0xba, 0xaa, 0x01, 0x03, 0x47, 0xec, 0x99, 0xbc, 0xce, 0xbe, 0x29, 0xcd, 0xb6, 0xba,
0xde, 0x91, 0x80, 0x7e, 0xe3, 0x55, 0x98, 0x93, 0x32, 0xc4, 0x31, 0x36, 0x28, 0x24, 0x62, 0x50,
0x9c, 0x10, 0xe1, 0x30, 0x3a, 0x74, 0xb4, 0xdb, 0x55, 0xce, 0xa0, 0x6b, 0x99, 0x4c, 0x4d, 0x91,
0x79, 0x95, 0xef, 0x08, 0x6a, 0x98, 0x5a, 0x31, 0x04, 0x99, 0x0a, 0x50, 0xc9, 0x62, 0x21, 0x28,
0x39, 0xb2, 0x10, 0x11, 0xd1, 0x9f, 0xc1, 0xbc, 0x67, 0x04, 0xf5, 0xdb, 0x2e, 0xb1, 0x7b, 0x1d,
0xc7, 0x51, 0xe0, 0xa0, 0xb8, 0x89, 0x3f, 0x80, 0xc9, 0x3e, 0x11, 0x39, 0xa5, 0xb0, 0x8c, 0x96,
0xf8, 0xfb, 0xed, 0x92, 0x32, 0x98, 0xf5, 0xe3, 0x36, 0xdc, 0x96, 0xd2, 0xb9, 0x47, 0x63, 0xc5,
0x87, 0x8d, 0x92, 0xb7, 0x6e, 0xee, 0xd6, 0xe8, 0xad, 0x3b, 0xcd, 0xd7, 0xde, 0x83, 0x28, 0x3f,
0xe2, 0x8e, 0x94, 0xb1, 0x35, 0x56, 0xad, 0xd8, 0xe4, 0x3e, 0xf5, 0x42, 0x72, 0x2c, 0x61, 0x87,
0x30, 0x1b, 0x8c, 0xe4, 0xb1, 0xd2, 0xd8, 0x2c, 0x64, 0x5c, 0xeb, 0x94, 0xc8, 0x24, 0xc6, 0x1b,
0xd2, 0x60, 0x2f, 0xcc, 0xc7, 0x32, 0xd8, 0xf0, 0x85, 0xb1, 0x2d, 0x39, 0xae, 0xbd, 0x74, 0x35,
0xe5, 0xe1, 0x8b, 0x37, 0xf0, 0x36, 0x5c, 0x0b, 0xa7, 0x89, 0xb1, 0x4c, 0x7e, 0xc5, 0x37, 0x70,
0x5c, 0x26, 0x19, 0x4b, 0xee, 0xc7, 0x7e, 0x32, 0x50, 0x12, 0xca, 0x58, 0x22, 0x75, 0xa8, 0xc5,
0xe5, 0x97, 0x1f, 0x63, 0xbf, 0x7a, 0xe9, 0x66, 0x2c, 0x61, 0x8e, 0x2f, 0x6c, 0xfc, 0xe5, 0xf7,
0x73, 0x44, 0x7a, 0x64, 0x8e, 0x10, 0x41, 0xe2, 0x67, 0xb1, 0x9f, 0x60, 0xd3, 0x09, 0x1d, 0x7e,
0x02, 0x1d, 0x57, 0x07, 0xad, 0x21, 0x9e, 0x0e, 0xd6, 0x90, 0x1b, 0x5b, 0x4d, 0xbb, 0x63, 0x2d,
0xc6, 0x27, 0x7e, 0xee, 0x8c, 0x64, 0xe6, 0xb1, 0x04, 0x7f, 0x0a, 0x0b, 0xc9, 0x49, 0x79, 0x1c,
0xc9, 0x8f, 0xeb, 0x90, 0xf7, 0x0e, 0x94, 0xca, 0x6f, 0x1f, 0x0a, 0x90, 0xdb, 0xde, 0xd9, 0xdb,
0x5d, 0x59, 0x6d, 0xf0, 0x1f, 0x3f, 0xac, 0xee, 0xe8, 0xfa, 0xfe, 0x6e, 0xb3, 0x92, 0x5a, 0xfe,
0x6d, 0x1a, 0x52, 0x9b, 0xaf, 0xd0, 0x9f, 0x43, 0x86, 0xbf, 0x04, 0x8e, 0x78, 0xfe, 0xad, 0x8d,
0x7a, 0xec, 0xc4, 0x37, 0xbe, 0xf8, 0xff, 0x5f, 0x7d, 0x95, 0xba, 0x8a, 0x2b, 0xf5, 0xe1, 0xbb,
0x87, 0xc4, 0x35, 0xea, 0xa7, 0xc3, 0x3a, 0xab, 0x0f, 0xcf, 0xb4, 0xc7, 0x68, 0x1f, 0xd2, 0xbb,
0x03, 0x17, 0x25, 0x3e, 0x0d, 0xd7, 0x92, 0xdf, 0x40, 0xf1, 0x1c, 0x13, 0x3c, 0x83, 0xcb, 0x8a,
0xe0, 0xfe, 0xc0, 0xa5, 0x62, 0x07, 0x50, 0x50, 0x5f, 0x31, 0x2f, 0x7c, 0x33, 0xae, 0x5d, 0xfc,
0x42, 0x8a, 0xef, 0x30, 0x75, 0x37, 0xf0, 0x35, 0x45, 0x1d, 0x7f, 0x6b, 0x55, 0x67, 0xd3, 0x3c,
0x33, 0x51, 0xe2, 0xab, 0x72, 0x2d, 0xf9, 0xe1, 0x34, 0x76, 0x36, 0xee, 0x99, 0x49, 0xc5, 0x9a,
0xe2, 0xdd, 0xb4, 0xe5, 0xa2, 0xdb, 0x31, 0xef, 0x66, 0xea, 0x0b, 0x51, 0x6d, 0x21, 0x99, 0x41,
0x28, 0x5a, 0x60, 0x8a, 0x6a, 0xf8, 0xaa, 0xa2, 0xa8, 0xe5, 0xb1, 0x3d, 0xd3, 0x1e, 0x2f, 0x1f,
0x43, 0x86, 0x21, 0xc4, 0xe8, 0x2f, 0xe4, 0x47, 0x2d, 0x06, 0xdb, 0x4e, 0x58, 0xfc, 0x00, 0xb6,
0x8c, 0xab, 0x4c, 0x19, 0xc2, 0x25, 0xa9, 0x8c, 0x61, 0xc4, 0xcf, 0xb4, 0xc7, 0x8b, 0xda, 0xdb,
0xda, 0xf2, 0x6f, 0x26, 0x21, 0xc3, 0xe0, 0x22, 0x64, 0x01, 0xf8, 0x68, 0x6a, 0x78, 0x96, 0x11,
0x7c, 0x36, 0x3c, 0xcb, 0x28, 0x10, 0x8b, 0xe7, 0x99, 0xe2, 0x2a, 0x9e, 0x91, 0x8a, 0x19, 0x12,
0x55, 0x67, 0xe0, 0x1a, 0xf5, 0xe9, 0x50, 0x00, 0x66, 0x3c, 0xcc, 0x50, 0x9c, 0xc0, 0x00, 0xaa,
0x1a, 0xde, 0x21, 0x31, 0x88, 0x2a, 0xc6, 0x4c, 0xe7, 0x4d, 0x7c, 0x5d, 0xf1, 0x2c, 0x57, 0x6b,
0x33, 0x46, 0xaa, 0xf7, 0xef, 0x34, 0x28, 0x07, 0x71, 0x51, 0x74, 0x37, 0x46, 0x72, 0x18, 0x5e,
0xad, 0xdd, 0x1b, 0xcd, 0x94, 0x64, 0x01, 0x57, 0x7f, 0x4a, 0x48, 0xdf, 0xa0, 0x8c, 0xc2, 0xf1,
0xe8, 0x1f, 0x34, 0x98, 0x0e, 0x81, 0x9d, 0x28, 0x4e, 0x43, 0x04, 0x4a, 0xad, 0xdd, 0xbf, 0x80,
0x4b, 0x18, 0xf2, 0x80, 0x19, 0xb2, 0x80, 0x6f, 0x44, 0x5c, 0xe1, 0x76, 0x7a, 0xc4, 0xb5, 0x84,
0x31, 0xde, 0x32, 0x70, 0x60, 0x32, 0x76, 0x19, 0x02, 0x40, 0x67, 0xec, 0x32, 0x04, 0x51, 0xcd,
0x11, 0xcb, 0xc0, 0xd1, 0x48, 0xba, 0xc5, 0x7f, 0x97, 0x86, 0xdc, 0x2a, 0xff, 0x05, 0x22, 0x72,
0x20, 0xef, 0x21, 0x80, 0x68, 0x3e, 0x0e, 0x8d, 0xf1, 0x6f, 0x0b, 0xb5, 0xdb, 0x89, 0xfd, 0x42,
0xfb, 0x7d, 0xa6, 0xfd, 0x36, 0xae, 0x49, 0xed, 0xe2, 0x87, 0x8e, 0x75, 0x7e, 0xed, 0xaf, 0x1b,
0xed, 0x36, 0x9d, 0xf8, 0xdf, 0x42, 0x51, 0x85, 0xe9, 0xd0, 0x9d, 0x58, 0x14, 0x48, 0x45, 0xfa,
0x6a, 0x78, 0x14, 0x8b, 0xd0, 0xbe, 0xc8, 0xb4, 0x63, 0x7c, 0x2b, 0x41, 0xbb, 0xcd, 0xd8, 0x03,
0x06, 0x70, 0x98, 0x2d, 0xde, 0x80, 0x00, 0x8a, 0x17, 0x6f, 0x40, 0x10, 0xa5, 0xbb, 0xd0, 0x80,
0x01, 0x63, 0xa7, 0x06, 0xbc, 0x06, 0xf0, 0x41, 0x35, 0x14, 0xeb, 0x57, 0xe5, 0xea, 0x14, 0x0e,
0xf9, 0x28, 0x1e, 0x17, 0xdd, 0x73, 0x21, 0xd5, 0xdd, 0x8e, 0x43, 0x43, 0x7f, 0xf9, 0x9b, 0x2c,
0x14, 0x5e, 0x1a, 0x1d, 0xd3, 0x25, 0xa6, 0x61, 0xb6, 0x08, 0x3a, 0x82, 0x0c, 0x2b, 0x8d, 0xe1,
0x2c, 0xa7, 0x62, 0x4d, 0xe1, 0x2c, 0x17, 0x00, 0x62, 0xf0, 0x3d, 0xa6, 0x79, 0x1e, 0xcf, 0x49,
0xcd, 0x3d, 0x5f, 0x7c, 0x9d, 0x61, 0x28, 0x74, 0xc2, 0x7f, 0x09, 0x59, 0x01, 0xcf, 0x87, 0x84,
0x05, 0xb0, 0x95, 0xda, 0xcd, 0xf8, 0xce, 0xa4, 0xed, 0xa5, 0xaa, 0x72, 0x18, 0x2f, 0xd5, 0xf5,
0x06, 0xc0, 0x07, 0x08, 0xc3, 0xce, 0x8d, 0xe0, 0x89, 0xb5, 0x85, 0x64, 0x06, 0xa1, 0xf7, 0x11,
0xd3, 0x7b, 0x17, 0xcf, 0xc7, 0xe9, 0x6d, 0x7b, 0xfc, 0x54, 0xf7, 0x21, 0x4c, 0xae, 0x1b, 0xce,
0x09, 0x0a, 0x15, 0x3b, 0xe5, 0x47, 0x03, 0xb5, 0x5a, 0x5c, 0x97, 0xd0, 0x74, 0x97, 0x69, 0xba,
0x85, 0xab, 0x71, 0x9a, 0x4e, 0x0c, 0x87, 0x56, 0x0f, 0x74, 0x02, 0x59, 0xfe, 0x3b, 0x82, 0xb0,
0x2f, 0x03, 0xbf, 0x45, 0x08, 0xfb, 0x32, 0xf8, 0xd3, 0x83, 0xcb, 0x69, 0x72, 0x61, 0x4a, 0x3e,
0xde, 0xa3, 0x5b, 0xa1, 0xa5, 0x09, 0x3e, 0xf4, 0xd7, 0xe6, 0x93, 0xba, 0x85, 0xbe, 0x87, 0x4c,
0xdf, 0x1d, 0x7c, 0x33, 0x76, 0xed, 0x04, 0xf7, 0x33, 0xed, 0xf1, 0xdb, 0x1a, 0x2d, 0x13, 0xe0,
0x83, 0xac, 0x91, 0xe8, 0x08, 0xe3, 0xb5, 0x91, 0xe8, 0x88, 0xe0, 0xb3, 0x78, 0x99, 0x29, 0x7f,
0x8a, 0x1f, 0xc6, 0x29, 0x77, 0x6d, 0xc3, 0x74, 0x8e, 0x88, 0xfd, 0x16, 0x07, 0xd3, 0x9c, 0x93,
0x4e, 0x9f, 0x46, 0xca, 0xef, 0xa7, 0x61, 0x92, 0x9e, 0x47, 0x69, 0x79, 0xf6, 0xaf, 0xf1, 0x61,
0x6b, 0x22, 0xe0, 0x59, 0xd8, 0x9a, 0x28, 0x02, 0x10, 0x2d, 0xcf, 0xec, 0xb7, 0xe6, 0x84, 0x31,
0x51, 0xaf, 0x3b, 0x50, 0x50, 0xee, 0xfa, 0x28, 0x46, 0x60, 0x10, 0x99, 0x0b, 0xd7, 0x85, 0x18,
0xa0, 0x00, 0xdf, 0x66, 0x3a, 0xe7, 0xf0, 0x6c, 0x40, 0x67, 0x9b, 0x73, 0x51, 0xa5, 0x7f, 0x0d,
0x45, 0x15, 0x13, 0x40, 0x31, 0x32, 0x43, 0xc8, 0x5f, 0x38, 0x25, 0xc6, 0x41, 0x0a, 0xd1, 0xec,
0xe0, 0xfd, 0xae, 0x5e, 0xb2, 0x52, 0xe5, 0x7d, 0xc8, 0x09, 0xa0, 0x20, 0x6e, 0xb6, 0x41, 0xa8,
0x30, 0x6e, 0xb6, 0x21, 0x94, 0x21, 0x7a, 0xcc, 0x63, 0x5a, 0xe9, 0x7d, 0x48, 0x96, 0x20, 0xa1,
0xf1, 0x05, 0x71, 0x93, 0x34, 0xfa, 0xd8, 0x57, 0x92, 0x46, 0xe5, 0x2e, 0x3a, 0x4a, 0xe3, 0x31,
0x71, 0x45, 0x2c, 0xc9, 0x7b, 0x1e, 0x4a, 0x10, 0xa8, 0xa6, 0x7c, 0x3c, 0x8a, 0x25, 0xe9, 0x54,
0xee, 0x2b, 0x15, 0xf9, 0x1e, 0x7d, 0x0e, 0xe0, 0x43, 0x1a, 0xe1, 0xd3, 0x56, 0x2c, 0x2e, 0x1a,
0x3e, 0x6d, 0xc5, 0xa3, 0x22, 0xd1, 0xfc, 0xe1, 0xeb, 0xe6, 0x17, 0x03, 0xaa, 0xfd, 0x5f, 0x34,
0x40, 0x51, 0x04, 0x04, 0x3d, 0x89, 0xd7, 0x10, 0x8b, 0xb8, 0xd6, 0x9e, 0x5e, 0x8e, 0x39, 0xa9,
0x44, 0xf8, 0x66, 0xb5, 0xd8, 0x88, 0xfe, 0x6b, 0x6a, 0xd8, 0x97, 0x1a, 0x94, 0x02, 0x10, 0x0a,
0x7a, 0x90, 0xb0, 0xc6, 0x21, 0xd0, 0xb6, 0xf6, 0xf0, 0x42, 0xbe, 0xa4, 0x93, 0x98, 0xb2, 0x23,
0xe4, 0x41, 0xfc, 0x1f, 0x35, 0x28, 0x07, 0x61, 0x17, 0x94, 0x20, 0x3f, 0x02, 0xfc, 0xd6, 0x16,
0x2f, 0x66, 0xbc, 0x78, 0xa9, 0xfc, 0xb3, 0x79, 0x1f, 0x72, 0x02, 0xac, 0x89, 0x0b, 0x88, 0x20,
0x6c, 0x1c, 0x17, 0x10, 0x21, 0xa4, 0x27, 0x21, 0x20, 0x6c, 0xab, 0x4b, 0x94, 0x10, 0x14, 0x88,
0x4e, 0x92, 0xc6, 0xd1, 0x21, 0x18, 0x82, 0x83, 0x46, 0x69, 0xf4, 0x43, 0x50, 0xc2, 0x39, 0x28,
0x41, 0xe0, 0x05, 0x21, 0x18, 0x46, 0x83, 0x12, 0x42, 0x90, 0x29, 0x55, 0x42, 0xd0, 0x07, 0x5f,
0xe2, 0x42, 0x30, 0x82, 0x88, 0xc7, 0x85, 0x60, 0x14, 0xbf, 0x49, 0x58, 0x57, 0xa6, 0x3b, 0x10,
0x82, 0x33, 0x31, 0x58, 0x0d, 0x7a, 0x9a, 0xe0, 0xd0, 0x58, 0xb0, 0xbd, 0xf6, 0xd6, 0x25, 0xb9,
0x47, 0xee, 0x7d, 0xbe, 0x14, 0x72, 0xef, 0x7f, 0xad, 0xc1, 0x6c, 0x1c, 0xd6, 0x83, 0x12, 0x74,
0x25, 0x00, 0xf5, 0xb5, 0xa5, 0xcb, 0xb2, 0x5f, 0xec, 0x35, 0x2f, 0x1a, 0x9e, 0x57, 0xfe, 0xfb,
0xbb, 0x79, 0xed, 0xff, 0xbe, 0x9b, 0xd7, 0x7e, 0xf1, 0xdd, 0xbc, 0xf6, 0xaf, 0xbf, 0x9c, 0x9f,
0x38, 0xcc, 0xb2, 0xff, 0xe1, 0xf5, 0xee, 0x1f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x74, 0x55, 0x61,
0xe6, 0x68, 0x36, 0x00, 0x00,
0xd8, 0x37, 0xfe, 0x56, 0x83, 0xe2, 0x27, 0x86, 0xdb, 0x92, 0x4b, 0x87, 0x36, 0xa0, 0xec, 0x25,
0x37, 0x46, 0x11, 0xb6, 0x84, 0x4a, 0x2c, 0x1b, 0x23, 0x8f, 0xd2, 0xb2, 0x3a, 0x96, 0x5a, 0x2a,
0x81, 0x89, 0x32, 0xcc, 0x16, 0xe9, 0x7a, 0xa2, 0x52, 0xc9, 0xa2, 0x18, 0xa3, 0x2a, 0x4a, 0x25,
0x3c, 0x9f, 0xf6, 0x8f, 0x1f, 0x3c, 0x97, 0x7c, 0x9b, 0x02, 0x14, 0xb5, 0xe1, 0xfb, 0x9e, 0xc8,
0xee, 0x43, 0xd9, 0x71, 0x0d, 0x3b, 0xb2, 0x37, 0x4a, 0x8c, 0xea, 0x25, 0xe8, 0x87, 0x30, 0xdd,
0xb7, 0xad, 0x63, 0x9b, 0x38, 0xce, 0x81, 0x69, 0xb9, 0x9d, 0xa3, 0x73, 0x71, 0xa8, 0x2d, 0x4b,
0xf2, 0x36, 0xa3, 0xa2, 0x06, 0xe4, 0x8e, 0x3a, 0x5d, 0x97, 0xd8, 0x4e, 0x35, 0xb3, 0x90, 0x5e,
0x2c, 0x2f, 0x3f, 0xb9, 0xc8, 0x6b, 0x4b, 0x1f, 0x32, 0xfe, 0xe6, 0x79, 0x9f, 0xe8, 0x72, 0xac,
0x7a, 0x50, 0xcc, 0x06, 0x0e, 0xcf, 0x73, 0x30, 0xf5, 0x9a, 0x8a, 0xa0, 0x97, 0xe2, 0x1c, 0x3f,
0xdb, 0xb1, 0xf6, 0x46, 0x1b, 0xdf, 0x07, 0xf0, 0x45, 0xd1, 0x2c, 0xbc, 0xbd, 0xb3, 0xbb, 0xdf,
0xac, 0x4c, 0xa0, 0x22, 0x4c, 0x6d, 0xef, 0xac, 0x35, 0xb6, 0x1a, 0x34, 0x65, 0xe3, 0xba, 0x74,
0x9b, 0xea, 0xde, 0x80, 0x5c, 0x2d, 0x28, 0xf7, 0x9f, 0x52, 0x50, 0x12, 0x1b, 0x64, 0xac, 0x5d,
0xaa, 0xaa, 0x48, 0x05, 0x54, 0xd0, 0x03, 0x2b, 0xdf, 0x38, 0x6d, 0x71, 0x2e, 0x96, 0x4d, 0x9a,
0x36, 0xf8, 0x3e, 0x20, 0x6d, 0xe1, 0x71, 0xaf, 0x1d, 0x1b, 0xd9, 0x99, 0xd8, 0xc8, 0x46, 0x77,
0xa1, 0xe4, 0x6d, 0x44, 0xc3, 0x11, 0x65, 0x38, 0xaf, 0x17, 0xe5, 0x1e, 0xa3, 0x34, 0x74, 0x1f,
0xb2, 0x64, 0x48, 0x4c, 0xd7, 0xa9, 0x16, 0x58, 0x42, 0x2e, 0xc9, 0xa3, 0x71, 0x83, 0x52, 0x75,
0xd1, 0x89, 0xff, 0x08, 0xae, 0xb0, 0x2b, 0xc8, 0x0b, 0xdb, 0x30, 0xd5, 0xbb, 0x52, 0xb3, 0xb9,
0x25, 0x5c, 0x47, 0x3f, 0x51, 0x19, 0x52, 0x1b, 0x6b, 0x62, 0xa2, 0xa9, 0x8d, 0x35, 0xfc, 0x85,
0x06, 0x48, 0x1d, 0x37, 0x96, 0x2f, 0x43, 0xc2, 0xa5, 0xfa, 0xb4, 0xaf, 0x7e, 0x16, 0x32, 0xc4,
0xb6, 0x2d, 0x9b, 0x79, 0x2d, 0xaf, 0xf3, 0x06, 0xbe, 0x27, 0x6c, 0xd0, 0xc9, 0xd0, 0x3a, 0xf5,
0x62, 0x86, 0x4b, 0xd3, 0x3c, 0x53, 0x37, 0x61, 0x26, 0xc0, 0x35, 0x56, 0x61, 0x78, 0x08, 0x57,
0x99, 0xb0, 0x4d, 0x42, 0xfa, 0x2b, 0xdd, 0xce, 0x30, 0x51, 0x6b, 0x1f, 0xae, 0x85, 0x19, 0x7f,
0x5a, 0x1f, 0xe1, 0x3f, 0x15, 0x1a, 0x9b, 0x9d, 0x1e, 0x69, 0x5a, 0x5b, 0xc9, 0xb6, 0xd1, 0xc4,
0x79, 0x4a, 0xce, 0x1d, 0x51, 0x41, 0xd9, 0x37, 0xfe, 0x4f, 0x0d, 0xae, 0x47, 0x86, 0xff, 0xc4,
0xab, 0x3a, 0x0f, 0x70, 0x4c, 0xb7, 0x0f, 0x69, 0xd3, 0x0e, 0x7e, 0x79, 0x57, 0x28, 0x9e, 0x9d,
0x34, 0xf7, 0x14, 0x85, 0x9d, 0xb3, 0x62, 0xcd, 0xd9, 0x1f, 0x47, 0x96, 0x9f, 0x5b, 0x50, 0x60,
0x84, 0x3d, 0xd7, 0x70, 0x07, 0x4e, 0x64, 0x31, 0xfe, 0x46, 0x6c, 0x01, 0x39, 0x68, 0xac, 0x79,
0xbd, 0x03, 0x59, 0x76, 0x6e, 0x95, 0xa7, 0xb6, 0xd0, 0x45, 0x41, 0xb1, 0x43, 0x17, 0x8c, 0xf8,
0x04, 0xb2, 0x2f, 0x19, 0xd8, 0xa7, 0x58, 0x36, 0x29, 0x97, 0xc2, 0x34, 0x7a, 0x1c, 0x82, 0xc8,
0xeb, 0xec, 0x9b, 0x1d, 0x72, 0x08, 0xb1, 0xf7, 0xf5, 0x2d, 0x7e, 0x98, 0xca, 0xeb, 0x5e, 0x9b,
0xba, 0xac, 0xd5, 0xed, 0x10, 0xd3, 0x65, 0xbd, 0x93, 0xac, 0x57, 0xa1, 0xe0, 0x25, 0xa8, 0x70,
0x4d, 0x2b, 0xed, 0xb6, 0x72, 0x58, 0xf1, 0xe4, 0x69, 0x41, 0x79, 0xf8, 0x6b, 0x0d, 0xae, 0x28,
0x03, 0xc6, 0x72, 0xcc, 0x53, 0xc8, 0x72, 0x48, 0x53, 0xd4, 0xc5, 0xd9, 0xe0, 0x28, 0xae, 0x46,
0x17, 0x3c, 0x68, 0x09, 0x72, 0xfc, 0x4b, 0x9e, 0x18, 0xe3, 0xd9, 0x25, 0x13, 0xbe, 0x0f, 0x33,
0x82, 0x44, 0x7a, 0x56, 0xdc, 0xde, 0x66, 0x0e, 0xc5, 0x9f, 0xc3, 0x6c, 0x90, 0x6d, 0xac, 0x29,
0x29, 0x46, 0xa6, 0x2e, 0x63, 0xe4, 0x8a, 0x34, 0x72, 0xbf, 0xdf, 0x56, 0xca, 0x78, 0x78, 0xd5,
0xd5, 0x15, 0x49, 0x85, 0x56, 0xc4, 0x9b, 0x80, 0x14, 0xf1, 0xb3, 0x4e, 0x60, 0x46, 0x6e, 0x87,
0xad, 0x8e, 0xe3, 0x1d, 0xee, 0xde, 0x00, 0x52, 0x89, 0x3f, 0xb7, 0x41, 0x6b, 0xe4, 0xc8, 0x36,
0x8e, 0x7b, 0xc4, 0xab, 0x4f, 0xf4, 0xa8, 0xaf, 0x12, 0xc7, 0xca, 0xe8, 0x75, 0xb8, 0xf2, 0xd2,
0x1a, 0xd2, 0xd4, 0x40, 0xa9, 0x7e, 0xc8, 0xf0, 0xab, 0x9e, 0xb7, 0x6c, 0x5e, 0x9b, 0x2a, 0x57,
0x07, 0x8c, 0xa5, 0xfc, 0x7f, 0x35, 0x28, 0xae, 0x74, 0x0d, 0xbb, 0x27, 0x15, 0xbf, 0x0f, 0x59,
0x7e, 0x81, 0x11, 0x98, 0xc1, 0x83, 0xa0, 0x18, 0x95, 0x97, 0x37, 0x56, 0xf8, 0x75, 0x47, 0x8c,
0xa2, 0x86, 0x8b, 0x67, 0x85, 0xb5, 0xd0, 0x33, 0xc3, 0x1a, 0x7a, 0x0b, 0x32, 0x06, 0x1d, 0xc2,
0x52, 0x70, 0x39, 0x7c, 0x75, 0x64, 0xd2, 0xd8, 0xb9, 0x8d, 0x73, 0xe1, 0xf7, 0xa0, 0xa0, 0x68,
0xa0, 0x97, 0xe3, 0x17, 0x0d, 0x71, 0x00, 0x5b, 0x59, 0x6d, 0x6e, 0xbc, 0xe2, 0x77, 0xe6, 0x32,
0xc0, 0x5a, 0xc3, 0x6b, 0xa7, 0xf0, 0xa7, 0x62, 0x94, 0xc8, 0x77, 0xaa, 0x3d, 0x5a, 0x92, 0x3d,
0xa9, 0x4b, 0xd9, 0x73, 0x06, 0x25, 0x31, 0xfd, 0x71, 0xd3, 0x37, 0x93, 0x97, 0x90, 0xbe, 0x15,
0xe3, 0x75, 0xc1, 0x88, 0xa7, 0xa1, 0x24, 0x12, 0xba, 0xd8, 0x7f, 0xff, 0xa3, 0x41, 0x59, 0x52,
0xc6, 0xc5, 0x36, 0x25, 0x2c, 0xc3, 0x2b, 0x80, 0x07, 0xca, 0x5c, 0x83, 0x6c, 0xfb, 0x70, 0xaf,
0xf3, 0x46, 0xe2, 0xd0, 0xa2, 0x45, 0xe9, 0x5d, 0xae, 0x87, 0x3f, 0x06, 0x89, 0x16, 0xbd, 0xa0,
0xdb, 0xc6, 0x91, 0xbb, 0x61, 0xb6, 0xc9, 0x19, 0x3b, 0x37, 0x4e, 0xea, 0x3e, 0x81, 0xdd, 0x57,
0xc5, 0xa3, 0x11, 0x3b, 0x2c, 0xaa, 0x8f, 0x48, 0x33, 0x70, 0x65, 0x65, 0xe0, 0x9e, 0x34, 0x4c,
0xe3, 0xb0, 0x2b, 0x33, 0x16, 0x2d, 0xb3, 0x94, 0xb8, 0xd6, 0x71, 0x54, 0x6a, 0x03, 0x66, 0x28,
0x95, 0x98, 0x6e, 0xa7, 0xa5, 0xa4, 0x37, 0x59, 0xc4, 0xb4, 0x50, 0x11, 0x33, 0x1c, 0xe7, 0xb5,
0x65, 0xb7, 0xc5, 0xd4, 0xbc, 0x36, 0x5e, 0xe3, 0xc2, 0xf7, 0x9d, 0x40, 0x99, 0xfa, 0xbe, 0x52,
0x16, 0x7d, 0x29, 0x2f, 0x88, 0x3b, 0x42, 0x0a, 0x7e, 0x02, 0x57, 0x25, 0xa7, 0xc0, 0xfd, 0x46,
0x30, 0xef, 0xc0, 0x2d, 0xc9, 0xbc, 0x7a, 0x42, 0x2f, 0x56, 0xbb, 0x42, 0xe1, 0x0f, 0xb5, 0xf3,
0x39, 0x54, 0x3d, 0x3b, 0xd9, 0x61, 0xd9, 0xea, 0xaa, 0x06, 0x0c, 0x1c, 0xb1, 0x67, 0xf2, 0x3a,
0xfb, 0xa6, 0x34, 0xdb, 0xea, 0x7a, 0x47, 0x02, 0xfa, 0x8d, 0x57, 0x61, 0x4e, 0xca, 0x10, 0xc7,
0xd8, 0xa0, 0x90, 0x88, 0x41, 0x71, 0x42, 0x84, 0xc3, 0xe8, 0xd0, 0xd1, 0x6e, 0x57, 0x39, 0x83,
0xae, 0x65, 0x32, 0x35, 0x45, 0xe6, 0x55, 0xbe, 0x23, 0xa8, 0x61, 0x6a, 0xc5, 0x10, 0x64, 0x2a,
0x40, 0x25, 0x8b, 0x85, 0xa0, 0xe4, 0xc8, 0x42, 0x44, 0x44, 0x7f, 0x06, 0xf3, 0x9e, 0x11, 0xd4,
0x6f, 0xbb, 0xc4, 0xee, 0x75, 0x1c, 0x47, 0x41, 0x8a, 0xe2, 0x26, 0xfe, 0x00, 0x26, 0xfb, 0x44,
0xe4, 0x94, 0xc2, 0x32, 0x5a, 0xe2, 0x4f, 0xbb, 0x4b, 0xca, 0x60, 0xd6, 0x8f, 0xdb, 0x70, 0x5b,
0x4a, 0xe7, 0x1e, 0x8d, 0x15, 0x1f, 0x36, 0x4a, 0x5e, 0xc8, 0xb9, 0x5b, 0xa3, 0x17, 0xf2, 0x34,
0x5f, 0x7b, 0x0f, 0xbd, 0xfc, 0x88, 0x3b, 0x52, 0xc6, 0xd6, 0x58, 0xb5, 0x62, 0x93, 0xfb, 0xd4,
0x0b, 0xc9, 0xb1, 0x84, 0x1d, 0xc2, 0x6c, 0x30, 0x92, 0xc7, 0x4a, 0x63, 0xb3, 0x90, 0x71, 0xad,
0x53, 0x22, 0x93, 0x18, 0x6f, 0x48, 0x83, 0xbd, 0x30, 0x1f, 0xcb, 0x60, 0xc3, 0x17, 0xc6, 0xb6,
0xe4, 0xb8, 0xf6, 0xd2, 0xd5, 0x94, 0x87, 0x2f, 0xde, 0xc0, 0xdb, 0x70, 0x2d, 0x9c, 0x26, 0xc6,
0x32, 0xf9, 0x15, 0xdf, 0xc0, 0x71, 0x99, 0x64, 0x2c, 0xb9, 0x1f, 0xfb, 0xc9, 0x40, 0x49, 0x28,
0x63, 0x89, 0xd4, 0xa1, 0x16, 0x97, 0x5f, 0x7e, 0x8c, 0xfd, 0xea, 0xa5, 0x9b, 0xb1, 0x84, 0x39,
0xbe, 0xb0, 0xf1, 0x97, 0xdf, 0xcf, 0x11, 0xe9, 0x91, 0x39, 0x42, 0x04, 0x89, 0x9f, 0xc5, 0x7e,
0x82, 0x4d, 0x27, 0x74, 0xf8, 0x09, 0x74, 0x5c, 0x1d, 0xb4, 0x86, 0x78, 0x3a, 0x58, 0x43, 0x6e,
0x6c, 0x35, 0xed, 0x8e, 0xb5, 0x18, 0x9f, 0xf8, 0xb9, 0x33, 0x92, 0x99, 0xc7, 0x12, 0xfc, 0x29,
0x2c, 0x24, 0x27, 0xe5, 0x71, 0x24, 0x3f, 0xae, 0x43, 0xde, 0x3b, 0x50, 0x2a, 0x3f, 0x8b, 0x28,
0x40, 0x6e, 0x7b, 0x67, 0x6f, 0x77, 0x65, 0xb5, 0xc1, 0x7f, 0x17, 0xb1, 0xba, 0xa3, 0xeb, 0xfb,
0xbb, 0xcd, 0x4a, 0x6a, 0xf9, 0xb7, 0x69, 0x48, 0x6d, 0xbe, 0x42, 0x7f, 0x0e, 0x19, 0xfe, 0x48,
0x38, 0xe2, 0x65, 0xb8, 0x36, 0xea, 0x1d, 0x14, 0xdf, 0xf8, 0xe2, 0xff, 0x7f, 0xf5, 0x55, 0xea,
0x2a, 0xae, 0xd4, 0x87, 0xef, 0x1e, 0x12, 0xd7, 0xa8, 0x9f, 0x0e, 0xeb, 0xac, 0x3e, 0x3c, 0xd3,
0x1e, 0xa3, 0x7d, 0x48, 0xef, 0x0e, 0x5c, 0x94, 0xf8, 0x6a, 0x5c, 0x4b, 0x7e, 0x1e, 0xc5, 0x73,
0x4c, 0xf0, 0x0c, 0x2e, 0x2b, 0x82, 0xfb, 0x03, 0x97, 0x8a, 0x1d, 0x40, 0x41, 0x7d, 0xe0, 0xbc,
0xf0, 0x39, 0xb9, 0x76, 0xf1, 0xe3, 0x29, 0xbe, 0xc3, 0xd4, 0xdd, 0xc0, 0xd7, 0x14, 0x75, 0xfc,
0x19, 0x56, 0x9d, 0x4d, 0xf3, 0xcc, 0x44, 0x89, 0x0f, 0xce, 0xb5, 0xe4, 0x37, 0xd5, 0xd8, 0xd9,
0xb8, 0x67, 0x26, 0x15, 0x6b, 0x8a, 0x27, 0xd5, 0x96, 0x8b, 0x6e, 0xc7, 0x3c, 0xa9, 0xa9, 0x8f,
0x47, 0xb5, 0x85, 0x64, 0x06, 0xa1, 0x68, 0x81, 0x29, 0xaa, 0xe1, 0xab, 0x8a, 0xa2, 0x96, 0xc7,
0xf6, 0x4c, 0x7b, 0xbc, 0x7c, 0x0c, 0x19, 0x86, 0x10, 0xa3, 0xbf, 0x90, 0x1f, 0xb5, 0x18, 0xd8,
0x3b, 0x61, 0xf1, 0x03, 0xd8, 0x32, 0xae, 0x32, 0x65, 0x08, 0x97, 0xa4, 0x32, 0x86, 0x11, 0x3f,
0xd3, 0x1e, 0x2f, 0x6a, 0x6f, 0x6b, 0xcb, 0xbf, 0x99, 0x84, 0x0c, 0x83, 0x8b, 0x90, 0x05, 0xe0,
0xa3, 0xa9, 0xe1, 0x59, 0x46, 0xf0, 0xd9, 0xf0, 0x2c, 0xa3, 0x40, 0x2c, 0x9e, 0x67, 0x8a, 0xab,
0x78, 0x46, 0x2a, 0x66, 0x48, 0x54, 0x9d, 0x81, 0x6b, 0xd4, 0xa7, 0x43, 0x01, 0x98, 0xf1, 0x30,
0x43, 0x71, 0x02, 0x03, 0xa8, 0x6a, 0x78, 0x87, 0xc4, 0x20, 0xaa, 0x18, 0x33, 0x9d, 0x37, 0xf1,
0x75, 0xc5, 0xb3, 0x5c, 0xad, 0xcd, 0x18, 0xa9, 0xde, 0xbf, 0xd3, 0xa0, 0x1c, 0xc4, 0x45, 0xd1,
0xdd, 0x18, 0xc9, 0x61, 0x78, 0xb5, 0x76, 0x6f, 0x34, 0x53, 0x92, 0x05, 0x5c, 0xfd, 0x29, 0x21,
0x7d, 0x83, 0x32, 0x0a, 0xc7, 0xa3, 0x7f, 0xd0, 0x60, 0x3a, 0x04, 0x76, 0xa2, 0x38, 0x0d, 0x11,
0x28, 0xb5, 0x76, 0xff, 0x02, 0x2e, 0x61, 0xc8, 0x03, 0x66, 0xc8, 0x02, 0xbe, 0x11, 0x71, 0x85,
0xdb, 0xe9, 0x11, 0xd7, 0x12, 0xc6, 0x78, 0xcb, 0xc0, 0x81, 0xc9, 0xd8, 0x65, 0x08, 0x00, 0x9d,
0xb1, 0xcb, 0x10, 0x44, 0x35, 0x47, 0x2c, 0x03, 0x47, 0x23, 0xe9, 0x16, 0xff, 0x5d, 0x1a, 0x72,
0xab, 0xfc, 0xc7, 0x89, 0xc8, 0x81, 0xbc, 0x87, 0x00, 0xa2, 0xf9, 0x38, 0x34, 0xc6, 0xbf, 0x2d,
0xd4, 0x6e, 0x27, 0xf6, 0x0b, 0xed, 0xf7, 0x99, 0xf6, 0xdb, 0xb8, 0x26, 0xb5, 0x8b, 0xdf, 0x40,
0xd6, 0xf9, 0xb5, 0xbf, 0x6e, 0xb4, 0xdb, 0x74, 0xe2, 0x7f, 0x0b, 0x45, 0x15, 0xa6, 0x43, 0x77,
0x62, 0x51, 0x20, 0x15, 0xe9, 0xab, 0xe1, 0x51, 0x2c, 0x42, 0xfb, 0x22, 0xd3, 0x8e, 0xf1, 0xad,
0x04, 0xed, 0x36, 0x63, 0x0f, 0x18, 0xc0, 0x61, 0xb6, 0x78, 0x03, 0x02, 0x28, 0x5e, 0xbc, 0x01,
0x41, 0x94, 0xee, 0x42, 0x03, 0x06, 0x8c, 0x9d, 0x1a, 0xf0, 0x1a, 0xc0, 0x07, 0xd5, 0x50, 0xac,
0x5f, 0x95, 0xab, 0x53, 0x38, 0xe4, 0xa3, 0x78, 0x5c, 0x74, 0xcf, 0x85, 0x54, 0x77, 0x3b, 0x0e,
0x0d, 0xfd, 0xe5, 0xaf, 0xb3, 0x50, 0x78, 0x69, 0x74, 0x4c, 0x97, 0x98, 0x86, 0xd9, 0x22, 0xe8,
0x08, 0x32, 0xac, 0x34, 0x86, 0xb3, 0x9c, 0x8a, 0x35, 0x85, 0xb3, 0x5c, 0x00, 0x88, 0xc1, 0xf7,
0x98, 0xe6, 0x79, 0x3c, 0x27, 0x35, 0xf7, 0x7c, 0xf1, 0x75, 0x86, 0xa1, 0xd0, 0x09, 0xff, 0x25,
0x64, 0x05, 0x3c, 0x1f, 0x12, 0x16, 0xc0, 0x56, 0x6a, 0x37, 0xe3, 0x3b, 0x93, 0xb6, 0x97, 0xaa,
0xca, 0x61, 0xbc, 0x54, 0xd7, 0x1b, 0x00, 0x1f, 0x20, 0x0c, 0x3b, 0x37, 0x82, 0x27, 0xd6, 0x16,
0x92, 0x19, 0x84, 0xde, 0x47, 0x4c, 0xef, 0x5d, 0x3c, 0x1f, 0xa7, 0xb7, 0xed, 0xf1, 0x53, 0xdd,
0x87, 0x30, 0xb9, 0x6e, 0x38, 0x27, 0x28, 0x54, 0xec, 0x94, 0xdf, 0x13, 0xd4, 0x6a, 0x71, 0x5d,
0x42, 0xd3, 0x5d, 0xa6, 0xe9, 0x16, 0xae, 0xc6, 0x69, 0x3a, 0x31, 0x1c, 0x5a, 0x3d, 0xd0, 0x09,
0x64, 0xf9, 0x4f, 0x0c, 0xc2, 0xbe, 0x0c, 0xfc, 0x4c, 0x21, 0xec, 0xcb, 0xe0, 0xaf, 0x12, 0x2e,
0xa7, 0xc9, 0x85, 0x29, 0xf9, 0xae, 0x8f, 0x6e, 0x85, 0x96, 0x26, 0xf8, 0x1b, 0x80, 0xda, 0x7c,
0x52, 0xb7, 0xd0, 0xf7, 0x90, 0xe9, 0xbb, 0x83, 0x6f, 0xc6, 0xae, 0x9d, 0xe0, 0x7e, 0xa6, 0x3d,
0x7e, 0x5b, 0xa3, 0x65, 0x02, 0x7c, 0x90, 0x35, 0x12, 0x1d, 0x61, 0xbc, 0x36, 0x12, 0x1d, 0x11,
0x7c, 0x16, 0x2f, 0x33, 0xe5, 0x4f, 0xf1, 0xc3, 0x38, 0xe5, 0xae, 0x6d, 0x98, 0xce, 0x11, 0xb1,
0xdf, 0xe2, 0x60, 0x9a, 0x73, 0xd2, 0xe9, 0xd3, 0x48, 0xf9, 0xfd, 0x34, 0x4c, 0xd2, 0xf3, 0x28,
0x2d, 0xcf, 0xfe, 0x35, 0x3e, 0x6c, 0x4d, 0x04, 0x3c, 0x0b, 0x5b, 0x13, 0x45, 0x00, 0xa2, 0xe5,
0x99, 0xfd, 0x0c, 0x9d, 0x30, 0x26, 0xea, 0x75, 0x07, 0x0a, 0xca, 0x5d, 0x1f, 0xc5, 0x08, 0x0c,
0x22, 0x73, 0xe1, 0xba, 0x10, 0x03, 0x14, 0xe0, 0xdb, 0x4c, 0xe7, 0x1c, 0x9e, 0x0d, 0xe8, 0x6c,
0x73, 0x2e, 0xaa, 0xf4, 0xaf, 0xa1, 0xa8, 0x62, 0x02, 0x28, 0x46, 0x66, 0x08, 0xf9, 0x0b, 0xa7,
0xc4, 0x38, 0x48, 0x21, 0x9a, 0x1d, 0xbc, 0x9f, 0xdc, 0x4b, 0x56, 0xaa, 0xbc, 0x0f, 0x39, 0x01,
0x14, 0xc4, 0xcd, 0x36, 0x08, 0x15, 0xc6, 0xcd, 0x36, 0x84, 0x32, 0x44, 0x8f, 0x79, 0x4c, 0x2b,
0xbd, 0x0f, 0xc9, 0x12, 0x24, 0x34, 0xbe, 0x20, 0x6e, 0x92, 0x46, 0x1f, 0xfb, 0x4a, 0xd2, 0xa8,
0xdc, 0x45, 0x47, 0x69, 0x3c, 0x26, 0xae, 0x88, 0x25, 0x79, 0xcf, 0x43, 0x09, 0x02, 0xd5, 0x94,
0x8f, 0x47, 0xb1, 0x24, 0x9d, 0xca, 0x7d, 0xa5, 0x22, 0xdf, 0xa3, 0xcf, 0x01, 0x7c, 0x48, 0x23,
0x7c, 0xda, 0x8a, 0xc5, 0x45, 0xc3, 0xa7, 0xad, 0x78, 0x54, 0x24, 0x9a, 0x3f, 0x7c, 0xdd, 0xfc,
0x62, 0x40, 0xb5, 0xff, 0x8b, 0x06, 0x28, 0x8a, 0x80, 0xa0, 0x27, 0xf1, 0x1a, 0x62, 0x11, 0xd7,
0xda, 0xd3, 0xcb, 0x31, 0x27, 0x95, 0x08, 0xdf, 0xac, 0x16, 0x1b, 0xd1, 0x7f, 0x4d, 0x0d, 0xfb,
0x52, 0x83, 0x52, 0x00, 0x42, 0x41, 0x0f, 0x12, 0xd6, 0x38, 0x04, 0xda, 0xd6, 0x1e, 0x5e, 0xc8,
0x97, 0x74, 0x12, 0x53, 0x76, 0x84, 0x3c, 0x88, 0xff, 0xa3, 0x06, 0xe5, 0x20, 0xec, 0x82, 0x12,
0xe4, 0x47, 0x80, 0xdf, 0xda, 0xe2, 0xc5, 0x8c, 0x17, 0x2f, 0x95, 0x7f, 0x36, 0xef, 0x43, 0x4e,
0x80, 0x35, 0x71, 0x01, 0x11, 0x84, 0x8d, 0xe3, 0x02, 0x22, 0x84, 0xf4, 0x24, 0x04, 0x84, 0x6d,
0x75, 0x89, 0x12, 0x82, 0x02, 0xd1, 0x49, 0xd2, 0x38, 0x3a, 0x04, 0x43, 0x70, 0xd0, 0x28, 0x8d,
0x7e, 0x08, 0x4a, 0x38, 0x07, 0x25, 0x08, 0xbc, 0x20, 0x04, 0xc3, 0x68, 0x50, 0x42, 0x08, 0x32,
0xa5, 0x4a, 0x08, 0xfa, 0xe0, 0x4b, 0x5c, 0x08, 0x46, 0x10, 0xf1, 0xb8, 0x10, 0x8c, 0xe2, 0x37,
0x09, 0xeb, 0xca, 0x74, 0x07, 0x42, 0x70, 0x26, 0x06, 0xab, 0x41, 0x4f, 0x13, 0x1c, 0x1a, 0x0b,
0xb6, 0xd7, 0xde, 0xba, 0x24, 0xf7, 0xc8, 0xbd, 0xcf, 0x97, 0x42, 0xee, 0xfd, 0x7f, 0xd7, 0x60,
0x36, 0x0e, 0xeb, 0x41, 0x09, 0xba, 0x12, 0x80, 0xfa, 0xda, 0xd2, 0x65, 0xd9, 0x2f, 0xf6, 0x9a,
0x17, 0x0d, 0xcf, 0x2b, 0xff, 0xfd, 0xdd, 0xbc, 0xf6, 0x7f, 0xdf, 0xcd, 0x6b, 0xbf, 0xf8, 0x6e,
0x5e, 0xfb, 0xd7, 0x5f, 0xce, 0x4f, 0x1c, 0x66, 0xd9, 0x7f, 0xfe, 0x7a, 0xf7, 0x0f, 0x01, 0x00,
0x00, 0xff, 0xff, 0xb4, 0xfd, 0xab, 0xe6, 0x83, 0x36, 0x00, 0x00,
}

View File

@ -671,6 +671,13 @@ message WatchCreateRequest {
// If prev_kv is set, created watcher gets the previous KV before the event happens.
// If the previous KV is already compacted, nothing will be returned.
bool prev_kv = 6;
// If watch_id is provided and non-zero, it will be assigned to this watcher.
// Since creating a watcher in etcd is not a synchronous operation,
// this can be used ensure that ordering is correct when creating multiple
// watchers on the same stream. Creating a watcher with an ID already in
// use on the stream will cause an error to be returned.
int64 watch_id = 7;
}
message WatchCancelRequest {

View File

@ -716,7 +716,7 @@ func TestWatchableKVWatch(t *testing.T) {
w := s.NewWatchStream()
defer w.Close()
wid := w.Watch([]byte("foo"), []byte("fop"), 0)
wid, _ := w.Watch(0, []byte("foo"), []byte("fop"), 0)
wev := []mvccpb.Event{
{Type: mvccpb.PUT,
@ -783,7 +783,7 @@ func TestWatchableKVWatch(t *testing.T) {
}
w = s.NewWatchStream()
wid = w.Watch([]byte("foo1"), []byte("foo2"), 3)
wid, _ = w.Watch(0, []byte("foo1"), []byte("foo2"), 3)
select {
case resp := <-w.Chan():

View File

@ -78,7 +78,7 @@ func BenchmarkWatchableStoreWatchSyncPut(b *testing.B) {
watchIDs := make([]WatchID, b.N)
for i := range watchIDs {
// non-0 value to keep watchers in unsynced
watchIDs[i] = w.Watch(k, nil, 1)
watchIDs[i], _ = w.Watch(0, k, nil, 1)
}
b.ResetTimer()
@ -142,7 +142,7 @@ func BenchmarkWatchableStoreUnsyncedCancel(b *testing.B) {
watchIDs := make([]WatchID, watcherN)
for i := 0; i < watcherN; i++ {
// non-0 value to keep watchers in unsynced
watchIDs[i] = w.Watch(testKey, nil, 1)
watchIDs[i], _ = w.Watch(0, testKey, nil, 1)
}
// random-cancel N watchers to make it not biased towards
@ -182,7 +182,7 @@ func BenchmarkWatchableStoreSyncedCancel(b *testing.B) {
watchIDs := make([]WatchID, watcherN)
for i := 0; i < watcherN; i++ {
// 0 for startRev to keep watchers in synced
watchIDs[i] = w.Watch(testKey, nil, 0)
watchIDs[i], _ = w.Watch(0, testKey, nil, 0)
}
// randomly cancel watchers to make it not biased towards

View File

@ -42,7 +42,7 @@ func TestWatch(t *testing.T) {
s.Put(testKey, testValue, lease.NoLease)
w := s.NewWatchStream()
w.Watch(testKey, nil, 0)
w.Watch(0, testKey, nil, 0)
if !s.synced.contains(string(testKey)) {
// the key must have had an entry in synced
@ -63,7 +63,7 @@ func TestNewWatcherCancel(t *testing.T) {
s.Put(testKey, testValue, lease.NoLease)
w := s.NewWatchStream()
wt := w.Watch(testKey, nil, 0)
wt, _ := w.Watch(0, testKey, nil, 0)
if err := w.Cancel(wt); err != nil {
t.Error(err)
@ -114,7 +114,7 @@ func TestCancelUnsynced(t *testing.T) {
watchIDs := make([]WatchID, watcherN)
for i := 0; i < watcherN; i++ {
// use 1 to keep watchers in unsynced
watchIDs[i] = w.Watch(testKey, nil, 1)
watchIDs[i], _ = w.Watch(0, testKey, nil, 1)
}
for _, idx := range watchIDs {
@ -160,7 +160,7 @@ func TestSyncWatchers(t *testing.T) {
for i := 0; i < watcherN; i++ {
// specify rev as 1 to keep watchers in unsynced
w.Watch(testKey, nil, 1)
w.Watch(0, testKey, nil, 1)
}
// Before running s.syncWatchers() synced should be empty because we manually
@ -242,7 +242,7 @@ func TestWatchCompacted(t *testing.T) {
}
w := s.NewWatchStream()
wt := w.Watch(testKey, nil, compactRev-1)
wt, _ := w.Watch(0, testKey, nil, compactRev-1)
select {
case resp := <-w.Chan():
@ -271,7 +271,7 @@ func TestWatchFutureRev(t *testing.T) {
w := s.NewWatchStream()
wrev := int64(10)
w.Watch(testKey, nil, wrev)
w.Watch(0, testKey, nil, wrev)
for i := 0; i < 10; i++ {
rev := s.Put(testKey, testValue, lease.NoLease)
@ -310,7 +310,7 @@ func TestWatchRestore(t *testing.T) {
defer cleanup(newStore, newBackend, newPath)
w := newStore.NewWatchStream()
w.Watch(testKey, nil, rev-1)
w.Watch(0, testKey, nil, rev-1)
newStore.Restore(b)
select {
@ -349,7 +349,7 @@ func TestWatchBatchUnsynced(t *testing.T) {
}
w := s.NewWatchStream()
w.Watch(v, nil, 1)
w.Watch(0, v, nil, 1)
for i := 0; i < batches; i++ {
if resp := <-w.Chan(); len(resp.Events) != watchBatchMaxRevs {
t.Fatalf("len(events) = %d, want %d", len(resp.Events), watchBatchMaxRevs)
@ -485,7 +485,7 @@ func TestWatchVictims(t *testing.T) {
for i := 0; i < numWatches; i++ {
go func() {
w := s.NewWatchStream()
w.Watch(testKey, nil, 1)
w.Watch(0, testKey, nil, 1)
defer func() {
w.Close()
wg.Done()
@ -561,7 +561,7 @@ func TestStressWatchCancelClose(t *testing.T) {
w := s.NewWatchStream()
ids := make([]WatchID, 10)
for i := range ids {
ids[i] = w.Watch(testKey, nil, 0)
ids[i], _ = w.Watch(0, testKey, nil, 0)
}
<-readyc
wg.Add(1 + len(ids)/2)

View File

@ -22,8 +22,14 @@ import (
"github.com/coreos/etcd/mvcc/mvccpb"
)
// AutoWatchID is the watcher ID passed in WatchStream.Watch when no
// user-provided ID is available. If pass, an ID will automatically be assigned.
const AutoWatchID WatchID = 0
var (
ErrWatcherNotExist = errors.New("mvcc: watcher does not exist")
ErrWatcherNotExist = errors.New("mvcc: watcher does not exist")
ErrEmptyWatcherRange = errors.New("mvcc: watcher range is empty")
ErrWatcherDuplicateID = errors.New("mvcc: duplicate watch ID provided on the WatchStream")
)
type WatchID int64
@ -36,12 +42,13 @@ type WatchStream interface {
// happened on the given key or range [key, end) from the given startRev.
//
// The whole event history can be watched unless compacted.
// If `startRev` <=0, watch observes events after currentRev.
// If "startRev" <=0, watch observes events after currentRev.
//
// The returned `id` is the ID of this watcher. It appears as WatchID
// The returned "id" is the ID of this watcher. It appears as WatchID
// in events that are sent to the created watcher through stream channel.
//
Watch(key, end []byte, startRev int64, fcs ...FilterFunc) WatchID
// The watch ID is used when it's not equal to AutoWatchID. Otherwise,
// an auto-generated watch ID is returned.
Watch(id WatchID, key, end []byte, startRev int64, fcs ...FilterFunc) (WatchID, error)
// Chan returns a chan. All watch response will be sent to the returned chan.
Chan() <-chan WatchResponse
@ -98,28 +105,34 @@ type watchStream struct {
}
// Watch creates a new watcher in the stream and returns its WatchID.
// TODO: return error if ws is closed?
func (ws *watchStream) Watch(key, end []byte, startRev int64, fcs ...FilterFunc) WatchID {
func (ws *watchStream) Watch(id WatchID, key, end []byte, startRev int64, fcs ...FilterFunc) (WatchID, error) {
// prevent wrong range where key >= end lexicographically
// watch request with 'WithFromKey' has empty-byte range end
if len(end) != 0 && bytes.Compare(key, end) != -1 {
return -1
return -1, ErrEmptyWatcherRange
}
ws.mu.Lock()
defer ws.mu.Unlock()
if ws.closed {
return -1
return -1, ErrEmptyWatcherRange
}
id := ws.nextID
ws.nextID++
if id == AutoWatchID {
for ws.watchers[ws.nextID] != nil {
ws.nextID++
}
id = ws.nextID
ws.nextID++
} else if _, ok := ws.watchers[id]; ok {
return -1, ErrWatcherDuplicateID
}
w, c := ws.watchable.watch(key, end, startRev, id, ws.ch, fcs...)
ws.cancels[id] = c
ws.watchers[id] = w
return id
return id, nil
}
func (ws *watchStream) Chan() <-chan WatchResponse {

View File

@ -33,6 +33,6 @@ func BenchmarkKVWatcherMemoryUsage(b *testing.B) {
b.ReportAllocs()
b.StartTimer()
for i := 0; i < b.N; i++ {
w.Watch([]byte(fmt.Sprint("foo", i)), nil, 0)
w.Watch(0, []byte(fmt.Sprint("foo", i)), nil, 0)
}
}

View File

@ -40,7 +40,7 @@ func TestWatcherWatchID(t *testing.T) {
idm := make(map[WatchID]struct{})
for i := 0; i < 10; i++ {
id := w.Watch([]byte("foo"), nil, 0)
id, _ := w.Watch(0, []byte("foo"), nil, 0)
if _, ok := idm[id]; ok {
t.Errorf("#%d: id %d exists", i, id)
}
@ -62,7 +62,7 @@ func TestWatcherWatchID(t *testing.T) {
// unsynced watchers
for i := 10; i < 20; i++ {
id := w.Watch([]byte("foo2"), nil, 1)
id, _ := w.Watch(0, []byte("foo2"), nil, 1)
if _, ok := idm[id]; ok {
t.Errorf("#%d: id %d exists", i, id)
}
@ -79,6 +79,41 @@ func TestWatcherWatchID(t *testing.T) {
}
}
func TestWatcherRequestsCustomID(t *testing.T) {
b, tmpPath := backend.NewDefaultTmpBackend()
s := WatchableKV(newWatchableStore(b, &lease.FakeLessor{}, nil))
defer cleanup(s, b, tmpPath)
w := s.NewWatchStream()
defer w.Close()
// - Request specifically ID #1
// - Try to duplicate it, get an error
// - Make sure the auto-assignment skips over things we manually assigned
tt := []struct {
givenID WatchID
expectedID WatchID
expectedErr error
}{
{1, 1, nil},
{1, 0, ErrWatcherDuplicateID},
{0, 0, nil},
{0, 2, nil},
}
for i, tcase := range tt {
id, err := w.Watch(tcase.givenID, []byte("foo"), nil, 0)
if tcase.expectedErr != nil || err != nil {
if err != tcase.expectedErr {
t.Errorf("expected get error %q in test case %q, got %q", tcase.expectedErr, i, err)
}
} else if tcase.expectedID != id {
t.Errorf("expected to create ID %d, got %d in test case %d", tcase.expectedID, id, i)
}
}
}
// TestWatcherWatchPrefix tests if Watch operation correctly watches
// and returns events with matching prefixes.
func TestWatcherWatchPrefix(t *testing.T) {
@ -95,7 +130,7 @@ func TestWatcherWatchPrefix(t *testing.T) {
keyWatch, keyEnd, keyPut := []byte("foo"), []byte("fop"), []byte("foobar")
for i := 0; i < 10; i++ {
id := w.Watch(keyWatch, keyEnd, 0)
id, _ := w.Watch(0, keyWatch, keyEnd, 0)
if _, ok := idm[id]; ok {
t.Errorf("#%d: unexpected duplicated id %x", i, id)
}
@ -127,7 +162,7 @@ func TestWatcherWatchPrefix(t *testing.T) {
// unsynced watchers
for i := 10; i < 15; i++ {
id := w.Watch(keyWatch1, keyEnd1, 1)
id, _ := w.Watch(0, keyWatch1, keyEnd1, 1)
if _, ok := idm[id]; ok {
t.Errorf("#%d: id %d exists", i, id)
}
@ -163,14 +198,14 @@ func TestWatcherWatchWrongRange(t *testing.T) {
w := s.NewWatchStream()
defer w.Close()
if id := w.Watch([]byte("foa"), []byte("foa"), 1); id != -1 {
t.Fatalf("key == end range given; id expected -1, got %d", id)
if _, err := w.Watch(0, []byte("foa"), []byte("foa"), 1); err != ErrEmptyWatcherRange {
t.Fatalf("key == end range given; expected ErrEmptyWatcherRange, got %+v", err)
}
if id := w.Watch([]byte("fob"), []byte("foa"), 1); id != -1 {
t.Fatalf("key > end range given; id expected -1, got %d", id)
if _, err := w.Watch(0, []byte("fob"), []byte("foa"), 1); err != ErrEmptyWatcherRange {
t.Fatalf("key > end range given; expected ErrEmptyWatcherRange, got %+v", err)
}
// watch request with 'WithFromKey' has empty-byte range end
if id := w.Watch([]byte("foo"), []byte{}, 1); id != 0 {
if id, _ := w.Watch(0, []byte("foo"), []byte{}, 1); id != 0 {
t.Fatalf("\x00 is range given; id expected 0, got %d", id)
}
}
@ -192,7 +227,7 @@ func TestWatchDeleteRange(t *testing.T) {
w := s.NewWatchStream()
from, to := []byte(testKeyPrefix), []byte(fmt.Sprintf("%s_%d", testKeyPrefix, 99))
w.Watch(from, to, 0)
w.Watch(0, from, to, 0)
s.DeleteRange(from, to)
@ -222,7 +257,7 @@ func TestWatchStreamCancelWatcherByID(t *testing.T) {
w := s.NewWatchStream()
defer w.Close()
id := w.Watch([]byte("foo"), nil, 0)
id, _ := w.Watch(0, []byte("foo"), nil, 0)
tests := []struct {
cancelID WatchID
@ -284,7 +319,7 @@ func TestWatcherRequestProgress(t *testing.T) {
default:
}
id := w.Watch(notTestKey, nil, 1)
id, _ := w.Watch(0, notTestKey, nil, 1)
w.RequestProgress(id)
select {
case resp := <-w.Chan():
@ -295,7 +330,7 @@ func TestWatcherRequestProgress(t *testing.T) {
s.syncWatchers()
w.RequestProgress(id)
wrs := WatchResponse{WatchID: 0, Revision: 2}
wrs := WatchResponse{WatchID: id, Revision: 2}
select {
case resp := <-w.Chan():
if !reflect.DeepEqual(resp, wrs) {
@ -318,7 +353,7 @@ func TestWatcherWatchWithFilter(t *testing.T) {
return e.Type == mvccpb.PUT
}
w.Watch([]byte("foo"), nil, 0, filterPut)
w.Watch(0, []byte("foo"), nil, 0, filterPut)
done := make(chan struct{})
go func() {

View File

@ -10,8 +10,8 @@ if ! [[ "$0" =~ scripts/genproto.sh ]]; then
exit 255
fi
if [[ $(protoc --version | cut -f2 -d' ') != "3.5.0" ]]; then
echo "could not find protoc 3.5.0, is it installed + in PATH?"
if [[ $(protoc --version | cut -f2 -d' ') != "3.5.1" ]]; then
echo "could not find protoc 3.5.1, is it installed + in PATH?"
exit 255
fi