├── .gitignore ├── LICENSE ├── README.md ├── client.go ├── cmd └── peacho │ └── peacho.go ├── connState.go ├── definitions.go ├── example_test.go ├── examples └── server │ └── server.go ├── fuzz_test.go ├── go.mod ├── peasocket.go ├── peasocket_test.go ├── rxtx.go ├── server.go └── testdata └── fuzz ├── FuzzHeaderPut └── b2765df01f4a03cb21f126e13e18a74ed901c7c82b7a7645a724da1a2b4ec802 ├── FuzzLoopbackMessage ├── 14a8f53cd1c49cd79638f60a021ee0e11ac49e1c0de48f43c10f067568e33cc8 ├── 155f0d559e3d33f5c42bae1277a54533c4996749e08fa5235653d0b1edb985b4 ├── 17f3c85e957f6c4b35570599663243be829bfab454c287c06b64bfad894e5538 ├── 1b299a9f6b4f4d019c6a90069665defa517cb85ac1da55b2e2bad6d3510b292a ├── 27d4f8f0ff89c5d6b57db16ca04f365ff6da8b338110b5e9f7b5bdce3d54771e ├── 924ffc0d6b15450b86db1dbe79aabebcd675bbafd085f8a6cdd75ad80677eae6 ├── 9c085f9dc8bedcdbd466602263fb0baf5922dc8050a80990498d0adf3818fccf ├── ab1429a42543219c19740e8c2466c22b36046a8ad68e62e317e2f8dfda6d2898 ├── abf5cb1ad7067774508b10e704279e619b2b82d3997ba8c28c545e784faf8521 ├── d27453fd81a736957a99207b2c9a109a026d52f2f18cc3b144bf0bdc094729fc ├── dc727647f156913f63b8727728edba8f03ded1337c99fb34a187d8686f3c9a2f ├── e6038814489048ad2b1b728d50426df7295c3b44fee352381170f0c86c170ea2 ├── eb6ab80a9fb4e25043fcb6a82519eb91d26de7f6d68293613db5e349cf4b7040 ├── ecb1cdba517884931fb7343b0750e80cd7b9364017f9d17fe01fe09dc959a95a ├── edb9274bfc2b4ebf72faaead9b2ddc26bb968e3fa351b3f5b5c9a0e2a7c64379 ├── f831c183d547a4e6be45719e65e5cabd766249d621658eb8871bccf92b1b8e21 └── ff4ce0b8881795c20eceaa2d8480a178f76e8142e1cd12476982251e4f589eb7 └── FuzzMaskedReader ├── 0f2affb229265eb4afa688fd7e25aae8b2c03a961d4c3e61b6d5077d37bd531e ├── 18d2fe6710c21b408997c6f1c52d09292d431783c4a97d54f0686a11a25a4ad5 ├── 203eea51a24b89a1c97308156610510320d4dd6f0ef6f0bf5aaa90813dae5384 ├── 2a6fd3e2276ea5aa077f7853f29216e96e49e62830eb8cd29220d7270120579f ├── 569f534b9c22bd19dbb7d799b0fb6ee510267eeedcf797ce87114adaa62e9aa8 ├── 7425e8ee2a7930e563490b1f1a19d4b8a75bfd19c1d7e243f27148d0b5d5d4d3 ├── 7cc689243f4c2ffac7db6c451ab2083223a0f28ce276dd0fd38fd6672446b9cf ├── 85b51d69d1758cd1c9a445ba47e87ff74963ac0cb5874e527c07dd41d2604453 ├── 8cb0c14d08a3825449162ec6b092fbc6d08785789950ec18341695ff93026f29 ├── a6f3e00b21b5e318f80252bf405eb454baaa5b29214245494840cc30e7537ff3 ├── ab2df5171d46de906d671984311116cdfee3dcbf679afdfc4478ca70e9f4706d ├── b84716b1fb55f4cca2203dba199075af0e0d3cc0ca16a121dfeba281e40fc90d ├── be2c380a8a1b73cb4bd3658a7ceb1db3b35bc03bdf3785ac13d588a0524e95ca ├── dedf4276541e3fcd41241b3fd5839c358813b2b76cffb555e2cf363937c3eecf ├── eb62e94b5e3228aea5eba9b501d09ecb7f33c4b4e8db72de6179acd6cb6828ca ├── fbec51edd6b5e0fcf91210cdd431787756f0f980b7689e91fa6631e3a58d2799 ├── fbf0155d37efbbf3ec751fe097ec522dba8927ff24c73ef3db8cb601a8344655 └── ff55d8a64672b1f6feb847e9d0e4a26bc37583c35b0b5c8a6bb6c02ed17aa8c6 /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | # IDE 8 | .vscode/* 9 | .vscode 10 | # Test binary, built with `go test -c` 11 | *.test 12 | *.pprof 13 | *.prof 14 | *_bin 15 | 16 | # Coverage profile, `go test -coverprofile=coverage.cov` 17 | *.cov 18 | 19 | # Output of the go coverage tool, specifically when used with LiteIDE 20 | *.out 21 | 22 | # Dependency directories (remove the comment below to include it) 23 | # vendor/ 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Patricio Whittingslow 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Go Report Card](https://goreportcard.com/badge/github.com/soypat/peasocket)](https://goreportcard.com/report/github.com/soypat/peasocket) 2 | [![GoDoc](https://godoc.org/github.com/soypat/peasocket?status.svg)](https://godoc.org/github.com/soypat/peasocket) 3 | [![codecov](https://codecov.io/gh/soypat/peasocket/branch/main/graph/badge.svg)](https://codecov.io/gh/soypat/peasocket/branch/main) 4 | 5 | # peasocket 6 | Little websocket implementation 7 | 8 | ## Highlights 9 | * Zero allocations when fragmenting messages with `WriteFragmentedMessage` 10 | * Empty `go.mod` file. 11 | * Lockless base implementation 12 | * Dead-simple Client implementation 13 | * No channels 14 | * No goroutines 15 | * 1 Mutex on state and another on `Tx` to auto-reply pings. 16 | 17 | * Ready for use in embedded systems 18 | * Tinygo compatible. 19 | * Base implementation is very simple, 1000 lines of code excluding `Client`. This makes debugging and testing easy. 20 | * Easy to extend. Exposes all websocket functionality. 21 | 22 | ## `cmd/peacho` Command 23 | A echoing client. Connects to a server and echoes back any messages received. -------------------------------------------------------------------------------- /client.go: -------------------------------------------------------------------------------- 1 | package peasocket 2 | 3 | import ( 4 | "bufio" 5 | "context" 6 | "crypto/sha1" 7 | "encoding/base64" 8 | "errors" 9 | "fmt" 10 | "io" 11 | "net" 12 | "net/http" 13 | "net/url" 14 | "strings" 15 | "sync" 16 | "unsafe" 17 | ) 18 | 19 | // flag error for when server gracefully closes the websocket connection with a Close frame. 20 | var ( 21 | errServerGracefulClose = errors.New("server closure") 22 | errClientGracefulClose = errors.New("client closure") 23 | errUnexpectedPong = errors.New("unexpected pong") 24 | ) 25 | 26 | // Client is a client websocket implementation. 27 | // 28 | // Users should use Client in no more than two goroutines unless calling the 29 | // explicitly stated as concurrency safe methods i.e: Err, IsConnected. 30 | // These two goroutines should be separated by responsability: 31 | // - Read goroutine should call methods that read received messages 32 | // - Write goroutine should call methods that write to the connection. 33 | // 34 | // Client automatically takes care of incoming Ping and Close frames during ReadNextFrame 35 | type Client struct { 36 | // Tx mutex. 37 | muTx sync.Mutex 38 | tx TxBuffered 39 | state connState 40 | rx Rx 41 | ServerURL string 42 | entropy func() uint32 43 | } 44 | 45 | // NewClient creates a new Client ready to connect to a websocket enabled server. 46 | // serverURL can start with wss:// or ws:// to enable TLS (secure websocket protocol). 47 | // entropy is a function that returns randomized 32 bit integers from a high-entropy source. 48 | // userBuffer will be used as scratch space to copy messages. 49 | // 50 | // If entropy is nil then a default will be used. 51 | // If userBuffer is zero-lengthed one will be automatically allocated. 52 | func NewClient(serverURL string, userBuffer []byte, entropy func() uint32) *Client { 53 | if entropy == nil { 54 | entropy = defaultEntropy 55 | } 56 | L := len(userBuffer) 57 | if userBuffer == nil { 58 | userBuffer = make([]byte, 32*1024) 59 | } else if L < MaxControlPayload*3 { 60 | panic("user buffer length too short, must be at least 3*125") 61 | } 62 | ctl1 := userBuffer[0:0:MaxControlPayload] 63 | ctl2 := userBuffer[MaxControlPayload : MaxControlPayload : 2*MaxControlPayload] 64 | c := &Client{ 65 | ServerURL: serverURL, 66 | entropy: entropy, 67 | state: connState{ 68 | pendingPingOrClose: ctl1, 69 | expectedPong: ctl2, 70 | closeErr: errClientGracefulClose, 71 | copyBuf: userBuffer[2*MaxControlPayload:], 72 | }, 73 | } 74 | rxc, txc := c.state.callbacks(true) 75 | c.rx.RxCallbacks = rxc 76 | c.tx.TxCallbacks = txc 77 | return c 78 | } 79 | 80 | // Dial not yet tested. Performs websocket handshake over net.Conn. 81 | func (c *Client) Dial(ctx context.Context, overwriteHeaders http.Header) error { 82 | if !c.state.IsClosed() { 83 | return errors.New("must first close existing connection before starting a new one") 84 | } 85 | 86 | remoteAddr, err := net.ResolveTCPAddr("tcp", strings.TrimPrefix(c.ServerURL, "ws://")) 87 | if err != nil { 88 | return err 89 | } 90 | conn, err := net.DialTCP("tcp", nil, remoteAddr) 91 | if err != nil { 92 | return err 93 | } 94 | secureWebsocketKey := c.secureKeyString() 95 | req, err := c.makeRequest(context.Background(), secureWebsocketKey, overwriteHeaders) 96 | if err != nil { 97 | return err 98 | } 99 | err = req.Write(conn) // Write HTTP/1.1 request with challenge. 100 | if err != nil { 101 | return err 102 | } 103 | resp, err := http.ReadResponse(bufio.NewReaderSize(conn, 256), req) 104 | if err != nil { 105 | return err 106 | } 107 | if err := validateServerResponse(resp, secureWebsocketKey); err != nil { 108 | return err 109 | } 110 | c.tx.SetTxTransport(conn) 111 | c.rx.SetRxTransport(conn) 112 | c.state.closeErr = nil 113 | return nil 114 | } 115 | 116 | // DialViaHTTPClient completes a websocket handshake and returns an error if 117 | // the handshake failed. After a successful handshake the Client is ready to begin 118 | // communicating with the server via websocket protocol. 119 | func (c *Client) DialViaHTTPClient(ctx context.Context, overwriteHeaders http.Header) error { 120 | if !c.state.IsClosed() { 121 | return errors.New("must first close existing connection before starting a new one") 122 | } 123 | if ctx == nil { 124 | return errors.New("nil context") 125 | } 126 | secureWebsocketKey := c.secureKeyString() 127 | req, err := c.makeRequest(ctx, secureWebsocketKey, overwriteHeaders) 128 | if err != nil { 129 | return err 130 | } 131 | client := http.DefaultClient 132 | resp, err := client.Do(req) 133 | if err != nil { 134 | return fmt.Errorf("failed handshake GET: %w", err) 135 | } 136 | if err := validateServerResponse(resp, secureWebsocketKey); err != nil { 137 | return err 138 | } 139 | 140 | // TODO(soypat): use net.Conn instead. 141 | rwc, ok := resp.Body.(io.ReadWriteCloser) 142 | if !ok { 143 | return fmt.Errorf("response body not an io.ReadWriteCloser: %T", resp.Body) 144 | } 145 | c.tx.SetTxTransport(rwc) 146 | c.rx.SetRxTransport(rwc) 147 | c.state.OnConnect() 148 | return nil 149 | } 150 | 151 | // IsConnected returns true if the underlying transport of the websocket is ready to send/receive messages. 152 | // It is shorthand for c.Err() == nil. 153 | // 154 | // IsConnected is safe for concurrent use. 155 | func (c *Client) IsConnected() bool { return c.state.IsConnected() } 156 | 157 | // Err returns the error that closed the connection. 158 | // It only returns an error while the connection is closed. 159 | // One can test if the server gracefully closed the connection with 160 | // 161 | // errors.As(err, &peasocket.CloseErr{}) 162 | // 163 | // Err is safe for concurrent use. 164 | func (c *Client) Err() error { 165 | c.state.mu.Lock() 166 | defer c.state.mu.Unlock() 167 | if c.state.closeErr == errServerGracefulClose { 168 | return fmt.Errorf("server closed websocket: %w", c.state.makeCloseErr()) 169 | } 170 | return c.state.closeErr 171 | } 172 | 173 | // BufferedMessages returns the amount of received messages N that are ready to be read 174 | // such that the user is guaranteed to not get an error calling NextMessageReader N times. 175 | // 176 | // BufferedMessages is safe for concurrent use. 177 | func (c *Client) BufferedMessages() int { 178 | return c.state.BufferedMessages() 179 | } 180 | 181 | // Buffered returns the amount of bytes ready to be read in the received message buffer. 182 | // 183 | // Buffered is safe for concurrent use. 184 | func (c *Client) Buffered() int { 185 | return c.state.Buffered() 186 | } 187 | 188 | // HandleNextFrame reads next frame in connection. Should be called in a loop 189 | func (c *Client) HandleNextFrame() error { 190 | if !c.IsConnected() { 191 | return c.Err() 192 | } 193 | _, err := c.rx.ReadNextFrame() 194 | if err != nil { 195 | return err 196 | } 197 | c.muTx.Lock() 198 | err = c.state.ReplyOutstandingFrames(c.entropy(), &c.tx) 199 | c.muTx.Unlock() 200 | if err != nil { 201 | c.CloseConn(err) 202 | return err 203 | } 204 | return nil 205 | } 206 | 207 | // BufferedMessageReader returns a reader to a complete websocket message. It copies 208 | // the whole contents of the received message to a new buffer which is returned 209 | // to the caller. 210 | // The returned message may have been fragmented over the wire. 211 | func (c *Client) BufferedMessageReader() (io.Reader, FrameType, error) { 212 | return c.state.NextMessage() 213 | } 214 | 215 | // WriteBufferedMessageTo writes the next received message in the queue to the argument writer. 216 | func (c *Client) WriteBufferedMessageTo(w io.Writer) (FrameType, int64, error) { 217 | return c.state.WriteNextMessageTo(w) 218 | } 219 | 220 | // WriteFragmentedMessage writes contents of r over the wire using the userBuffer 221 | // as scratch memory. This function does not allocate memory unless userBuffer is nil. 222 | func (c *Client) WriteFragmentedMessage(r io.Reader, userBuffer []byte) (int, error) { 223 | c.muTx.Lock() 224 | defer c.muTx.Unlock() 225 | err := c.state.ReplyOutstandingFrames(c.entropy(), &c.tx) 226 | if err != nil { 227 | c.CloseConn(err) 228 | return 0, err 229 | } 230 | return c.tx.WriteFragmentedMessage(c.entropy(), r, userBuffer) 231 | } 232 | 233 | // WriteMessage writes a binary message over the websocket connection. 234 | func (c *Client) WriteMessage(payload []byte) error { 235 | c.muTx.Lock() 236 | defer c.muTx.Unlock() 237 | err := c.state.ReplyOutstandingFrames(c.entropy(), &c.tx) 238 | if err != nil { 239 | c.CloseConn(err) 240 | return err 241 | } 242 | _, err = c.tx.writeMessage(c.entropy(), payload, false) 243 | return err 244 | } 245 | 246 | // WriteTextMessage writes a utf-8 message over the websocket connection. 247 | func (c *Client) WriteTextMessage(payload []byte) error { 248 | c.muTx.Lock() 249 | defer c.muTx.Unlock() 250 | err := c.state.ReplyOutstandingFrames(c.entropy(), &c.tx) 251 | if err != nil { 252 | c.CloseConn(err) 253 | return err 254 | } 255 | _, err = c.tx.writeMessage(c.entropy(), payload, true) 256 | return err 257 | } 258 | 259 | // makeRequest creates the HTTP handshake required to initiate a websocket connection 260 | // with a server. 261 | func (c *Client) makeRequest(ctx context.Context, secureWebsocketKey string, overwriteHeaders http.Header) (*http.Request, error) { 262 | u, err := url.Parse(c.ServerURL) 263 | if err != nil { 264 | return nil, err 265 | } 266 | switch u.Scheme { 267 | case "ws": 268 | u.Scheme = "http" 269 | case "wss": 270 | u.Scheme = "https" 271 | case "http", "https": 272 | default: 273 | return nil, fmt.Errorf("unexpected url scheme: %q", u.Scheme) 274 | } 275 | 276 | req := &http.Request{ 277 | Method: http.MethodGet, 278 | URL: u, 279 | Proto: httpProto, 280 | ProtoMajor: httpProtoMajor, 281 | ProtoMinor: httpProtoMinor, 282 | Header: make(http.Header), 283 | Host: u.Host, 284 | } 285 | req = req.WithContext(ctx) 286 | hd, err := makeWSHeader(overwriteHeaders) 287 | if err != nil { 288 | return nil, err 289 | } 290 | hd["Sec-WebSocket-Key"] = []string{secureWebsocketKey} 291 | req.Header = hd 292 | return req, nil 293 | } 294 | 295 | // CloseConn closes the underlying transport without sending websocket frames. 296 | // 297 | // If err is nil CloseConn will panic. 298 | func (c *Client) CloseConn(err error) error { 299 | w := c.tx.trp 300 | r := c.rx.trp 301 | if w != nil && r != nil { 302 | c.state.CloseConn(err) 303 | w.Close() 304 | r.Close() 305 | return nil 306 | } 307 | return errors.New("transport(s) nil, will not attempt to close. this may be a bug in peasocket") 308 | } 309 | 310 | // CloseWebsocket sends a close control frame over the websocket connection. Does 311 | // not attempt to close the underlying transport. 312 | // If CloseWebsocket received a [*CloseErr] type no memory allocation is performed and it's 313 | // status code and reason is used in the websocket close frame. 314 | // 315 | // If err is nil CloseWebsocket panics. 316 | func (c *Client) CloseWebsocket(err error) error { 317 | c.muTx.Lock() 318 | defer c.muTx.Unlock() 319 | return c.state.CloseWebsocket(err, c.entropy(), &c.tx) 320 | } 321 | 322 | func validateServerResponse(resp *http.Response, secureWebsocketKey string) error { 323 | if resp.StatusCode != http.StatusSwitchingProtocols { 324 | return fmt.Errorf("expected %v switching protocol http status, got %v", http.StatusSwitchingProtocols, resp.StatusCode) 325 | } 326 | if !strings.EqualFold(resp.Header.Get("Connection"), "Upgrade") || !strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") || 327 | !strings.EqualFold(resp.Header.Get("Sec-WebSocket-Accept"), serverProofOfReceipt(secureWebsocketKey)) { 328 | // resp.Header.Get("Sec-Websocket-Version") != "13" TODO(soypat): Add this once done testing since Nhooyr's library sometimes does not set this field. 329 | return fmt.Errorf(`invalid header field(s) "Sec-Websocket-Version:13", "Connection:Upgrade", "Upgrade:websocket" or "Sec-WebSocket-Accept":, got %v`, resp.Header) 330 | } 331 | return nil 332 | } 333 | 334 | // On the Sec-WebSocket-Key header field, which the server uses to 335 | // prove it received the handshake: 336 | // 337 | // For this header field, the server has to take the value (as present 338 | // in the header field as sent by client, e.g., the base64-encoded [RFC4648] version minus 339 | // any leading and trailing whitespace) and concatenate this with the 340 | // Globally Unique Identifier (GUID, [RFC4122]) "258EAFA5-E914-47DA- 341 | // 95CA-C5AB0DC85B11" in string form, which is unlikely to be used by 342 | // network endpoints that do not understand the WebSocket Protocol. A 343 | // SHA-1 hash (160 bits) [FIPS.180-3], base64-encoded (see Section 4 of 344 | // [RFC4648]), of this concatenation is then returned in the server's 345 | // handshake. 346 | var predefinedGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11") 347 | 348 | func serverProofOfReceipt(clientSecureWSKey string) string { 349 | clientSecureWSKey = strings.TrimSpace(clientSecureWSKey) 350 | h := sha1.New() 351 | h.Write([]byte(clientSecureWSKey)) 352 | h.Write(predefinedGUID) 353 | return base64.StdEncoding.EncodeToString(h.Sum(nil)) 354 | } 355 | 356 | func (c *Client) secureKeyString() string { 357 | key := c.secureKey() 358 | return base64.StdEncoding.EncodeToString(key[:]) 359 | } 360 | 361 | func (c *Client) secureKey() [16]byte { 362 | const u32size = unsafe.Sizeof(uint32(0)) 363 | k32 := [4]uint32{c.entropy(), c.entropy(), c.entropy(), c.entropy()} 364 | return *(*[4 * u32size]byte)(unsafe.Pointer(&k32)) 365 | } 366 | 367 | func makeWSHeader(overwriteHeaders http.Header) (http.Header, error) { 368 | header := make(http.Header) 369 | header["Upgrade"] = []string{"websocket"} 370 | header["Connection"] = []string{"Upgrade"} 371 | header["Sec-WebSocket-Version"] = []string{"13"} 372 | for k, v := range overwriteHeaders { 373 | if len(v) == 0 { 374 | return nil, errors.New("overwrite header key " + k + " empty") 375 | } 376 | header.Set(k, v[0]) 377 | for _, v0 := range v[1:] { 378 | header.Add(k, v0) 379 | } 380 | } 381 | return header, nil 382 | } 383 | -------------------------------------------------------------------------------- /cmd/peacho/peacho.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "flag" 7 | "io" 8 | "log" 9 | "net" 10 | "time" 11 | 12 | "github.com/soypat/peasocket" 13 | ) 14 | 15 | func main() { 16 | var ( 17 | wsURL string 18 | keepGoing bool 19 | ) 20 | flag.StringVar(&wsURL, "url", "ws://localhost:8080", "Websocket server URL to echo to (required).") 21 | flag.BoolVar(&keepGoing, "k", true, "Retry until program terminated") 22 | flag.Parse() 23 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 24 | defer cancel() 25 | client := peasocket.NewClient(wsURL, nil, nil) 26 | err := client.Dial(ctx, nil) 27 | if err != nil { 28 | log.Fatal("while dialing:", err) 29 | } 30 | defer client.CloseWebsocket(&peasocket.CloseError{ 31 | Status: peasocket.StatusGoingAway, 32 | Reason: []byte("peacho finalized"), 33 | }) 34 | go func() { 35 | for { 36 | err := client.HandleNextFrame() 37 | if client.Err() != nil { 38 | if keepGoing { 39 | log.Println("failure, retrying:", client.Err()) 40 | client.CloseConn(client.Err()) 41 | client.Dial(ctx, nil) 42 | time.Sleep(time.Second) 43 | continue 44 | } 45 | log.Println("connection closed, ending loop") 46 | return 47 | } 48 | if err != nil { 49 | log.Println("read next frame failed:", err) 50 | time.Sleep(500 * time.Millisecond) 51 | } 52 | } 53 | }() 54 | // Exponential Backoff algorithm to not saturate 55 | // the process with calls to NextMessageReader 56 | // https://en.wikipedia.org/wiki/Exponential_backoff 57 | backoff := peasocket.ExponentialBackoff{MaxWait: 500 * time.Millisecond} 58 | for { 59 | msg, _, err := client.BufferedMessageReader() 60 | if err != nil { 61 | if errors.Is(err, net.ErrClosed) || client.Err() != nil { 62 | log.Fatal("websocket closed:", client.Err()) 63 | } 64 | backoff.Miss() 65 | continue 66 | } 67 | backoff.Hit() 68 | b, err := io.ReadAll(msg) 69 | if err != nil { 70 | log.Fatal("while reading message:", err) 71 | } 72 | log.Println("echoing message:", string(b)) 73 | err = client.WriteMessage(b) 74 | if err != nil { 75 | log.Fatal("while echoing message:", err) 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /connState.go: -------------------------------------------------------------------------------- 1 | package peasocket 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "sync" 10 | ) 11 | 12 | // connState stores the persisting state of a websocket client connection. 13 | // Since this state is shared between frames it is protected by a mutex so that 14 | // the Client implementation is concurrent-safe. 15 | type connState struct { 16 | mu sync.Mutex 17 | rxBuffer bytes.Buffer 18 | // currentMessageOpcode is either FrameText or FrameBinary during normal functioning. 19 | // When messages are discarded currentMessageOpcodeBecomes 20 | currentMessageOpcode FrameType 21 | currentMessageSize uint64 22 | // messageSizes correspond to the websocket messages 23 | // in the rxBuffer 24 | messageMetadata []struct { 25 | length uint64 26 | ftype FrameType 27 | } 28 | pendingPingOrClose []byte 29 | expectedPong []byte 30 | copyBuf []byte 31 | closeErr error 32 | } 33 | 34 | func (cs *connState) callbacks(isClient bool) (RxCallbacks, TxCallbacks) { 35 | closureErr := errClientGracefulClose 36 | if isClient { 37 | closureErr = errServerGracefulClose 38 | } 39 | return RxCallbacks{ 40 | OnMessage: func(rx *Rx, message io.Reader) error { 41 | cs.mu.Lock() 42 | defer cs.mu.Unlock() 43 | if cs.closeErr != nil { 44 | return cs.closeErr 45 | } 46 | n, err := io.CopyBuffer(&cs.rxBuffer, message, cs.copyBuf) 47 | if err != nil { 48 | cs.currentMessageOpcode = 0 49 | cs.currentMessageSize = 0 50 | return err 51 | } else if cs.currentMessageSize == 0 { 52 | cs.currentMessageOpcode = rx.LastReceivedHeader.FrameType() 53 | } 54 | cs.currentMessageSize += uint64(n) 55 | if rx.LastReceivedHeader.Fin() { 56 | cs.messageMetadata = append(cs.messageMetadata, struct { 57 | length uint64 58 | ftype FrameType 59 | }{ 60 | length: cs.currentMessageSize, 61 | ftype: cs.currentMessageOpcode, 62 | }) 63 | cs.currentMessageSize = 0 64 | cs.currentMessageOpcode = 0 65 | } 66 | return nil 67 | }, 68 | OnCtl: func(rx *Rx, payload io.Reader) (err error) { 69 | op := rx.LastReceivedHeader.FrameType() 70 | plen := rx.LastReceivedHeader.PayloadLength 71 | cs.mu.Lock() 72 | defer cs.mu.Unlock() 73 | if cs.closeErr != nil { 74 | return cs.closeErr 75 | } 76 | var n int 77 | switch op { 78 | case FramePing: 79 | n, err = io.ReadFull(payload, cs.pendingPingOrClose[:plen]) 80 | if err != nil { 81 | break 82 | } 83 | // Replaces pending ping with new ping. 84 | cs.pendingPingOrClose = cs.pendingPingOrClose[:n] 85 | 86 | case FramePong: 87 | var pongBuf [MaxControlPayload]byte 88 | n, err = io.ReadFull(payload, pongBuf[:plen]) 89 | if err != nil { 90 | break 91 | } 92 | if bytes.Equal(cs.expectedPong, pongBuf[:n]) { 93 | cs.expectedPong = nil 94 | } else { 95 | err = errUnexpectedPong 96 | } 97 | 98 | case FrameClose: 99 | n, err = io.ReadFull(payload, cs.pendingPingOrClose[:plen]) 100 | if err != nil { 101 | break 102 | } 103 | cs.closeErr = closureErr 104 | // Replaces pending ping with new ping. 105 | cs.pendingPingOrClose = cs.pendingPingOrClose[:n] 106 | default: 107 | panic("unknown control FrameType") // This should be unreachable. 108 | } 109 | if err != nil { 110 | err = fmt.Errorf("decoding control frame %s: %w", rx.LastReceivedHeader, err) 111 | } 112 | return err 113 | }, 114 | OnError: func(rx *Rx, err error) { 115 | cs.CloseConn(err) 116 | }, 117 | }, TxCallbacks{ 118 | OnError: func(tx *TxBuffered, err error) { 119 | cs.CloseConn(err) 120 | }, 121 | } 122 | } 123 | 124 | func (cs *connState) PendingAction() bool { 125 | cs.mu.Lock() 126 | defer cs.mu.Unlock() 127 | return cs.closeErr == nil && len(cs.pendingPingOrClose) != 0 128 | } 129 | 130 | func (cs *connState) Err() error { 131 | cs.mu.Lock() 132 | defer cs.mu.Unlock() 133 | return cs.closeErr 134 | } 135 | 136 | func (cs *connState) IsConnected() bool { 137 | return cs.Err() == nil 138 | } 139 | 140 | func (cs *connState) IsClosed() bool { 141 | return cs.Err() != nil 142 | } 143 | 144 | func (cs *connState) PeekNextMessage() (FrameType, int64) { 145 | if cs.BufferedMessages() == 0 { 146 | return 0, 0 147 | } 148 | cs.mu.Lock() 149 | defer cs.mu.Unlock() 150 | got := cs.messageMetadata[len(cs.messageMetadata)-1] 151 | return got.ftype, int64(got.length) 152 | } 153 | 154 | func (cs *connState) CloseWebsocket(err error, mask uint32, tx *TxBuffered) error { 155 | if err == nil { 156 | panic("nil error") 157 | } 158 | if cs.IsClosed() { 159 | return errors.New("no websocket connection to close") 160 | } 161 | closeErr, ok := err.(*CloseError) 162 | if !ok { 163 | closeErr.Status = StatusAbnormalClosure 164 | closeErr.Reason = []byte(err.Error()) 165 | } 166 | cs.mu.Lock() 167 | cs.closeErr = err 168 | cs.mu.Unlock() 169 | _, err = tx.WriteClose(mask, closeErr.Status, closeErr.Reason) 170 | if err != nil { 171 | return err 172 | } 173 | 174 | return nil 175 | } 176 | 177 | func (cs *connState) GetServerClosedReason() (sc StatusCode, reason []byte) { 178 | cs.mu.Lock() 179 | defer cs.mu.Unlock() 180 | if cs.closeErr == nil || len(cs.pendingPingOrClose) < 2 { 181 | return 0, nil 182 | } 183 | sc = StatusCode(binary.BigEndian.Uint16(cs.pendingPingOrClose[:2])) 184 | return sc, cs.pendingPingOrClose[2:] 185 | } 186 | 187 | func (cs *connState) Buffered() int { 188 | cs.mu.Lock() 189 | defer cs.mu.Unlock() 190 | return cs.rxBuffer.Len() 191 | } 192 | 193 | func (cs *connState) BufferedMessages() int { 194 | cs.mu.Lock() 195 | defer cs.mu.Unlock() 196 | return len(cs.messageMetadata) 197 | } 198 | 199 | // DiscardReadMessages discards all completely read messages. 200 | func (cs *connState) DiscardReadMessages() { 201 | cs.mu.Lock() 202 | n := uint64(0) 203 | for _, msgData := range cs.messageMetadata { 204 | n += msgData.length 205 | } 206 | cs.messageMetadata = nil 207 | cs.rxBuffer.Next(int(n)) 208 | cs.mu.Unlock() 209 | } 210 | 211 | // OnConnect is meant to be called on opening a new connection to delete 212 | // previous connection state. 213 | func (cs *connState) OnConnect() { 214 | cs.mu.Lock() 215 | cs.closeErr = nil 216 | cs.rxBuffer.Truncate(int(cs.currentMessageSize)) 217 | cs.currentMessageOpcode = 0 218 | cs.currentMessageSize = 0 219 | cs.expectedPong = cs.expectedPong[:0] 220 | cs.pendingPingOrClose = cs.pendingPingOrClose[:0] 221 | cs.mu.Unlock() 222 | } 223 | 224 | func (cs *connState) NextMessage() (io.Reader, FrameType, error) { 225 | var buf bytes.Buffer 226 | ft, _, err := cs.WriteNextMessageTo(&buf) 227 | if err != nil { 228 | return nil, 0, err 229 | } 230 | return &buf, ft, nil 231 | } 232 | 233 | func (cs *connState) WriteNextMessageTo(w io.Writer) (FrameType, int64, error) { 234 | buffered := cs.Buffered() 235 | if buffered == 0 || len(cs.messageMetadata) == 0 { 236 | return 0, 0, errors.New("no messages in buffer") 237 | } 238 | cs.mu.Lock() 239 | defer cs.mu.Unlock() 240 | got := cs.messageMetadata[0] 241 | cs.messageMetadata = cs.messageMetadata[1:] 242 | if len(cs.messageMetadata) == 0 { 243 | cs.messageMetadata = nil // release memory allocation to prevent leak. 244 | } 245 | n, err := io.CopyBuffer(w, io.LimitReader(&cs.rxBuffer, int64(got.length)), cs.copyBuf) 246 | if err != nil { 247 | return 0, n, err // should be unreachable. 248 | } 249 | return got.ftype, n, nil 250 | } 251 | 252 | func (state *connState) CloseConn(err error) { 253 | if err == nil { 254 | panic("close error cannot be nil") 255 | } 256 | state.mu.Lock() 257 | defer state.mu.Unlock() 258 | if state.closeErr == nil { 259 | state.closeErr = err // Will set first error encountered after close. 260 | } 261 | } 262 | 263 | func (state *connState) ReplyOutstandingFrames(mask uint32, tx *TxBuffered) error { 264 | state.mu.Lock() 265 | defer state.mu.Unlock() 266 | if state.closeErr != nil || len(state.pendingPingOrClose) == 0 { 267 | return nil // Nothing to do. 268 | } 269 | _, err := tx.WritePong(mask, state.pendingPingOrClose) 270 | state.pendingPingOrClose = state.pendingPingOrClose[:0] 271 | if err != nil { 272 | err = fmt.Errorf("failed while responding pong to incoming ping: %w", err) 273 | } 274 | return err 275 | } 276 | 277 | func (state *connState) makeCloseErr() *CloseError { 278 | if len(state.pendingPingOrClose) < 2 { 279 | return &CloseError{Status: StatusNoStatusRcvd} 280 | } 281 | return &CloseError{ 282 | Status: StatusCode(binary.BigEndian.Uint16(state.pendingPingOrClose[:2])), 283 | Reason: state.pendingPingOrClose[2:], 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /definitions.go: -------------------------------------------------------------------------------- 1 | package peasocket 2 | 3 | // CloseError implements the error interface. It represents the websocket 4 | // Close Frame metadata that elaborates the reason for closure and a status code. 5 | type CloseError struct { 6 | Status StatusCode 7 | Reason []byte 8 | } 9 | 10 | // Error implements the error interface for CloseError. Example of output: 11 | // 12 | // "going away: a user defined reason" 13 | func (c *CloseError) Error() string { 14 | return c.Status.String() + ": " + string(c.Reason) 15 | } 16 | 17 | const ( 18 | // MaxControlPayload is the maximum length of a control frame payload as per [section 5.5]. 19 | // 20 | // [section 5.5]: https://tools.ietf.org/html/rfc6455#section-5.5. 21 | MaxControlPayload = 125 22 | maxCloseReason = MaxControlPayload - 2 // minus 2 to include 16 bit status code. 23 | maxOpcode = 0b1111 24 | MaxHeaderSize = 14 25 | ) 26 | 27 | const ( 28 | httpProto = "HTTP/1.1" 29 | httpProtoMinor = 1 30 | httpProtoMajor = 1 31 | ) 32 | 33 | // [Non-control frames]. 34 | // 35 | // [Non-control frames]: https://tools.ietf.org/html/rfc6455#section-11.8. 36 | const ( 37 | FrameContinuation FrameType = iota 38 | FrameText 39 | FrameBinary 40 | // 3 - 7 are reserved for further non-control frames. 41 | _ 42 | _ 43 | _ 44 | _ 45 | _ 46 | _numNCFrames 47 | ) 48 | 49 | // [Control frames]. 50 | // 51 | // [Control frames]: https://tools.ietf.org/html/rfc6455#section-11.8 52 | const ( 53 | FrameClose FrameType = iota + _numNCFrames 54 | // A Ping control frame may serve either as a keepalive or as a means to verify that 55 | // the remote endpoint is still responsive. 56 | // An endpoint MAY send a Ping frame any time after the connection is 57 | // established and before the connection is closed. 58 | FramePing 59 | // A Pong control frame sent in response to a Ping frame must have identical 60 | // "Application data" as found in the message body of the Ping frame 61 | // being replied to. 62 | FramePong 63 | // 11-16 are reserved for further control frames. 64 | ) 65 | 66 | // StatusCode represents a [WebSocket status code]. 67 | // 68 | // [WebSocket status code]: https://tools.ietf.org/html/rfc6455#section-7.4 69 | type StatusCode uint16 70 | 71 | // These are only the [status codes defined by the protocol]. 72 | // 73 | // You can define custom codes in the 3000-4999 range. 74 | // The 3000-3999 range is reserved for use by libraries, frameworks and applications. 75 | // The 4000-4999 range is reserved for private use. 76 | // 77 | // [status codes defined by the protocol]: https://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number 78 | const ( 79 | StatusNormalClosure StatusCode = 1000 80 | StatusGoingAway StatusCode = 1001 81 | StatusProtocolError StatusCode = 1002 82 | StatusUnsupportedData StatusCode = 1003 83 | statusReserved StatusCode = 1004 // 1004 is reserved and so unexported. 84 | 85 | // StatusNoStatusRcvd cannot be sent in a close message. 86 | // It is reserved for when a close message is received without 87 | // a status code. 88 | StatusNoStatusRcvd StatusCode = 1005 89 | 90 | // StatusAbnormalClosure is exported for use only with Wasm. 91 | // In non Wasm Go, the returned error will indicate whether the 92 | // connection was closed abnormally. 93 | StatusAbnormalClosure StatusCode = 1006 94 | 95 | StatusInvalidFramePayloadData StatusCode = 1007 96 | StatusPolicyViolation StatusCode = 1008 97 | StatusMessageTooBig StatusCode = 1009 98 | StatusMandatoryExtension StatusCode = 1010 99 | StatusInternalError StatusCode = 1011 100 | StatusServiceRestart StatusCode = 1012 101 | StatusTryAgainLater StatusCode = 1013 102 | StatusBadGateway StatusCode = 1014 103 | 104 | // StatusTLSHandshake is only exported for use with Wasm. 105 | // In non Wasm Go, the returned error will indicate whether there was 106 | // a TLS handshake failure. 107 | StatusTLSHandshake StatusCode = 1015 108 | ) 109 | 110 | // String returns the lower-cased human readable representation of the websocket closure status code. 111 | func (sc StatusCode) String() (s string) { 112 | switch sc { 113 | case StatusNormalClosure: 114 | s = "normal closure" 115 | case StatusGoingAway: 116 | s = "going away" 117 | case StatusProtocolError: 118 | s = "protocol error" 119 | case StatusUnsupportedData: 120 | s = "unsupported data" 121 | case statusReserved: 122 | s = "reserved" 123 | case StatusNoStatusRcvd: 124 | s = "no status received" 125 | case StatusAbnormalClosure: 126 | s = "abnormal closure" 127 | case StatusInvalidFramePayloadData: 128 | s = "invalid frame payload data" 129 | case StatusPolicyViolation: 130 | s = "policy violation" 131 | case StatusMessageTooBig: 132 | s = "message too big" 133 | case StatusMandatoryExtension: 134 | s = "mandatory extension" 135 | case StatusInternalError: 136 | s = "internal error" 137 | case StatusServiceRestart: 138 | s = "service restart" 139 | case StatusTryAgainLater: 140 | s = "try again later :)" 141 | case StatusBadGateway: 142 | s = "bad gateway" 143 | case StatusTLSHandshake: 144 | s = "TLS handshake" 145 | default: 146 | s = "unknown websocket status code" 147 | } 148 | return s 149 | } 150 | 151 | // String returns the lower-case human readable representation of the websocket op code. 152 | func (ft FrameType) String() (s string) { 153 | switch ft { 154 | case FrameContinuation: 155 | s = "continuation" 156 | case FrameText: 157 | s = "text" 158 | case FrameBinary: 159 | s = "binary" 160 | case FrameClose: 161 | s = "connection close" 162 | case FramePing: 163 | s = "ping" 164 | case FramePong: 165 | s = "pong" 166 | default: 167 | if ft >= 16 { 168 | s = "invalid frame opcode value" 169 | } else if ft >= 7 { 170 | s = "reserved ctl" 171 | } else { 172 | s = "reserved non-ctl" 173 | } 174 | } 175 | return s 176 | } 177 | 178 | // IsUTF8 returns true if the frame type is a websocket text frame. 179 | func (ft FrameType) IsUTF8() bool { return ft == FrameText } 180 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package peasocket_test 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "io" 7 | "log" 8 | "net" 9 | "time" 10 | 11 | "github.com/soypat/peasocket" 12 | ) 13 | 14 | func ExampleClient_echo() { 15 | const ( 16 | message = "Hello!" 17 | ) 18 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 19 | defer cancel() 20 | client := peasocket.NewClient("ws://localhost:8080", nil, nil) 21 | err := client.DialViaHTTPClient(ctx, nil) 22 | if err != nil { 23 | log.Fatal("while dialing:", err) 24 | } 25 | defer client.CloseWebsocket(&peasocket.CloseError{ 26 | Status: peasocket.StatusGoingAway, 27 | Reason: []byte("bye bye"), 28 | }) 29 | log.Printf("protocol switch success. prepare msg=%q", message) 30 | go func() { 31 | // This goroutine reads frames from network. 32 | for { 33 | err := client.HandleNextFrame() 34 | if errors.Is(err, net.ErrClosed) { 35 | log.Println("connection closed, ending loop") 36 | return 37 | } 38 | if err != nil { 39 | log.Println("read next frame failed:", err) 40 | time.Sleep(500 * time.Millisecond) 41 | } 42 | } 43 | }() 44 | 45 | for { 46 | // This goroutine gets messages that have been read 47 | // from the client's buffer and prints them. 48 | msg, _, err := client.BufferedMessageReader() 49 | if err != nil { 50 | if errors.Is(err, net.ErrClosed) { 51 | log.Fatal("websocket closed") 52 | } 53 | log.Println("no next message err:", err) 54 | time.Sleep(500 * time.Millisecond) 55 | continue 56 | } 57 | 58 | b, err := io.ReadAll(msg) 59 | log.Println("got message:", string(b)) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /examples/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "log" 7 | "os" 8 | "runtime/pprof" 9 | "time" 10 | 11 | "github.com/soypat/peasocket" 12 | ) 13 | 14 | var ( 15 | flagAddress string 16 | flagPingMessage string 17 | flagPingPeriod time.Duration 18 | ) 19 | 20 | func main() { 21 | fp, _ := os.Create("server.pprof") 22 | err := pprof.StartCPUProfile(fp) 23 | if err != nil { 24 | log.Fatal(err) 25 | } 26 | start := time.Now() 27 | 28 | flag.StringVar(&flagAddress, "addr", "127.0.0.1:8080", "Address on which to listen for websocket requests") 29 | flag.StringVar(&flagPingMessage, "pingmessage", "abc123ABC", "Heartbeat message") 30 | // flag.DurationVar(&flagHeartbeatPeriod, "hbperiod", 5*time.Second, "Time between heartbeat messages") 31 | flag.DurationVar(&flagPingPeriod, "ping", 1*time.Second, "Time between ping messages") 32 | flag.Parse() 33 | pingMessage := []byte(flagPingMessage) 34 | ctx := context.Background() 35 | log.Println("websocket listener on ", flagAddress) 36 | err = peasocket.ListenAndServe(ctx, flagAddress, func(ctx context.Context, sv *peasocket.Server) { 37 | if err := ctx.Err(); err != nil { 38 | panic("context err on entry: " + err.Error()) 39 | } 40 | go func() { 41 | backoff := peasocket.ExponentialBackoff{MaxWait: 500 * time.Millisecond} 42 | for ctx.Err() == nil && sv.IsConnected() { 43 | err := sv.HandleNextFrame() 44 | if err != nil { 45 | log.Print("error reading next frame:", err) 46 | backoff.Miss() 47 | } else { 48 | backoff.Hit() 49 | } 50 | } 51 | }() 52 | lastPing := time.Now() 53 | if time.Since(start) > 20*time.Second { 54 | pprof.StopCPUProfile() 55 | } 56 | backoff := peasocket.ExponentialBackoff{MaxWait: 500 * time.Millisecond} 57 | for { 58 | now := time.Now() 59 | if now.Sub(lastPing) > flagPingPeriod { 60 | backoff.Hit() 61 | pingctx, cancel := context.WithTimeout(ctx, flagPingPeriod) 62 | lastPing = now 63 | err := sv.Ping(pingctx, pingMessage) 64 | cancel() 65 | if err != nil { 66 | log.Println("ping error:", err) 67 | return 68 | } 69 | } else { 70 | backoff.Miss() 71 | } 72 | } 73 | }) 74 | log.Println("server error:", err) 75 | } 76 | -------------------------------------------------------------------------------- /fuzz_test.go: -------------------------------------------------------------------------------- 1 | package peasocket 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "math" 7 | "testing" 8 | ) 9 | 10 | // The go generate command below adds the fuzz corpus in the cache to git VCS. 11 | // 12 | //go:generate mv $(go env GOCACHE)/fuzz/github.com/soypat/peasocket/. testdata/fuzz/ 13 | func FuzzHeaderPut(f *testing.F) { 14 | testCases := []struct { 15 | payloadLen uint64 16 | maskKey uint32 17 | }{ 18 | {maskKey: 0, payloadLen: 20}, 19 | {maskKey: 0xa2312434, payloadLen: math.MaxUint16 + 20}, 20 | {maskKey: 0, payloadLen: math.MaxUint16 + 20}, 21 | {maskKey: 0xa2312434, payloadLen: 20}, 22 | } 23 | for _, tc := range testCases { 24 | f.Add(tc.payloadLen, tc.maskKey) 25 | } 26 | f.Fuzz(func(t *testing.T, payloadLen uint64, mask uint32) { 27 | if payloadLen == 0 { 28 | return 29 | } 30 | h := newHeader(FrameBinary, payloadLen, mask, false) 31 | var buf [MaxHeaderSize]byte 32 | n, err := h.Put(buf[:]) 33 | if err != nil { 34 | panic(err) 35 | } 36 | hgot, n2, err := DecodeHeader(bytes.NewReader(buf[:n])) 37 | if err != nil { 38 | panic(err) 39 | } 40 | if n2 != n { 41 | panic("lengths not equal over wire") 42 | } 43 | if h != hgot { 44 | panic("encoding not match decoding") 45 | } 46 | }) 47 | } 48 | func FuzzMaskedReader(f *testing.F) { 49 | testCases := []struct { 50 | data []byte 51 | maskKey uint32 52 | }{ 53 | {data: []byte("asd\x00ASd\xff\xf0"), maskKey: 0xa2312434}, 54 | {data: []byte("asd\x00ASd\xff\xf0"), maskKey: 0}, 55 | } 56 | for _, tc := range testCases { 57 | f.Add(tc.data, tc.maskKey) 58 | } 59 | f.Fuzz(func(t *testing.T, data []byte, maskKey uint32) { 60 | if len(data) == 0 { 61 | return 62 | } 63 | mr := maskedReader{ 64 | R: bytes.NewBuffer(data), 65 | MaskKey: maskKey, 66 | } 67 | expect := append([]byte{}, data...) 68 | expectKey := mask(maskKey, expect) 69 | got1 := make([]byte, len(data)/2+1) 70 | expect1 := expect[:len(got1)] 71 | n, err := mr.Read(got1) 72 | if err != nil || n != len(got1) { 73 | t.Fatal(err, n, len(got1)) 74 | } 75 | if !bytes.Equal(expect1, got1) { 76 | t.Errorf("expected %q, got %q", expect1, got1) 77 | } 78 | // Proceed with next mask op 79 | got2 := make([]byte, len(expect)-len(got1)) 80 | expect2 := expect[len(got1):] 81 | n, err = mr.Read(got2) 82 | if err != nil || n != len(got2) { 83 | t.Fatal(err, n, len(got2)) 84 | } 85 | if !bytes.Equal(expect2, got2) { 86 | t.Errorf("second mask op expected %q, got %q", expect2, got2) 87 | } 88 | if expectKey != mr.MaskKey { 89 | t.Errorf("bad mask key, expect %v, got %v", expectKey, mr.MaskKey) 90 | } 91 | }) 92 | } 93 | 94 | func FuzzLoopbackMessage(f *testing.F) { 95 | testCases := []struct { 96 | data []byte 97 | maskKey uint32 98 | }{ 99 | {data: []byte("asd\x00ASd\xff\xf0"), maskKey: 0xa2312434}, 100 | {data: []byte("asd\x00ASd\xff\xf0"), maskKey: 0}, 101 | } 102 | for _, tc := range testCases { 103 | f.Add(tc.data, tc.maskKey) 104 | } 105 | f.Fuzz(func(t *testing.T, dataCanon []byte, maskKey uint32) { 106 | if len(dataCanon) == 0 { 107 | return 108 | } 109 | trp := &transport{} 110 | loopBuf := &trp.buf 111 | datacp := append([]byte{}, dataCanon...) 112 | tx := &TxBuffered{ 113 | trp: trp, 114 | } 115 | rx := Rx{ 116 | trp: trp, 117 | } 118 | written, err := tx.WriteMessage(maskKey, datacp) 119 | if err != nil { 120 | t.Fatal(err) 121 | } 122 | actualWritten := loopBuf.Len() 123 | if written != actualWritten { 124 | t.Error("written bytes not match result of WriteMessage", written, loopBuf.Len()) 125 | } 126 | callbackCalled := false 127 | rx.RxCallbacks.OnMessage = func(rx *Rx, r io.Reader) error { 128 | callbackCalled = true 129 | b, err := io.ReadAll(r) 130 | if err != nil { 131 | t.Fatal(err) 132 | } 133 | pl := rx.LastReceivedHeader.PayloadLength 134 | if uint64(len(b)) != pl { 135 | t.Error("expected payload length not match read", len(b), pl) 136 | } 137 | if !bytes.Equal(b, dataCanon) { 138 | dataMasked := append([]byte{}, dataCanon...) 139 | maskWS(maskKey, dataMasked) 140 | t.Errorf("data loopback failed for %v\ngot:\n\t%q\nexpect:\n\t%q\nexpect masked:\n\t%q", rx.LastReceivedHeader.String(), b, dataCanon, dataMasked) 141 | } 142 | return nil 143 | } 144 | n, err := rx.ReadNextFrame() 145 | if err != nil { 146 | t.Error(err) 147 | } 148 | if !callbackCalled { 149 | t.Error("callback not called") 150 | } 151 | if n != written { 152 | t.Error("read bytes not match bytes written over loopback", n, actualWritten) 153 | } 154 | }) 155 | } 156 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/soypat/peasocket 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /peasocket.go: -------------------------------------------------------------------------------- 1 | package peasocket 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/binary" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "math" 10 | "time" 11 | ) 12 | 13 | // Header represents a [WebSocket frame header]. A header can occupy 14 | // 2 to 10 bytes on the wire depending on payload size and if content is masked. 15 | // 16 | // 0 1 2 3 17 | // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 18 | // +-+-+-+-+-------+-+-------------+-------------------------------+ 19 | // |F|R|R|R| opcode|M| Payload len | Extended payload length | 20 | // |I|S|S|S| (4) |A| (7) | (16/64) | 21 | // |N|V|V|V| |S| | (if payload len==126/127) | 22 | // | |1|2|3| |K| | | 23 | // +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + 24 | // | Extended payload length continued, if payload len == 127 | 25 | // + - - - - - - - - - - - - - - - +-------------------------------+ 26 | // | |Masking-key, if MASK set to 1 | 27 | // +-------------------------------+-------------------------------+ 28 | // | Masking-key (continued) | Payload Data | 29 | // +-------------------------------- - - - - - - - - - - - - - - - + 30 | // : Payload Data continued ... : 31 | // + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 32 | // | Payload Data continued ... | 33 | // +---------------------------------------------------------------+ 34 | // 35 | // [WebSocket frame header]: https://tools.ietf.org/html/rfc6455#section-5.2. 36 | type Header struct { 37 | firstByte byte 38 | // All frames sent from the client to the server are masked by a 39 | // 32-bit value that is contained within the frame. This field is 40 | // present if the mask bit is set to 1 and is absent if the mask bit 41 | // is set to 0. See Section 5.3 for further information on client- 42 | // to-server masking. 43 | Mask uint32 // A mask key of zero has no effect in XOR operations, thus zero value is no mask. 44 | 45 | // Defines whether the "Payload data" is masked. If set to 1, a 46 | // masking key is present in masking-key, and this is used to unmask 47 | // the "Payload data" as per Section 5.3. All frames sent from 48 | // client to server have this bit set to 1. 49 | // Masked bool 50 | // The length of the "Payload data", in bytes: if 0-125, that is the 51 | // payload length. If 126, the following 2 bytes interpreted as a 52 | // 16-bit unsigned integer are the payload length. If 127, the 53 | // following 8 bytes interpreted as a 64-bit unsigned integer (the 54 | // most significant bit MUST be 0) are the payload length. 55 | PayloadLength uint64 56 | } 57 | 58 | // Defines the interpretation of the "Payload data". If an unknown 59 | // opcode is received, the receiving endpoint MUST Fail the 60 | // WebSocket Connection. 61 | type FrameType uint8 62 | 63 | // IsControl returns true if the frame is valid and is a control frame (ping, pong or close). 64 | func (op FrameType) IsControl() bool { return op <= maxOpcode && op&(1<<3) != 0 } 65 | 66 | // FrameType returns the header's operation code as per [section 11]. 67 | // 68 | // [section 11]: https://tools.ietf.org/html/rfc6455#section-11.8. 69 | func (h Header) FrameType() FrameType { 70 | return FrameType(h.firstByte & 0b1111) 71 | } 72 | 73 | // IsMasked returns true if payload is masked. 74 | func (h Header) IsMasked() bool { return h.Mask != 0 } 75 | 76 | // Fin returns true if the FIN bit in the frame header is set. Always should be set for control frames. 77 | func (h Header) Fin() bool { return h.firstByte&(1<<7) != 0 } 78 | 79 | // Rsv1 returns true if the first frame header reserved bit is set. Used for extensions. 80 | func (h Header) Rsv1() bool { return h.firstByte&(1<<6) != 0 } 81 | 82 | // Rsv2 returns true if the second frame header reserved bit is set. Used for extensions. 83 | func (h Header) Rsv2() bool { return h.firstByte&(1<<5) != 0 } 84 | 85 | // Rsv3 returns true if the third frame header reserved bit is set. Used for extensions. 86 | func (h Header) Rsv3() bool { return h.firstByte&(1<<4) != 0 } 87 | 88 | // String returns a human readable representation of the Frame header in the following format: 89 | // 90 | // Frame: (payload=) FIN: 91 | func (h Header) String() string { 92 | fin, rsv1, rsv2, rsv3 := h.Fin(), h.Rsv1(), h.Rsv2(), h.Rsv3() 93 | if rsv1 || rsv2 || rsv3 { 94 | return fmt.Sprintf("Frame:%v (payload=%v) FIN:%t RSV:%v|%v|%v", h.FrameType().String(), h.PayloadLength, fin, rsv1, rsv2, rsv3) 95 | } 96 | return fmt.Sprintf("Frame:%v (payload=%v) FIN:%t", h.FrameType().String(), h.PayloadLength, fin) 97 | } 98 | 99 | // NewHeader creates a new websocket frame header. Set mask to 0 to disable masking 100 | // on the payload. 101 | func NewHeader(op FrameType, payload int, mask uint, fin bool) (Header, error) { 102 | if op > 0b1111 { 103 | return Header{}, errors.New("invalid opcode") 104 | } 105 | if payload < 0 { 106 | return Header{}, errors.New("negative payload length") 107 | } 108 | if mask > math.MaxUint32 { 109 | return Header{}, errors.New("negative mask or mask exceeds 32bit unsigned integer range") 110 | } 111 | return newHeader(op, uint64(payload), uint32(mask), fin), nil 112 | } 113 | 114 | // newHeader is a convenience function for easily creating Headers skipping the validation step. 115 | func newHeader(op FrameType, payload uint64, mask uint32, fin bool) Header { 116 | return Header{firstByte: byte(op) | b2u8(fin)<<7, Mask: mask, PayloadLength: payload} 117 | } 118 | 119 | var ( 120 | errReadingFirstHeaderByte = errors.New("reading first byte in header") 121 | ) 122 | 123 | // DecodeHeader reads a header from the io.Reader. It assumes the first byte corresponds 124 | // to the header's first byte. 125 | func DecodeHeader(r io.Reader) (Header, int, error) { 126 | var vbuf [1]byte 127 | _, err := r.Read(vbuf[:]) 128 | if err != nil { 129 | return Header{}, 0, fmt.Errorf("%s: %w", errReadingFirstHeaderByte, err) 130 | } 131 | firstByte := vbuf[0] 132 | if err != nil { 133 | return Header{}, 0, err 134 | } 135 | plen, masked, n, err := decodePayloadLength(r) 136 | n++ //add first byte 137 | if err != nil { 138 | return Header{}, n, err 139 | } 140 | h := Header{ 141 | firstByte: firstByte, 142 | PayloadLength: plen, 143 | } 144 | if masked { 145 | var buf [4]byte 146 | ngot, err := io.ReadFull(r, buf[:4]) 147 | n += ngot 148 | if err != nil { 149 | return Header{}, n, err 150 | } 151 | h.Mask = binary.BigEndian.Uint32(buf[:4]) 152 | } 153 | return h, n, nil 154 | } 155 | 156 | func decodePayloadLength(r io.Reader) (v uint64, masked bool, n int, err error) { 157 | var buf [8]byte 158 | n, err = r.Read(buf[:1]) // Read single byte 159 | masked = buf[0]&(1<<7) != 0 160 | buf[0] &^= (1 << 7) // Exclude mask bit from payload. 161 | if err != nil { 162 | if n == 0 { 163 | return 0, masked, n, errors.New("unexpected 0 bytes read from buffer and no error returned") 164 | } else if n == 1 && errors.Is(err, io.EOF) && buf[0] == 0 { 165 | // No payload. to read. 166 | return 0, masked, n, nil 167 | } 168 | return 0, masked, n, err 169 | } 170 | var ngot int 171 | switch buf[0] { 172 | case 126: 173 | ngot, err = io.ReadFull(r, buf[0:2]) 174 | v = uint64(binary.BigEndian.Uint16(buf[:2])) 175 | case 127: 176 | ngot, err = io.ReadFull(r, buf[:8]) 177 | v = binary.BigEndian.Uint64(buf[:8]) 178 | default: 179 | return uint64(buf[0]), masked, n, nil 180 | } 181 | n += ngot 182 | if err != nil { 183 | return 0, masked, n, err 184 | } 185 | if v < 126 { 186 | return 0, masked, n, errors.New("payload length protocol violation: minimal number of bytes MUST be used to encode the length") 187 | } 188 | return v, masked, n, nil 189 | } 190 | 191 | // Encode writes the websocket header to w as it would be sent over the wire. 192 | // It does not encode a payload. 193 | func (h *Header) Encode(w io.Writer) (int, error) { 194 | var buf [MaxHeaderSize]byte 195 | n, err := h.Put(buf[:]) 196 | if err != nil { 197 | return 0, err 198 | } 199 | return writeFull(w, buf[:n]) 200 | } 201 | 202 | // Put encodes the header into the start of the byte slice. 203 | func (h *Header) Put(b []byte) (int, error) { 204 | masked := h.IsMasked() 205 | hsize := headerSizeFromMessageSize(int(h.PayloadLength), masked) 206 | if len(b) < hsize { 207 | return 0, io.ErrShortBuffer 208 | } 209 | b[0] = h.firstByte 210 | n := h.putPayloadLength(b[1:]) + 1 211 | if masked { 212 | binary.BigEndian.PutUint32(b[n:], h.Mask) 213 | n += 4 214 | } 215 | // TODO(soypat): Remove once tests cover all cases. 216 | if hsize != n { 217 | panic("bug in Header.Put") 218 | } 219 | return n, nil 220 | } 221 | 222 | // putPayloadLength needs buf to be of length 9. 223 | func (h *Header) putPayloadLength(buf []byte) (n int) { 224 | mask := b2u8(h.IsMasked()) << 7 225 | pl := h.PayloadLength 226 | switch { 227 | case pl < 126: 228 | buf[0] = mask | byte(pl) 229 | n = 1 230 | case pl <= math.MaxUint16: 231 | buf[0] = mask | 126 232 | binary.BigEndian.PutUint16(buf[1:3], uint16(pl)) 233 | n = 3 234 | 235 | default: 236 | buf[0] = mask | 127 237 | binary.BigEndian.PutUint64(buf[1:9], pl) 238 | n = 9 239 | } 240 | return n 241 | } 242 | 243 | func defaultEntropy() uint32 { 244 | var output [4]byte 245 | _, err := io.ReadFull(rand.Reader, output[:]) 246 | if err != nil { 247 | panic(err) 248 | } 249 | return binary.BigEndian.Uint32(output[:]) 250 | } 251 | 252 | // b2u8 converts a bool to an uint8. true==1, false==0. 253 | func b2u8(b bool) uint8 { 254 | if b { 255 | return 1 256 | } 257 | return 0 258 | } 259 | 260 | // ExponentialBackoff implements a [Exponential Backoff] 261 | // delay algorithm to prevent saturation network or processor 262 | // with failing tasks. An ExponentialBackoff with a non-zero MaxWait is ready for use. 263 | // 264 | // [Exponential Backoff]: https://en.wikipedia.org/wiki/Exponential_backoff 265 | type ExponentialBackoff struct { 266 | // Wait defines the amount of time that Miss will wait on next call. 267 | Wait time.Duration 268 | // Maximum allowable value for Wait. 269 | MaxWait time.Duration 270 | // StartWait is the value that Wait takes after a call to Hit. 271 | StartWait time.Duration 272 | // ExpMinusOne is the shift performed on Wait minus one, so the zero value performs a shift of 1. 273 | ExpMinusOne uint32 274 | } 275 | 276 | // Hit sets eb.Wait to the StartWait value. 277 | func (eb *ExponentialBackoff) Hit() { 278 | if eb.MaxWait == 0 { 279 | panic("MaxWait cannot be zero") 280 | } 281 | eb.Wait = eb.StartWait 282 | } 283 | 284 | // Miss sleeps for eb.Wait and increases eb.Wait exponentially. 285 | func (eb *ExponentialBackoff) Miss() { 286 | const k = 1 287 | wait := eb.Wait 288 | maxWait := eb.MaxWait 289 | exp := eb.ExpMinusOne + 1 290 | if maxWait == 0 { 291 | panic("MaxWait cannot be zero") 292 | } 293 | time.Sleep(wait) 294 | wait |= time.Duration(k) 295 | wait <<= exp 296 | if wait > maxWait { 297 | wait = maxWait 298 | } 299 | eb.Wait = wait 300 | } 301 | -------------------------------------------------------------------------------- /peasocket_test.go: -------------------------------------------------------------------------------- 1 | package peasocket 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "encoding/binary" 7 | "io" 8 | "math/bits" 9 | "strings" 10 | "testing" 11 | "time" 12 | "unsafe" 13 | ) 14 | 15 | func TestMask(t *testing.T) { 16 | for _, tc := range []struct { 17 | desc string 18 | maskKey uint32 19 | msg, expect string 20 | }{ 21 | {desc: "zero message becomes mask", maskKey: 0xff00_ff00, msg: "\x00\x00\x00\x00", expect: "\xff\x00\xff\x00"}, 22 | {desc: "zero mask no effect", maskKey: 0, msg: "\x00\xff\x00\xf0", expect: "\x00\xff\x00\xf0"}, 23 | {desc: "proper xor", maskKey: 0xff16ff39, msg: "\xf0\xfa\xaa\x1e", expect: "\x0f\xec\x55\x27"}, 24 | {desc: "fifth byte xor by last key byte", maskKey: 0xff16ff39, msg: "\xf0\xfa\xaa\x1e\xd8", expect: "\x0f\xec\x55\x27\x27"}, 25 | {desc: "correct key rotation", maskKey: 0xff16ff39, msg: "\xf0\xfa\xaa\x1e\x1e\xfa", expect: "\x0f\xec\x55\x27\xe1\xec"}, 26 | } { 27 | got := []byte(tc.msg) 28 | gotLE := []byte(tc.msg) 29 | le := tc.maskKey // binary.BigEndian.Uint32((*[4]byte)(unsafe.Pointer(&tc.maskKey))[:]) 30 | expectKey := maskWS(tc.maskKey, got) 31 | _ = mask(le, gotLE) 32 | if string(got) != tc.expect { 33 | t.Errorf("%v: key=%#X\n\tmessage: %q\n\texpected: %q\n\tgot: %q", tc.desc, tc.maskKey, tc.msg, tc.expect, got) 34 | } 35 | if string(gotLE) != tc.expect { 36 | t.Errorf("%v: LE key=%#X\n\tmessage: %q\n\texpected: %q\n\tgot: %q", tc.desc, le, tc.msg, tc.expect, gotLE) 37 | } 38 | 39 | split1 := []byte(tc.msg[:len(tc.msg)/2+1]) 40 | split2 := []byte(tc.msg[len(split1):]) 41 | intermediateKey := maskWS(tc.maskKey, split1) 42 | finalKey := maskWS(intermediateKey, split2) 43 | if finalKey != expectKey { 44 | t.Error("split message key and full message keyt not match:", finalKey, expectKey) 45 | } 46 | got = append(split1, split2...) 47 | if string(got) != tc.expect { 48 | t.Errorf("2STEP %v: key=%#X\n\tmessage: %q\n\texpected: %q\n\tgot: %q", tc.desc, tc.maskKey, tc.msg, tc.expect, got) 49 | } 50 | } 51 | } 52 | 53 | func TestFragment(t *testing.T) { 54 | loopback := bytes.NewBuffer(nil) 55 | tx := TxBuffered{} 56 | rx := Rx{} 57 | tx.SetTxTransport(&closer{Writer: loopback}) 58 | rx.SetRxTransport(io.NopCloser(loopback)) 59 | // e := defaultEntropy() 60 | const message = "hello world!" 61 | buffer := make([]byte, 24) 62 | rd := strings.NewReader(message) 63 | n, err := tx.WriteFragmentedMessage(0, rd, buffer) 64 | wireLen := loopback.Len() 65 | t.Log(loopback.String()) 66 | if err != nil { 67 | t.Fatal(err) 68 | } else if n != wireLen { 69 | t.Error("returned bytes written of WriteFragmentedMessage not match written to loopback", n, wireLen) 70 | } 71 | done := false 72 | var buf bytes.Buffer 73 | rx.RxCallbacks.OnMessage = func(rx *Rx, message io.Reader) error { 74 | done = rx.LastReceivedHeader.Fin() 75 | buf.ReadFrom(message) 76 | return nil 77 | } 78 | i := 0 79 | for !done { 80 | i++ 81 | n, err = rx.ReadNextFrame() 82 | if err != nil || n != wireLen { 83 | t.Error("error reading frame", n, wireLen, err) 84 | } 85 | if i > 8 { 86 | t.Error("too many cycles, fin bit may have been incorrectly set") 87 | break 88 | } 89 | } 90 | got := buf.String() 91 | if got != message { 92 | t.Errorf("message garbled/incorrect after transferring with fragmentation\n\tgot:%q\n\twant:%q", got, message) 93 | } 94 | } 95 | 96 | func TestServerClientPingPong(t *testing.T) { 97 | const waitTimes = 1e7 * time.Millisecond 98 | svRxloopback := &transport{} 99 | svTxloopback := &transport{} 100 | sv := NewServer(make([]byte, 1024)) 101 | cl := NewClient("abc", make([]byte, 1024), nil) 102 | sv.rx.trp = svRxloopback 103 | sv.tx.trp = svTxloopback 104 | sv.state.OnConnect() 105 | cl.rx.trp = svTxloopback 106 | cl.tx.trp = svRxloopback 107 | cl.state.OnConnect() 108 | mainCtx := context.Background() 109 | svPingMessage := []byte("hello, buenos dias") 110 | for i := 0; i < 5; i++ { 111 | doneSv := false 112 | go func() { 113 | ctx, cancel := context.WithTimeout(mainCtx, waitTimes) 114 | defer cancel() 115 | err := sv.Ping(ctx, svPingMessage) 116 | if err != nil { 117 | panic(err) 118 | } 119 | doneSv = true 120 | }() 121 | time.Sleep(50 * time.Millisecond) 122 | err := cl.HandleNextFrame() 123 | if err != nil { 124 | t.Fatal(err) 125 | } 126 | err = sv.HandleNextFrame() 127 | if err != nil { 128 | t.Error("reading next frame", err) 129 | } 130 | time.Sleep(50 * time.Millisecond) 131 | if sv.state.PendingAction() { 132 | t.Error("server pending action") 133 | } 134 | if !doneSv { 135 | t.Fatalf("server did not get response rx=%q tx=%q", svRxloopback.buf.String(), svTxloopback.buf.String()) 136 | } 137 | } 138 | } 139 | 140 | // mask applies the WebSocket masking algorithm to b 141 | // with the given key. Taken from nhooyr's implementation. 142 | // See https://tools.ietf.org/html/rfc6455#section-5.3 143 | // 144 | // The returned value is the correctly rotated key to 145 | // to continue to mask/unmask the message. 146 | // 147 | // It is optimized for LittleEndian architectures. 148 | // 149 | // See https://github.com/golang/go/issues/31586 150 | func mask(key uint32, b []byte) uint32 { 151 | // Convert key to little endian: 152 | key = binary.BigEndian.Uint32((*[4]byte)(unsafe.Pointer(&key))[:]) 153 | if len(b) >= 8 { 154 | key64 := uint64(key)<<32 | uint64(key) 155 | 156 | // At some point in the future we can clean these unrolled loops up. 157 | // See https://github.com/golang/go/issues/31586#issuecomment-487436401 158 | 159 | // Then we xor until b is less than 128 bytes. 160 | for len(b) >= 128 { 161 | v := binary.LittleEndian.Uint64(b) 162 | binary.LittleEndian.PutUint64(b, v^key64) 163 | v = binary.LittleEndian.Uint64(b[8:16]) 164 | binary.LittleEndian.PutUint64(b[8:16], v^key64) 165 | v = binary.LittleEndian.Uint64(b[16:24]) 166 | binary.LittleEndian.PutUint64(b[16:24], v^key64) 167 | v = binary.LittleEndian.Uint64(b[24:32]) 168 | binary.LittleEndian.PutUint64(b[24:32], v^key64) 169 | v = binary.LittleEndian.Uint64(b[32:40]) 170 | binary.LittleEndian.PutUint64(b[32:40], v^key64) 171 | v = binary.LittleEndian.Uint64(b[40:48]) 172 | binary.LittleEndian.PutUint64(b[40:48], v^key64) 173 | v = binary.LittleEndian.Uint64(b[48:56]) 174 | binary.LittleEndian.PutUint64(b[48:56], v^key64) 175 | v = binary.LittleEndian.Uint64(b[56:64]) 176 | binary.LittleEndian.PutUint64(b[56:64], v^key64) 177 | v = binary.LittleEndian.Uint64(b[64:72]) 178 | binary.LittleEndian.PutUint64(b[64:72], v^key64) 179 | v = binary.LittleEndian.Uint64(b[72:80]) 180 | binary.LittleEndian.PutUint64(b[72:80], v^key64) 181 | v = binary.LittleEndian.Uint64(b[80:88]) 182 | binary.LittleEndian.PutUint64(b[80:88], v^key64) 183 | v = binary.LittleEndian.Uint64(b[88:96]) 184 | binary.LittleEndian.PutUint64(b[88:96], v^key64) 185 | v = binary.LittleEndian.Uint64(b[96:104]) 186 | binary.LittleEndian.PutUint64(b[96:104], v^key64) 187 | v = binary.LittleEndian.Uint64(b[104:112]) 188 | binary.LittleEndian.PutUint64(b[104:112], v^key64) 189 | v = binary.LittleEndian.Uint64(b[112:120]) 190 | binary.LittleEndian.PutUint64(b[112:120], v^key64) 191 | v = binary.LittleEndian.Uint64(b[120:128]) 192 | binary.LittleEndian.PutUint64(b[120:128], v^key64) 193 | b = b[128:] 194 | } 195 | 196 | // Then we xor until b is less than 64 bytes. 197 | for len(b) >= 64 { 198 | v := binary.LittleEndian.Uint64(b) 199 | binary.LittleEndian.PutUint64(b, v^key64) 200 | v = binary.LittleEndian.Uint64(b[8:16]) 201 | binary.LittleEndian.PutUint64(b[8:16], v^key64) 202 | v = binary.LittleEndian.Uint64(b[16:24]) 203 | binary.LittleEndian.PutUint64(b[16:24], v^key64) 204 | v = binary.LittleEndian.Uint64(b[24:32]) 205 | binary.LittleEndian.PutUint64(b[24:32], v^key64) 206 | v = binary.LittleEndian.Uint64(b[32:40]) 207 | binary.LittleEndian.PutUint64(b[32:40], v^key64) 208 | v = binary.LittleEndian.Uint64(b[40:48]) 209 | binary.LittleEndian.PutUint64(b[40:48], v^key64) 210 | v = binary.LittleEndian.Uint64(b[48:56]) 211 | binary.LittleEndian.PutUint64(b[48:56], v^key64) 212 | v = binary.LittleEndian.Uint64(b[56:64]) 213 | binary.LittleEndian.PutUint64(b[56:64], v^key64) 214 | b = b[64:] 215 | } 216 | 217 | // Then we xor until b is less than 32 bytes. 218 | for len(b) >= 32 { 219 | v := binary.LittleEndian.Uint64(b) 220 | binary.LittleEndian.PutUint64(b, v^key64) 221 | v = binary.LittleEndian.Uint64(b[8:16]) 222 | binary.LittleEndian.PutUint64(b[8:16], v^key64) 223 | v = binary.LittleEndian.Uint64(b[16:24]) 224 | binary.LittleEndian.PutUint64(b[16:24], v^key64) 225 | v = binary.LittleEndian.Uint64(b[24:32]) 226 | binary.LittleEndian.PutUint64(b[24:32], v^key64) 227 | b = b[32:] 228 | } 229 | 230 | // Then we xor until b is less than 16 bytes. 231 | for len(b) >= 16 { 232 | v := binary.LittleEndian.Uint64(b) 233 | binary.LittleEndian.PutUint64(b, v^key64) 234 | v = binary.LittleEndian.Uint64(b[8:16]) 235 | binary.LittleEndian.PutUint64(b[8:16], v^key64) 236 | b = b[16:] 237 | } 238 | 239 | // Then we xor until b is less than 8 bytes. 240 | for len(b) >= 8 { 241 | v := binary.LittleEndian.Uint64(b) 242 | binary.LittleEndian.PutUint64(b, v^key64) 243 | b = b[8:] 244 | } 245 | } 246 | 247 | // Then we xor until b is less than 4 bytes. 248 | for len(b) >= 4 { 249 | v := binary.LittleEndian.Uint32(b) 250 | binary.LittleEndian.PutUint32(b, v^key) 251 | b = b[4:] 252 | } 253 | 254 | // xor remaining bytes. 255 | for i := range b { 256 | b[i] ^= byte(key) 257 | key = bits.RotateLeft32(key, -8) 258 | } 259 | // Convert key back to big endian. 260 | key = binary.BigEndian.Uint32((*[4]byte)(unsafe.Pointer(&key))[:]) 261 | return key 262 | } 263 | 264 | type closer struct { 265 | io.Writer 266 | closed bool 267 | } 268 | 269 | func (c *closer) Close() error { 270 | c.closed = true 271 | return nil 272 | } 273 | 274 | type transport struct { 275 | buf bytes.Buffer 276 | closed bool 277 | } 278 | 279 | func (c *transport) Close() error { 280 | c.closed = true 281 | return nil 282 | } 283 | 284 | func (c *transport) Read(b []byte) (int, error) { 285 | if c.closed { 286 | return 0, io.EOF 287 | } 288 | return c.buf.Read(b) 289 | } 290 | 291 | func (c *transport) Write(b []byte) (int, error) { 292 | if c.closed { 293 | return 0, io.EOF 294 | } 295 | return c.buf.Write(b) 296 | } 297 | -------------------------------------------------------------------------------- /rxtx.go: -------------------------------------------------------------------------------- 1 | package peasocket 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "math" 10 | "math/bits" 11 | ) 12 | 13 | // rxtx.go will contain basic frame marshalling and unmarshalling logic 14 | // agnostic to the data being sent and the websocket control flow. 15 | 16 | // Rx provides a way to handle a Websocket transport. 17 | type Rx struct { 18 | LastReceivedHeader Header 19 | trp io.ReadCloser 20 | RxCallbacks RxCallbacks 21 | } 22 | 23 | type RxCallbacks struct { 24 | // OnError executed when a decoding error is encountered after 25 | // consuming a non-zero amoung of bytes from the underlying transport. 26 | // If this callback is set then it becomes the responsability of the callback 27 | // to close the underlying transport. 28 | OnError func(rx *Rx, err error) 29 | 30 | // If OnCtl is not nil then this is executed on every control frame received. 31 | // These may or may not have a payload that can be readily read from r. 32 | OnCtl func(rx *Rx, payload io.Reader) error 33 | // If OnMessage is not nil it is called on the Rx for every application 34 | // message received (non-control frames). 35 | // The Application Message is contained in message which will return EOF 36 | // when the whole payload has been read. 37 | // The payload bytes are unmasked unless unmasking explicitly disabled on the Rx. 38 | OnMessage func(rx *Rx, message io.Reader) error 39 | } 40 | 41 | // SetRxTransport sets the underlying transport of rx. 42 | func (rx *Rx) SetRxTransport(rc io.ReadCloser) { 43 | rx.trp = rc 44 | } 45 | 46 | // ReadNextFrame reads a frame from the underlying transport of rx. 47 | // This method is meant to be used after setting the rx's callbacks. 48 | // If the callback is not set then the payload data will be read and discarded. 49 | func (rx *Rx) ReadNextFrame() (int, error) { 50 | var reader io.Reader = rx.trp 51 | h, n, err := DecodeHeader(reader) 52 | if err != nil { 53 | if n > 0 || errors.Is(err, io.EOF) { 54 | // EOF means the connection is no longer usable, must close. 55 | rx.handleErr(err) 56 | } 57 | return n, err 58 | } 59 | rx.LastReceivedHeader = h 60 | // Perform basic frame validation. 61 | op := h.FrameType() 62 | isFin := h.Fin() 63 | isCtl := op.IsControl() 64 | if isCtl { 65 | if !isFin { 66 | err = errors.New("control frame needs FIN bit set") 67 | } else if h.PayloadLength > MaxControlPayload { 68 | err = errors.New("payload too large for control frame (>255)") 69 | } 70 | } 71 | if err != nil { 72 | rx.handleErr(err) 73 | return n, err 74 | } 75 | // Wrap reader to limit to payload length and unmask the received bytes if payload is masked. 76 | if h.IsMasked() { 77 | reader = newMaskedReader(reader, h.Mask) 78 | } 79 | lr := &io.LimitedReader{R: reader, N: int64(h.PayloadLength)} 80 | if isCtl && rx.RxCallbacks.OnCtl != nil { 81 | err = rx.RxCallbacks.OnCtl(rx, lr) 82 | } else if rx.RxCallbacks.OnMessage != nil { 83 | err = rx.RxCallbacks.OnMessage(rx, lr) 84 | } else { 85 | // No callback to handle message, we discard data. 86 | var discard [256]byte 87 | for err == nil { 88 | _, err = lr.Read(discard[:]) 89 | } 90 | if err == io.EOF { 91 | err = nil 92 | } 93 | } 94 | if err == nil && lr.N != 0 { 95 | err = errors.New("callback did not consume all bytes in frame") 96 | } 97 | if err != nil { 98 | rx.handleErr(err) 99 | } 100 | return n + int(h.PayloadLength-uint64(lr.N)), err 101 | } 102 | 103 | // handleErr is a convenience wrapper for RxCallbacks.OnError. If RxCallbacks.OnError 104 | // is not defined then handleErr just closes the connection. 105 | func (rx *Rx) handleErr(err error) { 106 | if rx.RxCallbacks.OnError != nil { 107 | rx.RxCallbacks.OnError(rx, err) 108 | return 109 | } 110 | rx.trp.Close() 111 | } 112 | 113 | // TxBuffered handles the marshalling of frames over a underlying transport. 114 | type TxBuffered struct { 115 | buf bytes.Buffer 116 | trp io.WriteCloser 117 | TxCallbacks TxCallbacks 118 | } 119 | 120 | // NewTxBuffered creates a new TxBuffered ready for use. 121 | func NewTxBuffered(wc io.WriteCloser) *TxBuffered { 122 | return &TxBuffered{trp: wc} 123 | } 124 | 125 | // SetTxTransport sets the underlying TxBuffered transport writer. 126 | func (tx *TxBuffered) SetTxTransport(wc io.WriteCloser) { 127 | tx.trp = wc 128 | } 129 | 130 | // TxCallbacks stores functions to be called on events during marshalling of websocket frames. 131 | type TxCallbacks struct { 132 | OnError func(tx *TxBuffered, err error) 133 | } 134 | 135 | // WriteTextMessage writes an UTF-8 encoded message over the transport. 136 | // This implementation does not check if payload is valid UTF-8. This can be done 137 | // with the unicode/utf8 Valid function. 138 | // payload's bytes will be masked and thus mutated if mask is non-zero. 139 | func (tx *TxBuffered) WriteTextMessage(mask uint32, payload []byte) (int, error) { 140 | return tx.writeMessage(mask, payload, true) 141 | } 142 | 143 | // WriteTextMessage writes an arbitrary message over the transport. 144 | // payload's bytes will be masked and thus mutated if mask is non-zero. 145 | func (tx *TxBuffered) WriteMessage(mask uint32, payload []byte) (int, error) { 146 | return tx.writeMessage(mask, payload, false) 147 | } 148 | 149 | func (tx *TxBuffered) writeMessage(mask uint32, payload []byte, isUTF8 bool) (int, error) { 150 | frameType := FrameBinary 151 | if isUTF8 { 152 | frameType = FrameText 153 | } 154 | tx.buf.Reset() 155 | h := newHeader(frameType, uint64(len(payload)), mask, true) 156 | _, err := h.Encode(&tx.buf) 157 | if err != nil { 158 | return 0, err 159 | } 160 | if h.IsMasked() { 161 | _ = maskWS(mask, payload) 162 | } 163 | _, err = tx.buf.Write(payload) 164 | if err != nil { 165 | return 0, err 166 | } 167 | n, err := tx.buf.WriteTo(tx.trp) 168 | if err != nil && n > 0 { 169 | tx.handleError(err) 170 | } 171 | return int(n), err 172 | } 173 | 174 | // WriteFragmentedMessage writes a fragmented message over websocket without allocating 175 | // memory. It reads the application message from payload and masks it with mask. 176 | func (tx *TxBuffered) WriteFragmentedMessage(mask uint32, payload io.Reader, userBuffer []byte) (int, error) { 177 | if userBuffer == nil { 178 | userBuffer = make([]byte, 1024) 179 | } 180 | if len(userBuffer) < 12 { 181 | return 0, errors.New("buffer too small to write messages") 182 | } 183 | buflen := len(userBuffer) 184 | masked := mask != 0 185 | hSize := headerSizeFromTransportSize(buflen, masked) 186 | maxMessageSize := uint64(buflen - hSize) 187 | 188 | h := newHeader(FrameBinary, maxMessageSize, mask, false) 189 | 190 | isEOF := false 191 | var n int 192 | for !isEOF { 193 | h.Put(userBuffer) 194 | if n == 0 { 195 | h = newHeader(FrameContinuation, maxMessageSize, mask, false) 196 | } 197 | var nBuf int 198 | for !isEOF && hSize+nBuf < len(userBuffer) { 199 | ngot, err := payload.Read(userBuffer[hSize+nBuf:]) 200 | nBuf += ngot 201 | isEOF = errors.Is(err, io.EOF) 202 | if err != nil && !isEOF { 203 | if n > 0 { 204 | tx.handleError(err) 205 | } 206 | return n, err 207 | } 208 | } 209 | 210 | if nBuf != int(maxMessageSize) { 211 | h.PayloadLength = uint64(nBuf) 212 | newHsize := headerSizeFromMessageSize(nBuf, masked) 213 | // newHsize will always be lesser than hSize. 214 | if newHsize != hSize { 215 | for i := 0; i < nBuf; i++ { 216 | userBuffer[newHsize+i] = userBuffer[hSize+i] 217 | } 218 | hSize = newHsize 219 | } 220 | h = newHeader(FrameContinuation, uint64(nBuf), mask, true) 221 | h.Put(userBuffer) 222 | } 223 | maskWS(mask, userBuffer[hSize:hSize+nBuf]) 224 | ngot, err := tx.trp.Write(userBuffer[:hSize+nBuf]) 225 | n += ngot 226 | if err != nil { 227 | if n > 0 { 228 | tx.handleError(err) 229 | } 230 | return n, err 231 | } 232 | } 233 | return n, nil 234 | } 235 | 236 | func headerSizeFromMessageSize(mSize int, masked bool) (hSize int) { 237 | maskSize := 0 238 | if masked { 239 | maskSize = 4 240 | } 241 | switch { 242 | case mSize < 126: 243 | // Smallest case, 2 byte header for application message under 126 bytes. 244 | hSize = 2 + maskSize 245 | case mSize <= math.MaxUint16: 246 | // 2 bytes same as last case + 16bit payload length 247 | hSize = (2 + 2) + maskSize 248 | default: 249 | hSize = (2 + 8) + maskSize 250 | } 251 | return hSize 252 | } 253 | 254 | func headerSizeFromTransportSize(tSize int, masked bool) (hSize int) { 255 | maskSize := 0 256 | if masked { 257 | maskSize = 4 258 | } 259 | switch { 260 | case tSize < 128-maskSize: 261 | // Smallest case, 2 byte header for application message under 126 bytes. 262 | hSize = 2 + maskSize 263 | case tSize < math.MaxUint16-maskSize: 264 | // 2 bytes same as last case + 16bit payload length 265 | hSize = (2 + 2) + maskSize 266 | default: 267 | hSize = (2 + 8) + maskSize 268 | } 269 | return hSize 270 | } 271 | 272 | func (tx *TxBuffered) handleError(err error) { 273 | if tx.TxCallbacks.OnError != nil { 274 | tx.TxCallbacks.OnError(tx, err) 275 | return 276 | } 277 | tx.trp.Close() 278 | } 279 | 280 | // WriteClose writes a Close frame over the websocket connection. reason will be mutated 281 | // if mask is non-zero. 282 | func (tx *TxBuffered) WriteClose(mask uint32, status StatusCode, reason []byte) (int, error) { 283 | if len(reason) > maxCloseReason { 284 | return 0, errors.New("close reason too long. must be under 123 bytes") 285 | } 286 | tx.buf.Reset() 287 | pl := uint64(2 + len(reason)) 288 | h := newHeader(FrameClose, pl, mask, true) 289 | _, err := h.Encode(&tx.buf) 290 | if err != nil { 291 | return 0, err 292 | } 293 | var codebuf [2]byte 294 | binary.BigEndian.PutUint16(codebuf[:], uint16(status)) 295 | if h.IsMasked() { 296 | mask = maskWS(mask, codebuf[:]) 297 | } 298 | _, err = tx.buf.Write(codebuf[:2]) 299 | if err != nil { 300 | return 0, err 301 | } 302 | if h.IsMasked() { 303 | _ = maskWS(mask, reason) 304 | } 305 | _, err = writeFull(&tx.buf, reason) 306 | if err != nil { 307 | return 0, err 308 | } 309 | n, err := tx.buf.WriteTo(tx.trp) 310 | if err != nil && n > 0 { 311 | tx.handleError(err) 312 | } 313 | return int(n), err 314 | } 315 | 316 | // WritePong writes a pong message over the Tx. 317 | func (tx *TxBuffered) WritePong(mask uint32, message []byte) (int, error) { 318 | if len(message) > MaxControlPayload { 319 | return 0, errors.New("pong message too long, must be under 125 bytes") 320 | } 321 | return tx.writepingpong(FramePong, mask, message) 322 | } 323 | 324 | // WritePing writes a ping message over the Tx. 325 | func (tx *TxBuffered) WritePing(mask uint32, message []byte) (int, error) { 326 | if len(message) > MaxControlPayload { 327 | return 0, errors.New("ping message too long, must be under 125 bytes") 328 | } 329 | return tx.writepingpong(FramePing, mask, message) 330 | } 331 | 332 | func (tx *TxBuffered) writepingpong(frame FrameType, mask uint32, message []byte) (int, error) { 333 | tx.buf.Reset() 334 | pl := uint64(len(message)) 335 | h := newHeader(frame, pl, mask, true) 336 | _, err := h.Encode(&tx.buf) 337 | if err != nil { 338 | return 0, err 339 | } 340 | if h.IsMasked() { 341 | _ = maskWS(mask, message) 342 | } 343 | _, err = writeFull(&tx.buf, message) 344 | if err != nil { 345 | return 0, err 346 | } 347 | n, err := tx.buf.WriteTo(tx.trp) 348 | if err != nil && n > 0 { 349 | tx.handleError(err) 350 | } 351 | return int(n), err 352 | } 353 | 354 | // newMaskedReader creates a new MaskedReader ready for use. 355 | func newMaskedReader(r io.Reader, maskKey uint32) *maskedReader { 356 | return &maskedReader{R: r, MaskKey: maskKey} 357 | } 358 | 359 | // maskedReader applies the [WebSocket masking algorithm] 360 | // to the underlying reader. 361 | // 362 | // [WebSocket masking algorithm]: https://tools.ietf.org/html/rfc6455#section-5.3 363 | type maskedReader struct { 364 | R io.Reader 365 | MaskKey uint32 366 | } 367 | 368 | // Read implements the [io.Reader] interface for the websocket masking algorithm. 369 | func (mr *maskedReader) Read(b []byte) (int, error) { 370 | lenRead, err := mr.R.Read(b) 371 | if lenRead == 0 { 372 | return 0, err 373 | } 374 | mr.MaskKey = maskWS(mr.MaskKey, b[:lenRead]) 375 | return lenRead, nil 376 | } 377 | 378 | func maskWS(key uint32, b []byte) uint32 { 379 | if key == 0 { 380 | return 0 381 | } 382 | for len(b) >= 4 { 383 | v := binary.BigEndian.Uint32(b) 384 | binary.BigEndian.PutUint32(b, v^key) 385 | b = b[4:] 386 | } 387 | 388 | if len(b) != 0 { 389 | // xor remaining bytes and shift mask. 390 | for i := range b { 391 | b[i] ^= byte(key >> 24) 392 | key = bits.RotateLeft32(key, 8) 393 | } 394 | } 395 | return key 396 | } 397 | 398 | // writeFull is a convenience function to perform dst.Write and detect 399 | // bad writer implementations while at it. 400 | func writeFull(dst io.Writer, src []byte) (int, error) { 401 | n, err := dst.Write(src) 402 | if n != len(src) && err == nil { 403 | err = fmt.Errorf("bad writer implementation %T", dst) 404 | } 405 | return n, err 406 | } 407 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | //go:build !tinygo 2 | 3 | package peasocket 4 | 5 | import ( 6 | "bufio" 7 | "bytes" 8 | "context" 9 | "errors" 10 | "fmt" 11 | "io" 12 | "log" 13 | "net" 14 | "net/http" 15 | "net/netip" 16 | "net/url" 17 | "strings" 18 | "sync" 19 | "time" 20 | ) 21 | 22 | var serverMaxBackoff = 500 * time.Millisecond 23 | 24 | // Server is a stateful websocket single-connection server implementation. 25 | // It is only partly safe for concurrent use. 26 | // 27 | // Users should use Server in no more than two goroutines unless calling the 28 | // explicitly stated as concurrency safe methods i.e: Err, IsConnected. 29 | // These two goroutines should be separated by responsability: The Read goroutine 30 | // should call methods that read received messages and the Write goroutine should 31 | // call methods that write to the connection. 32 | // 33 | // Server automatically takes care of incoming Ping and Close frames during ReadNextFrame 34 | type Server struct { 35 | // Tx mutex. 36 | muTx sync.Mutex 37 | tx TxBuffered 38 | rx Rx 39 | state connState 40 | } 41 | 42 | // NewClient creates a new Client ready to connect to a websocket enabled server. 43 | // serverURL can start with wss:// or ws:// to enable TLS (secure websocket protocol). 44 | // entropy is a function that returns randomized 32 bit integers from a high-entropy source. 45 | // If entropy is nil then a default will be used. 46 | func NewServer(rxCopyBuf []byte) *Server { 47 | if len(rxCopyBuf) == 0 { 48 | rxCopyBuf = make([]byte, 32*1024) 49 | } 50 | L := len(rxCopyBuf) 51 | if rxCopyBuf == nil { 52 | rxCopyBuf = make([]byte, 32*1024) 53 | } else if L < MaxControlPayload*3 { 54 | panic("user buffer length too short, must be at least 3*125") 55 | } 56 | ctl1 := rxCopyBuf[0:0:MaxControlPayload] 57 | ctl2 := rxCopyBuf[MaxControlPayload : MaxControlPayload : 2*MaxControlPayload] 58 | sv := &Server{ 59 | state: connState{ 60 | pendingPingOrClose: ctl1, 61 | expectedPong: ctl2, 62 | closeErr: errServerGracefulClose, 63 | copyBuf: rxCopyBuf[2*MaxControlPayload:], 64 | }, 65 | } 66 | sv.rx.RxCallbacks, sv.tx.TxCallbacks = sv.state.callbacks(false) 67 | return sv 68 | } 69 | 70 | // HandleNextFrame reads next frame in connection and takes care of 71 | // control frame actions. Should be called in a loop separate from write goroutines. 72 | func (sv *Server) HandleNextFrame() error { 73 | if !sv.IsConnected() { 74 | return sv.Err() 75 | } 76 | _, err := sv.rx.ReadNextFrame() 77 | if err != nil { 78 | return err 79 | } 80 | sv.muTx.Lock() 81 | err = sv.state.ReplyOutstandingFrames(0, &sv.tx) 82 | sv.muTx.Unlock() 83 | if err != nil { 84 | sv.CloseConn(err) 85 | return err 86 | } 87 | return nil 88 | } 89 | 90 | // ListenAndServe is the websocket counterpart to http.ListenAndServe. It sets up a 91 | // tcp.Listener on the argument address and begins listening for incoming websocket 92 | // client connections. On a succesful webosocket connection the handler is called 93 | // with a newly allocated Server instance. 94 | // 95 | // TODO(soypat): Add a Listener type for vetting HTTP headers and whatnot. ListenAndServe should then use a default Listener. 96 | func ListenAndServe(mainCtx context.Context, address string, handler func(ctx context.Context, sv *Server)) error { 97 | var cancel func() 98 | mainCtx, cancel = context.WithCancel(mainCtx) 99 | defer cancel() 100 | addrport, err := netip.ParseAddrPort(address) 101 | if err != nil { 102 | return err 103 | } 104 | listener, err := net.ListenTCP("tcp", net.TCPAddrFromAddrPort(addrport)) 105 | if err != nil { 106 | return err 107 | } 108 | defer listener.Close() 109 | if dl, ok := mainCtx.Deadline(); ok { 110 | err = listener.SetDeadline(dl) 111 | if err != nil { 112 | return err 113 | } 114 | } 115 | var sendBuf bytes.Buffer 116 | for { 117 | if err := mainCtx.Err(); err != nil { 118 | return err 119 | } 120 | conn, err := listener.AcceptTCP() 121 | if err != nil { 122 | return err 123 | } 124 | req, err := http.ReadRequest(bufio.NewReader(conn)) 125 | if err != nil { 126 | conn.Close() 127 | continue 128 | } 129 | resp := http.Response{ 130 | Status: http.StatusText(http.StatusSwitchingProtocols), 131 | StatusCode: http.StatusSwitchingProtocols, 132 | Proto: httpProto, 133 | ProtoMajor: httpProtoMajor, 134 | ProtoMinor: httpProtoMinor, 135 | Request: req, 136 | } 137 | challengeAccept, sc, err := validateClientUpgrade(req) 138 | resp.StatusCode = sc 139 | if err != nil { 140 | resp.Status = http.StatusText(sc) 141 | resp.Write(conn) 142 | conn.Close() 143 | continue 144 | } 145 | resp.Header, _ = makeWSHeader(nil) 146 | resp.Header["Sec-WebSocket-Accept"] = []string{challengeAccept} 147 | resp.Write(&sendBuf) 148 | _, err = sendBuf.WriteTo(conn) 149 | sendBuf.Reset() 150 | if err != nil { 151 | conn.Close() 152 | continue 153 | } 154 | go func() { 155 | ctx, cancel := context.WithCancel(mainCtx) 156 | defer cancel() 157 | sv := NewServer(make([]byte, 1024)) 158 | sv.rx.SetRxTransport(conn) 159 | sv.tx.SetTxTransport(conn) 160 | sv.state.OnConnect() 161 | defer conn.Close() 162 | log.Println("start handler") 163 | handler(ctx, sv) 164 | log.Println("end handler") 165 | }() 166 | } 167 | } 168 | 169 | // Ping blocks until a call to ReadNextFrame receives the corresponding Pong message. 170 | // Should be called in a goroutine separate to the Server Read goroutine. 171 | func (sv *Server) Ping(ctx context.Context, message []byte) error { 172 | if !sv.IsConnected() { 173 | return sv.Err() 174 | } 175 | sv.muTx.Lock() 176 | 177 | err := sv.state.ReplyOutstandingFrames(0, &sv.tx) 178 | if err != nil { 179 | sv.muTx.Unlock() 180 | return err 181 | } 182 | sv.state.mu.Lock() 183 | sv.state.expectedPong = message 184 | sv.state.mu.Unlock() 185 | _, err = sv.tx.WritePing(0, message) 186 | sv.muTx.Unlock() 187 | if err != nil { 188 | return err 189 | } 190 | backoff := ExponentialBackoff{MaxWait: serverMaxBackoff} 191 | for { 192 | if err := ctx.Err(); err != nil { 193 | return err 194 | } 195 | sv.state.mu.Lock() 196 | if sv.state.expectedPong == nil { 197 | sv.state.mu.Unlock() 198 | break 199 | } 200 | sv.state.mu.Unlock() 201 | backoff.Miss() 202 | } 203 | return nil 204 | } 205 | 206 | // Err returns the error that closed the connection. 207 | // It only returns an error while the connection is closed. 208 | // One can test if the client gracefully closed the connection with 209 | // 210 | // errors.As(err, &peasocket.CloseErr{}) // true on graceful websocket closure 211 | // 212 | // Err is safe for concurrent use. 213 | func (sv *Server) Err() error { 214 | sv.state.mu.Lock() 215 | defer sv.state.mu.Unlock() 216 | if sv.state.closeErr == errClientGracefulClose { 217 | return fmt.Errorf("client closed websocket: %w", sv.state.makeCloseErr()) 218 | } 219 | return sv.state.closeErr 220 | } 221 | 222 | // CloseConn closes the underlying transport without sending websocket frames. 223 | // 224 | // If err is nil CloseConn will panic. 225 | func (sv *Server) CloseConn(err error) error { 226 | w := sv.tx.trp 227 | r := sv.rx.trp 228 | if w != nil && r != nil { 229 | sv.state.CloseConn(err) 230 | w.Close() 231 | r.Close() 232 | return nil 233 | } 234 | return errors.New("transport(s) nil, will not attempt to close. this may be a bug in peasocket") 235 | } 236 | 237 | // IsConnected returns true if the underlying transport of the websocket is ready to send/receive messages. 238 | // It is shorthand for s.Err() == nil. 239 | // 240 | // IsConnected is safe for concurrent use. 241 | func (sv *Server) IsConnected() bool { return sv.state.IsConnected() } 242 | 243 | // CloseWebsocket sends a close control frame over the websocket connection. Does 244 | // not attempt to close the underlying transport. 245 | // If CloseWebsocket receives a [*CloseErr] type no memory allocation is performed and it's 246 | // status code and reason is used in the websocket close frame. 247 | // 248 | // If err is nil CloseWebsocket panics. 249 | func (sv *Server) CloseWebsocket(err error) error { 250 | sv.muTx.Lock() 251 | defer sv.muTx.Unlock() 252 | return sv.state.CloseWebsocket(err, 0, &sv.tx) 253 | } 254 | 255 | // WriteNextMessageTo writes entire contents the next message in queue 256 | // to the passed in writer. 257 | func (sv *Server) WriteNextMessageTo(w io.Writer) (FrameType, int64, error) { 258 | return sv.state.WriteNextMessageTo(w) 259 | } 260 | 261 | // WriteFragmentedMessage writes contents of r over the wire using the userBuffer 262 | // as scratch memory. This function does not allocate memory unless userBuffer is nil. 263 | func (c *Server) WriteFragmentedMessage(r io.Reader, userBuffer []byte) (int, error) { 264 | c.muTx.Lock() 265 | defer c.muTx.Unlock() 266 | err := c.state.ReplyOutstandingFrames(0, &c.tx) 267 | if err != nil { 268 | c.CloseConn(err) 269 | return 0, err 270 | } 271 | return c.tx.WriteFragmentedMessage(0, r, userBuffer) 272 | } 273 | 274 | func validateClientUpgrade(r *http.Request) (string, int, error) { 275 | challenge := r.Header.Get("Sec-Websocket-Key") 276 | if challenge == "" || !strings.EqualFold(r.Header.Get("Connection"), "upgrade") || 277 | !strings.EqualFold(r.Header.Get("Upgrade"), "websocket") || 278 | r.Header.Get("Sec-Websocket-Version") != "13" { 279 | return "", http.StatusBadRequest, fmt.Errorf(`invalid header field(s) "Sec-Websocket-Version:13" "Connection:Upgrade", "Upgrade:websocket" or "Sec-WebSocket-Accept":, got %v`, r.Header) 280 | } 281 | if !checkSameOrigin(r) { 282 | return "", http.StatusBadRequest, fmt.Errorf("request origin denied: Host=%q, Origin=%q", r.Host, r.Header.Get("Origin")) 283 | } 284 | return serverProofOfReceipt(challenge), http.StatusSwitchingProtocols, nil 285 | } 286 | 287 | func checkSameOrigin(r *http.Request) bool { 288 | origin := r.Header["Origin"] 289 | if len(origin) == 0 { 290 | return true 291 | } 292 | u, err := url.Parse(origin[0]) 293 | if err != nil { 294 | return false 295 | } 296 | return strings.EqualFold(u.Host, r.Host) 297 | } 298 | 299 | // ENABLE_DEBUG_LOG will NOT stay in this package. It is a temporary 300 | // helper that is why the name is the way it is. Calling this function with enable 301 | // set to true without a enable=false call in between will cause duplicated log output. 302 | // 303 | // Deprecated: ENABLE_DEBUG_LOG is deprecated. Will not stay in package peasocket. 304 | func (sv *Server) ENABLE_DEBUG_LOG(enable bool) { 305 | sv.state.mu.Lock() 306 | defer sv.state.mu.Unlock() 307 | if enable { 308 | txOnError := sv.tx.TxCallbacks.OnError 309 | sv.tx.TxCallbacks.OnError = func(tx *TxBuffered, err error) { 310 | log.Printf("Tx.OnError(%s): %q", err, tx.buf.String()) 311 | txOnError(tx, err) 312 | } 313 | rxOnError := sv.rx.RxCallbacks.OnError 314 | sv.rx.RxCallbacks.OnError = func(rx *Rx, err error) { 315 | log.Printf("Rx.OnError %s: %s", rx.LastReceivedHeader.String(), err) 316 | rxOnError(rx, err) 317 | } 318 | rxOnCtl := sv.rx.RxCallbacks.OnCtl 319 | sv.rx.RxCallbacks.OnCtl = func(rx *Rx, payload io.Reader) error { 320 | b, _ := io.ReadAll(payload) 321 | log.Printf("Rx.OnCtl:%s: %q", rx.LastReceivedHeader.String(), string(b)) 322 | return rxOnCtl(rx, bytes.NewReader(b)) 323 | } 324 | rxOnMsg := sv.rx.RxCallbacks.OnMessage 325 | sv.rx.RxCallbacks.OnMessage = func(rx *Rx, message io.Reader) error { 326 | b, _ := io.ReadAll(message) 327 | log.Printf("Rx.OnMsg:%s: %q", rx.LastReceivedHeader.String(), string(b)) 328 | return rxOnMsg(rx, bytes.NewReader(b)) 329 | } 330 | } else { 331 | sv.rx.RxCallbacks, sv.tx.TxCallbacks = sv.state.callbacks(false) 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzHeaderPut/b2765df01f4a03cb21f126e13e18a74ed901c7c82b7a7645a724da1a2b4ec802: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | uint64(65535) 3 | uint32(0) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzLoopbackMessage/14a8f53cd1c49cd79638f60a021ee0e11ac49e1c0de48f43c10f067568e33cc8: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("") 3 | uint32(2721129657) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzLoopbackMessage/155f0d559e3d33f5c42bae1277a54533c4996749e08fa5235653d0b1edb985b4: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("000000000000000000000000zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9ezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz?{\x91\xad\x99\x93\xf2zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz00000000000000000000000000000000000") 3 | uint32(2721129418) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzLoopbackMessage/17f3c85e957f6c4b35570599663243be829bfab454c287c06b64bfad894e5538: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("0000") 3 | uint32(2721129524) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzLoopbackMessage/1b299a9f6b4f4d019c6a90069665defa517cb85ac1da55b2e2bad6d3510b292a: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") 3 | uint32(2721129524) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzLoopbackMessage/27d4f8f0ff89c5d6b57db16ca04f365ff6da8b338110b5e9f7b5bdce3d54771e: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") 3 | uint32(2721129448) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzLoopbackMessage/924ffc0d6b15450b86db1dbe79aabebcd675bbafd085f8a6cdd75ad80677eae6: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("00") 3 | uint32(2721129524) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzLoopbackMessage/9c085f9dc8bedcdbd466602263fb0baf5922dc8050a80990498d0adf3818fccf: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xb7\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb0\x7f0\x00") 3 | uint32(2721129524) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzLoopbackMessage/ab1429a42543219c19740e8c2466c22b36046a8ad68e62e317e2f8dfda6d2898: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb0\x7f0\x00") 3 | uint32(2721129524) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzLoopbackMessage/abf5cb1ad7067774508b10e704279e619b2b82d3997ba8c28c545e784faf8521: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") 3 | uint32(2721129524) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzLoopbackMessage/d27453fd81a736957a99207b2c9a109a026d52f2f18cc3b144bf0bdc094729fc: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("00000000000000000000000000000000000000000000000000000000000") 3 | uint32(2721129524) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzLoopbackMessage/dc727647f156913f63b8727728edba8f03ded1337c99fb34a187d8686f3c9a2f: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbbh\xe3\x00w4\\\x961\xa7\xe7Sp;E\xb1N\xe30\x00\xfe\xc1\xb1 uU3\x1fR\x98\x1b\xacR\xd7\n\x9a\x1dᙏKڝ<\x9d̐\xfe\x94\xb1Z\xf3ږ\xe4\xfb\xe0\xf2\x92?\xd8_q\x15\xd7\".\xc8\xf4\xf8\x0e\f9Yt<\b?\xc9Q\x96\xe06\xc9W\x18\xac}\x7fM\x96K\t@>A\x8f\xc32\xce\x17)6\x18\x11\xbe\xf1\x89^\fv\xa2\x86@Fs\xddP;\x1c?\x80\t:\x86^`Sa@U\x8bL+\xf2\x7f\xe4\x9b\xf9\x1b\xa0\xbb\xe8\xe0ƶ\x85\xb0\xa9k\xfa\x827\x8c]O\x97LI\x8d\x9a\xac)\x90$x&\xe43\xe5ɋ\xc9xZ\x9b\xafP\x89\xa1\xd8aӳ~\xe3D\xf4\xe3\xb5\x19\xd2\xf9\xfd\x92\x00\xeb\x97\xf1\xb3\xb8s\xfe\xaav\xe0;\xcf\xf13\xbaP\xbe`x\x17\x90-\xc9\xe7+ܦYdZw\x81z\xecA3VT\xad\xf5\xdc\xf8\xff)xnx\xa1h\xd6)\xb9\xc10\xef4\xfa\x17Z;\x01V\x17\xd0Zf@.\t\x10\x18E\xbb\x03yL\xac\xd1\xc8P\x80ȁB\x94\x13\xfc\xd9K=\x1bP\x0ep\xa5\xc8t\x87\x84\x80\x87\xd73\x057\xe5\xc41\xbbҬ\x00i8\xaei\n\x87AI\xab\x98\x9e-4\x8c\xd4\xcd_\x1bl\xbd52X7\xbde\b\xa1\xd3:\x91\xcc\x0e\xdaYo\n\xf9\xae\xd9\xe8\xf9e\f\x95\xec\xa5\xe7\xe2\x00\x163\x8cFp\xe8G\xd4\x19\xbe\x10*0~MQ\xa07\x1c\x1d\x83\v\fu\xa9\x83\xac\x80!@\x8eN\xcd,\x05c\x9c\x9c\x94\xf6\xe0\x9a\xad\xce\xe9\xf5'f\xd9\xd6D\xf5\xb3i\xac\t\xd66S\x18R\xcaN\xcc\xca}\x14\xb4\xb4\xff\xa5\xe2p\x1f\xb1\n\xfe\x01\xb8\xa3;jy\xf3;3\xb1u\xcec\fnHT\x15\x99\x81ڭ\xb5\x80\xe9x\x814\x8d\xad\x016w\x96\x9e\xec\x9b\xe0fbC\x14\xdb\xd2\x04\xba\xe0\x01\xa1\xff\x16\xfcb\xf3\xef\xa6L\xbb<2\xb3:y\xe6\x9c\x15\xe27\x88i\x1eׇ\xc6\xed{*\xa6v\xe8\x00G\x8f\"\xa46狢\xb1YY\xb5\xaaҿ\xe4\x15\x85\xbf\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb\xbb0\x7f0\x00") 3 | uint32(2721129420) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzLoopbackMessage/e6038814489048ad2b1b728d50426df7295c3b44fee352381170f0c86c170ea2: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") 3 | uint32(2721129621) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzLoopbackMessage/eb6ab80a9fb4e25043fcb6a82519eb91d26de7f6d68293613db5e349cf4b7040: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("0000000000000000000000000000000000000000000000000000000000000000") 3 | uint32(2721129364) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzLoopbackMessage/ecb1cdba517884931fb7343b0750e80cd7b9364017f9d17fe01fe09dc959a95a: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") 3 | uint32(2721129524) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzLoopbackMessage/edb9274bfc2b4ebf72faaead9b2ddc26bb968e3fa351b3f5b5c9a0e2a7c64379: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("0000000000000000") 3 | uint32(2721129524) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzLoopbackMessage/f831c183d547a4e6be45719e65e5cabd766249d621658eb8871bccf92b1b8e21: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") 3 | uint32(2721129524) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzLoopbackMessage/ff4ce0b8881795c20eceaa2d8480a178f76e8142e1cd12476982251e4f589eb7: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("00000000000000000000000000000000") 3 | uint32(2721129524) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzMaskedReader/0f2affb229265eb4afa688fd7e25aae8b2c03a961d4c3e61b6d5077d37bd531e: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("ad\x00\vSdsd\x00\vSd\xff\xf0") 3 | uint32(2721129524) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzMaskedReader/18d2fe6710c21b408997c6f1c52d09292d431783c4a97d54f0686a11a25a4ad5: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") 3 | uint32(2721129541) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzMaskedReader/203eea51a24b89a1c97308156610510320d4dd6f0ef6f0bf5aaa90813dae5384: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") 3 | uint32(2721129547) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzMaskedReader/2a6fd3e2276ea5aa077f7853f29216e96e49e62830eb8cd29220d7270120579f: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("a\xb1\xac\xff\xf0") 3 | uint32(2721129472) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzMaskedReader/569f534b9c22bd19dbb7d799b0fb6ee510267eeedcf797ce87114adaa62e9aa8: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp0000000000") 3 | uint32(2721129658) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzMaskedReader/7425e8ee2a7930e563490b1f1a19d4b8a75bfd19c1d7e243f27148d0b5d5d4d3: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("") 3 | uint32(2721129618) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzMaskedReader/7cc689243f4c2ffac7db6c451ab2083223a0f28ce276dd0fd38fd6672446b9cf: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0e\x0eppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp0000000000") 3 | uint32(2721129737) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzMaskedReader/85b51d69d1758cd1c9a445ba47e87ff74963ac0cb5874e527c07dd41d2604453: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("000000") 3 | uint32(2721129570) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzMaskedReader/8cb0c14d08a3825449162ec6b092fbc6d08785789950ec18341695ff93026f29: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("asQ\xd8[Ҏz\xae\x8ad\x00ASd\xff\xf0") 3 | uint32(2721129543) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzMaskedReader/a6f3e00b21b5e318f80252bf405eb454baaa5b29214245494840cc30e7537ff3: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("000000000000000000000000000000000") 3 | uint32(2721129649) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzMaskedReader/ab2df5171d46de906d671984311116cdfee3dcbf679afdfc4478ca70e9f4706d: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") 3 | uint32(2721129570) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzMaskedReader/b84716b1fb55f4cca2203dba199075af0e0d3cc0ca16a121dfeba281e40fc90d: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("0") 3 | uint32(2721129524) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzMaskedReader/be2c380a8a1b73cb4bd3658a7ceb1db3b35bc03bdf3785ac13d588a0524e95ca: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("asQض\xb6\xb6\xb6\xb6[Ҏz\xae\x8ad\x00ASd\xff\xf0") 3 | uint32(2721129620) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzMaskedReader/dedf4276541e3fcd41241b3fd5839c358813b2b76cffb555e2cf363937c3eecf: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") 3 | uint32(2721129677) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzMaskedReader/eb62e94b5e3228aea5eba9b501d09ecb7f33c4b4e8db72de6179acd6cb6828ca: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") 3 | uint32(2721129670) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzMaskedReader/fbec51edd6b5e0fcf91210cdd431787756f0f980b7689e91fa6631e3a58d2799: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("00000000000000000000000000000000000000000000000000000000000000000") 3 | uint32(2721129529) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzMaskedReader/fbf0155d37efbbf3ec751fe097ec522dba8927ff24c73ef3db8cb601a8344655: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("asd\x00ASd\xb1\xb1\xff\xf0") 3 | uint32(2721129524) 4 | -------------------------------------------------------------------------------- /testdata/fuzz/FuzzMaskedReader/ff55d8a64672b1f6feb847e9d0e4a26bc37583c35b0b5c8a6bb6c02ed17aa8c6: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | []byte("000") 3 | uint32(2721129524) 4 | --------------------------------------------------------------------------------