Compare commits

...

20 commits

Author SHA1 Message Date
Simon Bruder 327235c451
be less silly next time (hard coded version number where it is dynamic)
All checks were successful
continuous-integration/drone/push Build is passing
2019-06-23 00:15:18 +00:00
Simon Bruder d63397b1ed
make docker image much smaller
Some checks reported errors
continuous-integration/drone/push Build was killed
2019-06-02 09:05:11 +00:00
Simon Bruder 1da5439358 add drone config
All checks were successful
continuous-integration/drone/push Build is passing
2019-03-25 16:25:41 +01:00
Simon Bruder 58c9465add always use latest alpine version 2019-03-25 16:24:07 +01:00
Matthieu Grieger dff929ddc9 Update README.md 2017-05-20 18:04:02 -07:00
Matthieu Grieger 138c1008eb Fixed YouTube playback offsets 2016-11-05 21:30:42 -07:00
Matthieu Grieger 2f6bda5018 Update version number and changelog 2016-11-05 19:41:58 -07:00
Matthieu Grieger a1c5399223 https://github.com/matthieugrieger/mumbledj/issues/182: Added back track/playlist submitter immediate skipping 2016-11-05 19:39:31 -07:00
Matthieu Grieger e1e3a334cd Fix https://github.com/matthieugrieger/mumbledj/issues/180: Panic on playlist with private video 2016-11-05 19:22:58 -07:00
Matthieu Grieger 51db9c3061 Fix https://github.com/matthieugrieger/mumbledj/issues/176: Empty IDs for SoundCloud tracks 2016-08-22 20:20:41 -07:00
Matthieu Grieger 9222608962 Fixed https://github.com/matthieugrieger/mumbledj/issues/174: Fixed deadlock during track skip/finish 2016-08-21 17:58:11 -07:00
Matthieu Grieger 466e9189c6 Fix https://github.com/matthieugrieger/mumbledj/issues/172: Index out of range error during skip 2016-08-14 10:00:38 -07:00
Matthieu Grieger 786ab8c3d6 Small stylistic and spelling changes 2016-07-11 16:08:16 -07:00
Matthieu Grieger f918d2397d Updated vendored dependencies 2016-07-11 16:03:02 -07:00
Gabriel Plassard 66be67719a Docker (#170)
* added dockerfile

* cleanup dockerfile

* update Dockerfile and create raspberry dockerfile

* doc & aria2
2016-07-11 15:57:47 -07:00
Matthieu Grieger 0a4d0aead1 Implemented register command 2016-07-10 21:09:54 -07:00
Matthieu Grieger 32167c1294 p12 files can now be provided to the bot to authenticate as a registered user 2016-07-10 20:41:51 -07:00
Matthieu Grieger 5434077d73 Remove crypto/pkcs12 2016-07-10 20:15:15 -07:00
Matthieu Grieger 1e174fde46 Update dependencies, add crypto/pkcs12 2016-07-04 10:06:28 -07:00
Matthieu Grieger 3de4917972 Potential fix for PEM IP SANs issue 2016-07-01 18:34:43 -07:00
69 changed files with 1874 additions and 729 deletions

1
.dockerignore Normal file
View file

@ -0,0 +1 @@
Dockerfile

13
.drone.yml Normal file
View file

@ -0,0 +1,13 @@
kind: pipeline
name: default
steps:
- name: docker
image: plugins/docker
settings:
registry: r.sbruder.de
username:
from_secret: docker_username
password:
from_secret: docker_password
repo: r.sbruder.de/mumbledj

View file

@ -1,6 +1,33 @@
MumbleDJ Changelog MumbleDJ Changelog
================== ==================
### November 5, 2016 -- `v3.2.1`
* Fixed YouTube video offsets. Now YouTube URLs with `?t=<timestamp>` at the end will start the audio playback at the appropriate position.
### November 5, 2016 -- `v3.2.0`
* Fixed a Go panic that would occur when a YouTube playlist contained a private video.
* Added back immediate skipping for tracks/playlists that are skipped by the submitter. This was a feature that was present in the last major version of MumbleDJ but was forgotten when rewriting the bot (sorry!).
### August 22, 2016 -- `v3.1.4`
* Fixed a SoundCloud API response parsing issue that would result in empty IDs for tracks.
* Fixed the startup check for SoundCloud API.
### August 21, 2016 -- `v3.1.3`
* Fixed a deadlock that would occur during the transition from the first to second track in a queue.
### August 14, 2016 -- `v3.1.2`
* Fixed an index out of range crash in the queue skipping function.
### July 11, 2016 -- `v3.1.1`
* Updated vendored dependencies to hopefully address the following issue: https://github.com/matthieugrieger/mumbledj/issues/169.
### July 10, 2016 -- `v3.1.0`
* File path for user `p12` certificate can now be provided for authenticating as a registered user via the `--p12` commandline flag or the `connection.user_p12` configuration value.
* Added `!register` command for registering the bot on the server.
### July 1, 2016 -- `v3.0.11`
* Potential fix for an issue with IP SANs on PEM certs.
### June 29, 2016 -- `v3.0.10` ### June 29, 2016 -- `v3.0.10`
* Fixed issue related to PEM keys being overwritten by PEM certs. * Fixed issue related to PEM keys being overwritten by PEM certs.

39
Dockerfile Normal file
View file

@ -0,0 +1,39 @@
FROM golang:alpine as builder
RUN apk add --no-cache \
build-base \
opus-dev
COPY . /go/src/github.com/matthieugrieger/mumbledj
WORKDIR /go/src/github.com/matthieugrieger/mumbledj
RUN go get -v \
&& go build -v -ldflags="-s -w"
FROM alpine
RUN adduser -D mumbledj
RUN apk add --no-cache \
aria2 \
libressl \
python2
RUN wget -O /usr/bin/youtube-dl https://yt-dl.org/downloads/latest/youtube-dl \
&& chmod +x /usr/bin/youtube-dl
RUN wget https://johnvansickle.com/ffmpeg/builds/ffmpeg-git-amd64-static.tar.xz \
&& tar xvf ffmpeg-git-amd64-static.tar.xz '*/ffmpeg' || true \
&& mv ffmpeg-git-*-amd64-static/ffmpeg /usr/bin/ffmpeg \
&& rm -rf ffmpeg-git-* \
&& apk add --no-cache upx \
&& upx /usr/bin/ffmpeg \
&& apk del upx
COPY --from=builder /go/src/github.com/matthieugrieger/mumbledj/mumbledj /usr/bin/mumbledj
COPY config.yaml /home/mumbledj/.config/mumbledj/config.yaml
USER mumbledj
ENTRYPOINT ["/usr/bin/mumbledj"]

View file

@ -3,6 +3,8 @@
<p align="center"><b>A Mumble bot that plays audio fetched from various media websites.</b></p> <p align="center"><b>A Mumble bot that plays audio fetched from various media websites.</b></p>
<p align="center"><a href="https://travis-ci.org/matthieugrieger/mumbledj"><img src="https://travis-ci.org/matthieugrieger/mumbledj.svg?branch=master"/></a> <a href="https://raw.githubusercontent.com/matthieugrieger/mumbledj/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg"/></a> <a href="https://github.com/matthieugrieger/mumbledj/releases"><img src="https://img.shields.io/github/release/matthieugrieger/mumbledj.svg"/></a> <a href="https://goreportcard.com/report/github.com/matthieugrieger/mumbledj"><img src="https://goreportcard.com/badge/github.com/matthieugrieger/mumbledj"/></a> <a href="https://codecov.io/gh/matthieugrieger/mumbledj"><img src="https://img.shields.io/codecov/c/github/matthieugrieger/mumbledj.svg"/></a> <a href="https://gitter.im/matthieugrieger/mumbledj"><img src="https://img.shields.io/gitter/room/matthieugrieger/mumbledj.svg" /></a></p> <p align="center"><a href="https://travis-ci.org/matthieugrieger/mumbledj"><img src="https://travis-ci.org/matthieugrieger/mumbledj.svg?branch=master"/></a> <a href="https://raw.githubusercontent.com/matthieugrieger/mumbledj/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg"/></a> <a href="https://github.com/matthieugrieger/mumbledj/releases"><img src="https://img.shields.io/github/release/matthieugrieger/mumbledj.svg"/></a> <a href="https://goreportcard.com/report/github.com/matthieugrieger/mumbledj"><img src="https://goreportcard.com/badge/github.com/matthieugrieger/mumbledj"/></a> <a href="https://codecov.io/gh/matthieugrieger/mumbledj"><img src="https://img.shields.io/codecov/c/github/matthieugrieger/mumbledj.svg"/></a> <a href="https://gitter.im/matthieugrieger/mumbledj"><img src="https://img.shields.io/gitter/room/matthieugrieger/mumbledj.svg" /></a></p>
<p align="center"><b>Unfortunately, this project is no longer maintained. Don't expect any responses on bug reports, feature requests, etc. Forks are welcome!</b></p>
## Table of Contents ## Table of Contents
* [Features](#features) * [Features](#features)
@ -13,6 +15,7 @@
* [Via `go get`](#via-go-get-recommended) * [Via `go get`](#via-go-get-recommended)
* [Pre-compiled Binaries](#pre-compiled-binaries-easiest) * [Pre-compiled Binaries](#pre-compiled-binaries-easiest)
* [From Source](#from-source) * [From Source](#from-source)
* [Docker](#docker)
* [Usage](#usage) * [Usage](#usage)
* [Commands](#commands) * [Commands](#commands)
* [Contributing](#contributing) * [Contributing](#contributing)
@ -57,7 +60,7 @@ A YouTube API key must be present in your configuration file in order to use the
**5)** Add the IP address of the machine MumbleDJ will run on in the box that appears (this is optional, but improves security). Click "Create". **5)** Add the IP address of the machine MumbleDJ will run on in the box that appears (this is optional, but improves security). Click "Create".
**6)** You should now see that an API key has been generated. Copy/paste this API key into the configuration file located at `$HOME/.config/mumbledj/mumbledj.yaml`. **6)** You should now see that an API key has been generated. Copy/paste this API key into the configuration file located at `$HOME/.config/mumbledj/mumbledj.yaml`.
#### SoundCloud API Key #### SoundCloud API Key
A SoundCloud client ID must be present in your configuration file in order to use the SoundCloud service within the bot. Below is a guide for retrieving a client ID: A SoundCloud client ID must be present in your configuration file in order to use the SoundCloud service within the bot. Below is a guide for retrieving a client ID:
@ -103,6 +106,35 @@ This will place a compiled `mumbledj` binary in the cloned directory if successf
sudo make install sudo make install
``` ```
### Docker
You can also use [Docker](https://www.docker.com) to run MumbleDJ.
First you need to clone the MumbleDJ repository to your machine:
```
git clone https://github.com/matthieugrieger/mumbledj.git
```
Assuming you have [Docker installed](https://www.docker.com/products/docker), you will have to build the image:
```
docker build -t mumbledj .
```
And then you can run it, passing the configuration through the command line:
```
docker run --rm --name=mumbledj mumbledj --server=SERVER --api_keys.youtube=YOUR_YOUTUBE_API_KEY --api_keys.soundcloud=YOUR_SOUNDCLOUD_API_KEY
```
In order to run the process as a daemon and restart it automatically on reboot you can use:
```
docker run -d --restart=unless-stopped --name=mumbledj mumbledj --server=SERVER --api_keys.youtube=YOUR_YOUTUBE_API_KEY --api_keys.soundcloud=YOUR_SOUNDCLOUD_API_KEY
```
You can also install Docker on a [Raspberry Pi](https://www.raspberrypi.org/) for instance with [hypriot](http://blog.hypriot.com/getting-started-with-docker-on-your-arm-device/) or with [archlinux](https://archlinuxarm.org/packages/arm/docker). You just need to build the ARM image:
```
docker build -f raspberry.Dockerfile -t mumbledj .
```
## Usage ## Usage
MumbleDJ is a compiled program that is executed via a terminal. MumbleDJ is a compiled program that is executed via a terminal.
@ -114,25 +146,26 @@ NAME:
USAGE: USAGE:
mumbledj [global options] command [command options] [arguments...] mumbledj [global options] command [command options] [arguments...]
VERSION: VERSION:
3.0.0-alpha v3.1.0
COMMANDS: COMMANDS:
GLOBAL OPTIONS: GLOBAL OPTIONS:
--config value, -c value location of MumbleDJ configuration file (default: "$HOME/.config/mumbledj/mumbledj.yaml") --config value, -c value location of MumbleDJ configuration file (default: "/home/matthieu/.config/mumbledj/config.yaml")
--server value, -s value address of Mumble server to connect to (default: "127.0.0.1") --server value, -s value address of Mumble server to connect to (default: "127.0.0.1")
--port value, -o value port of Mumble server to connect to (default: "64738") --port value, -o value port of Mumble server to connect to (default: "64738")
--username value, -u value username for the bot (default: "MumbleDJ") --username value, -u value username for the bot (default: "MumbleDJ")
--password value, -p value password for the Mumble server --password value, -p value password for the Mumble server
--channel value, -n value channel the bot enters after connecting to the Mumble server --channel value, -n value channel the bot enters after connecting to the Mumble server
--cert value, -e value path to PEM certificate --p12 value path to user p12 file for authenticating as a registered user
--key value, -k value path to PEM key --cert value, -e value path to PEM certificate
--accesstokens value, -a value list of access tokens separated by spaces --key value, -k value path to PEM key
--insecure, -i if present, the bot will not check Mumble certs for consistency --accesstokens value, -a value list of access tokens separated by spaces
--debug, -d if present, all debug messages will be shown --insecure, -i if present, the bot will not check Mumble certs for consistency
--help, -h show help --debug, -d if present, all debug messages will be shown
--version, -v print the version --help, -h show help
--version, -v print the version
``` ```
@ -251,6 +284,13 @@ Keep in mind that values that contain commas (such as `"SuperUser,Matt"`) will b
* __Admin-only by default__: No * __Admin-only by default__: No
* __Example__: `!pause` * __Example__: `!pause`
### register
* __Description__: Registers the bot on the server.
* __Default Aliases__: register, reg
* __Arguments__: None
* __Admin-only by default__: Yes
* __Example__: `!register`
### reload ### reload
* __Description__: Reloads the configuration file. * __Description__: Reloads the configuration file.
* __Default Aliases__: reload, r * __Default Aliases__: reload, r

File diff suppressed because one or more lines are too long

View file

@ -40,6 +40,7 @@ func SetDefaultConfig() {
viper.SetDefault("connection.port", 64738) viper.SetDefault("connection.port", 64738)
viper.SetDefault("connection.password", "") viper.SetDefault("connection.password", "")
viper.SetDefault("connection.username", "MumbleDJ") viper.SetDefault("connection.username", "MumbleDJ")
viper.SetDefault("connection.user_p12", "")
viper.SetDefault("connection.insecure", false) viper.SetDefault("connection.insecure", false)
viper.SetDefault("connection.cert", "") viper.SetDefault("connection.cert", "")
viper.SetDefault("connection.key", "") viper.SetDefault("connection.key", "")
@ -156,6 +157,12 @@ func SetDefaultConfig() {
viper.SetDefault("commands.pause.messages.no_audio_error", "Either the audio is already paused, or there are no tracks in the queue.") viper.SetDefault("commands.pause.messages.no_audio_error", "Either the audio is already paused, or there are no tracks in the queue.")
viper.SetDefault("commands.pause.messages.paused", "<b>%s</b> has paused audio playback.") viper.SetDefault("commands.pause.messages.paused", "<b>%s</b> has paused audio playback.")
viper.SetDefault("commands.register.aliases", []string{"register", "reg"})
viper.SetDefault("commands.register.is_admin", true)
viper.SetDefault("commands.register.description", "Registers the bot on the server.")
viper.SetDefault("commands.register.messages.already_registered_error", "I am already registered on the server.")
viper.SetDefault("commands.register.messages.registered", "I am now registered on the server.")
viper.SetDefault("commands.reload.aliases", []string{"reload", "r"}) viper.SetDefault("commands.reload.aliases", []string{"reload", "r"})
viper.SetDefault("commands.reload.is_admin", true) viper.SetDefault("commands.reload.is_admin", true)
viper.SetDefault("commands.reload.description", "Reloads the configuration file.") viper.SetDefault("commands.reload.description", "Reloads the configuration file.")
@ -189,6 +196,7 @@ func SetDefaultConfig() {
viper.SetDefault("commands.skip.description", "Places a vote to skip the current track.") viper.SetDefault("commands.skip.description", "Places a vote to skip the current track.")
viper.SetDefault("commands.skip.messages.already_voted_error", "You have already voted to skip this track.") viper.SetDefault("commands.skip.messages.already_voted_error", "You have already voted to skip this track.")
viper.SetDefault("commands.skip.messages.voted", "<b>%s</b> has voted to skip the current track.") viper.SetDefault("commands.skip.messages.voted", "<b>%s</b> has voted to skip the current track.")
viper.SetDefault("commands.skip.messages.submitter_voted", "<b>%s</b>, the submitter of this track, has voted to skip. Skipping immediately.")
viper.SetDefault("commands.skipplaylist.aliases", []string{"skipplaylist", "sp"}) viper.SetDefault("commands.skipplaylist.aliases", []string{"skipplaylist", "sp"})
viper.SetDefault("commands.skipplaylist.is_admin", false) viper.SetDefault("commands.skipplaylist.is_admin", false)
@ -196,6 +204,7 @@ func SetDefaultConfig() {
viper.SetDefault("commands.skipplaylist.messages.no_playlist_error", "The current track is not part of a playlist.") viper.SetDefault("commands.skipplaylist.messages.no_playlist_error", "The current track is not part of a playlist.")
viper.SetDefault("commands.skipplaylist.messages.already_voted_error", "You have already voted to skip this playlist.") viper.SetDefault("commands.skipplaylist.messages.already_voted_error", "You have already voted to skip this playlist.")
viper.SetDefault("commands.skipplaylist.messages.voted", "<b>%s</b> has voted to skip the current playlist.") viper.SetDefault("commands.skipplaylist.messages.voted", "<b>%s</b> has voted to skip the current playlist.")
viper.SetDefault("commands.skipplaylist.messages.submitter_voted", "<b>%s</b>, the submitter of this playlist, has voted to skip. Skipping immediately.")
viper.SetDefault("commands.toggleshuffle.aliases", []string{"toggleshuffle", "toggleshuf", "togshuf", "tsh"}) viper.SetDefault("commands.toggleshuffle.aliases", []string{"toggleshuffle", "toggleshuf", "togshuf", "tsh"})
viper.SetDefault("commands.toggleshuffle.is_admin", true) viper.SetDefault("commands.toggleshuffle.is_admin", true)

View file

@ -11,7 +11,10 @@ import (
"crypto/tls" "crypto/tls"
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"net" "net"
"os"
"os/exec"
"strings" "strings"
"time" "time"
@ -193,13 +196,48 @@ func (dj *MumbleDJ) Connect() error {
// Initialize key pair if needed. // Initialize key pair if needed.
if viper.GetBool("connection.insecure") { if viper.GetBool("connection.insecure") {
dj.TLSConfig.InsecureSkipVerify = true dj.TLSConfig.InsecureSkipVerify = true
} else {
dj.TLSConfig.ServerName = viper.GetString("connection.address")
if viper.GetString("connection.cert") != "" {
if viper.GetString("connection.key") == "" {
viper.Set("connection.key", viper.GetString("connection.cert"))
}
if certificate, err := tls.LoadX509KeyPair(viper.GetString("connection.cert"), viper.GetString("connection.key")); err == nil {
dj.TLSConfig.Certificates = append(dj.TLSConfig.Certificates, certificate)
} else {
return err
}
}
} }
if viper.GetString("connection.cert") != "" {
if viper.GetString("connection.key") == "" { // Add user p12 cert if needed.
viper.Set("connection.key", viper.GetString("connection.cert")) if viper.GetString("connection.user_p12") != "" {
if _, err := os.Stat(viper.GetString("connection.user_p12")); os.IsNotExist(err) {
return err
} }
if certificate, err := tls.LoadX509KeyPair(viper.GetString("connection.cert"), viper.GetString("connection.key")); err == nil { // Create temporary directory for converted p12 file.
dir, err := ioutil.TempDir("", "mumbledj")
if err != nil {
return err
}
defer os.RemoveAll(dir)
// Create temporary mumbledj.crt.pem from p12 file.
command := exec.Command("openssl", "pkcs12", "-password", "pass:", "-in", viper.GetString("connection.user_p12"), "-out", dir+"/mumbledj.crt.pem", "-clcerts", "-nokeys")
if err := command.Run(); err != nil {
return err
}
// Create temporary mumbledj.key.pem from p12 file.
command = exec.Command("openssl", "pkcs12", "-password", "pass:", "-in", viper.GetString("connection.user_p12"), "-out", dir+"/mumbledj.key.pem", "-nocerts", "-nodes")
if err := command.Run(); err != nil {
return err
}
if certificate, err := tls.LoadX509KeyPair(dir+"/mumbledj.crt.pem", dir+"/mumbledj.key.pem"); err == nil {
dj.TLSConfig.Certificates = append(dj.TLSConfig.Certificates, certificate) dj.TLSConfig.Certificates = append(dj.TLSConfig.Certificates, certificate)
} else { } else {
return err return err

View file

@ -199,7 +199,7 @@ func (q *Queue) Skip() {
q.mutex.Lock() q.mutex.Lock()
// If caching is disabled, delete the track from disk. // If caching is disabled, delete the track from disk.
if !viper.GetBool("cache.enabled") { if len(q.Queue) != 0 && !viper.GetBool("cache.enabled") {
DJ.YouTubeDL.Delete(q.Queue[0]) DJ.YouTubeDL.Delete(q.Queue[0])
} }

View file

@ -58,6 +58,10 @@ func PerformStartupChecks() {
if err := checkAria2Installation(); err != nil { if err := checkAria2Installation(); err != nil {
logrus.Warnln("aria2 is not installed or is not discoverable in $PATH. The bot will still partially work, but some services will not work properly.") logrus.Warnln("aria2 is not installed or is not discoverable in $PATH. The bot will still partially work, but some services will not work properly.")
} }
if err := checkOpenSSLInstallation(); err != nil {
logrus.Warnln("openssl is not installed or is not discoverable in $PATH. p12 certificate files will not work.")
}
} }
func checkYouTubeDLInstallation() error { func checkYouTubeDLInstallation() error {
@ -95,3 +99,12 @@ func checkAria2Installation() error {
} }
return nil return nil
} }
func checkOpenSSLInstallation() error {
logrus.Infoln("Checking openssl installation...")
command := exec.Command("openssl", "version")
if err := command.Run(); err != nil {
return errors.New("openssl is not properly installed")
}
return nil
}

View file

@ -35,6 +35,7 @@ func init() {
new(NumCachedCommand), new(NumCachedCommand),
new(NumTracksCommand), new(NumTracksCommand),
new(PauseCommand), new(PauseCommand),
new(RegisterCommand),
new(ReloadCommand), new(ReloadCommand),
new(ResetCommand), new(ResetCommand),
new(ResumeCommand), new(ResumeCommand),

53
commands/register.go Normal file
View file

@ -0,0 +1,53 @@
/*
* MumbleDJ
* By Matthieu Grieger
* commands/register.go
* Copyright (c) 2016 Matthieu Grieger (MIT License)
*/
package commands
import (
"errors"
"github.com/layeh/gumble/gumble"
"github.com/spf13/viper"
)
// RegisterCommand is a command that registers the bot on the server.
type RegisterCommand struct{}
// Aliases returns the current aliases for the command.
func (c *RegisterCommand) Aliases() []string {
return viper.GetStringSlice("commands.register.aliases")
}
// Description returns the description for the command.
func (c *RegisterCommand) Description() string {
return viper.GetString("commands.register.description")
}
// IsAdminCommand returns true if the command is only for admin use, and
// returns false otherwise.
func (c *RegisterCommand) IsAdminCommand() bool {
return viper.GetBool("commands.register.is_admin")
}
// Execute executes the command with the given user and arguments.
// Return value descriptions:
// string: A message to be returned to the user upon successful execution.
// bool: Whether the message should be private or not. true = private,
// false = public (sent to whole channel).
// error: An error message to be returned upon unsuccessful execution.
// If no error has occurred, pass nil instead.
// Example return statement:
// return "This is a private message!", true, nil
func (c *RegisterCommand) Execute(user *gumble.User, args ...string) (string, bool, error) {
if DJ.Client.Self.IsRegistered() {
return "", true, errors.New(viper.GetString("commands.register.messages.already_registered_error"))
}
DJ.Client.Self.Register()
return viper.GetString("commands.register.messages.registered"), true, nil
}

View file

@ -0,0 +1,8 @@
/*
* MumbleDJ
* By Matthieu Grieger
* commands/register_test.go
* Copyright (c) 2016 Matthieu Grieger (MIT License)
*/
package commands

View file

@ -47,6 +47,11 @@ func (c *SkipCommand) Execute(user *gumble.User, args ...string) (string, bool,
if DJ.Queue.Length() == 0 { if DJ.Queue.Length() == 0 {
return "", true, errors.New(viper.GetString("commands.common_messages.no_tracks_error")) return "", true, errors.New(viper.GetString("commands.common_messages.no_tracks_error"))
} }
if DJ.Queue.GetTrack(0).GetSubmitter() == user.Name {
// The user who submitted the track is skipping, this means we skip this track immediately.
DJ.Queue.StopCurrent()
return fmt.Sprintf(viper.GetString("commands.skip.messages.submitter_voted"), user.Name), false, nil
}
if err := DJ.Skips.AddTrackSkip(user); err != nil { if err := DJ.Skips.AddTrackSkip(user); err != nil {
return "", true, errors.New(viper.GetString("commands.skip.messages.already_voted_error")) return "", true, errors.New(viper.GetString("commands.skip.messages.already_voted_error"))
} }

View file

@ -58,6 +58,10 @@ func (c *SkipPlaylistCommand) Execute(user *gumble.User, args ...string) (string
if playlist := currentTrack.GetPlaylist(); playlist == nil { if playlist := currentTrack.GetPlaylist(); playlist == nil {
return "", true, errors.New(viper.GetString("commands.skipplaylist.messages.no_playlist_error")) return "", true, errors.New(viper.GetString("commands.skipplaylist.messages.no_playlist_error"))
} }
if currentTrack.GetPlaylist().GetSubmitter() == user.Name {
DJ.Queue.SkipPlaylist()
return fmt.Sprintf(viper.GetString("commands.skipplaylist.messages.submitter_voted"), user.Name), false, nil
}
if err := DJ.Skips.AddPlaylistSkip(user); err != nil { if err := DJ.Skips.AddPlaylistSkip(user); err != nil {
return "", true, errors.New(viper.GetString("commands.skipplaylist.messages.already_voted_error")) return "", true, errors.New(viper.GetString("commands.skipplaylist.messages.already_voted_error"))
} }

View file

@ -63,6 +63,10 @@ connection:
# Username for MumbleDJ. # Username for MumbleDJ.
username: "MumbleDJ" username: "MumbleDJ"
# Filepath to user p12 file for authenticating as a registered user.
# NOTE: If no p12 file is needed, set to empty string ("").
user_p12: ""
# Should the bot attempt an insecure connection? # Should the bot attempt an insecure connection?
# An insecure connection does not verify the certificate of the server for # An insecure connection does not verify the certificate of the server for
# consistency. It is best to leave this on, but disable it if you are having # consistency. It is best to leave this on, but disable it if you are having
@ -260,7 +264,7 @@ commands:
messages: messages:
no_channel_provided_error: "A destination channel must be supplied to move the bot." no_channel_provided_error: "A destination channel must be supplied to move the bot."
channel_doesnt_exist_error: "The provided channel does not exist." channel_doesnt_exist_error: "The provided channel does not exist."
move_successful: "You have successfully moved the bot to <b>%s</b>." move_successful: "You have successfully moved the bot to <b>%s</b>."
nexttrack: nexttrack:
aliases: aliases:
@ -302,6 +306,16 @@ commands:
no_audio_error: "Either the audio is already paused, or there are no tracks in the queue." no_audio_error: "Either the audio is already paused, or there are no tracks in the queue."
paused: "<b>%s</b> has paused audio playback." paused: "<b>%s</b> has paused audio playback."
register:
aliases:
- "register"
- "reg"
is_admin: true
description: "Registers the bot on the server."
messages:
already_registered_error: "I am already registered on the server."
registered: "I am now registered on the server."
reload: reload:
aliases: aliases:
- "reload" - "reload"
@ -360,6 +374,7 @@ commands:
messages: messages:
already_voted_error: "You have already voted to skip this track." already_voted_error: "You have already voted to skip this track."
voted: "<b>%s</b> has voted to skip the current track." voted: "<b>%s</b> has voted to skip the current track."
submitter_voted: "<b>%s</b>, the submitter of this track, has voted to skip. Skipping immediately."
skipplaylist: skipplaylist:
aliases: aliases:
@ -371,6 +386,7 @@ commands:
no_playlist_error: "The current track is not part of a playlist." no_playlist_error: "The current track is not part of a playlist."
already_voted_error: "You have already voted to skip this playlist." already_voted_error: "You have already voted to skip this playlist."
voted: "<b>%s</b> has voted to skip the current playlist." voted: "<b>%s</b> has voted to skip the current playlist."
submitter_voted: "<b>%s</b>, the submitter of this playlist, has voted to skip. Skipping immediately."
toggleshuffle: toggleshuffle:
aliases: aliases:
@ -404,4 +420,4 @@ commands:
parsing_error: "The requested volume could not be parsed." parsing_error: "The requested volume could not be parsed."
out_of_range_error: "Volumes must be between the values <b>%.2f</b> and <b>%.2f</b>." out_of_range_error: "Volumes must be between the values <b>%.2f</b> and <b>%.2f</b>."
current_volume: "The current volume is <b>%.2f</b>." current_volume: "The current volume is <b>%.2f</b>."
volume_changed: "<b>%s</b> has changed the volume to <b>%.2f</b>." volume_changed: "<b>%s</b> has changed the volume to <b>%.2f</b>."

40
glide.lock generated
View file

@ -1,20 +1,20 @@
hash: b68f7c8a3b59d7dac3e12321ed6a2265b553c2856ae70e0ed5e960ba8412f8d8 hash: b68f7c8a3b59d7dac3e12321ed6a2265b553c2856ae70e0ed5e960ba8412f8d8
updated: 2016-06-21T09:22:02.569941705-07:00 updated: 2016-07-11T16:01:20.19606261-07:00
imports: imports:
- name: github.com/antonholmquist/jason - name: github.com/antonholmquist/jason
version: "" version: c23cef7eaa75a6a5b8810120e167bd590d8fd2ab
- name: github.com/BurntSushi/toml - name: github.com/BurntSushi/toml
version: "" version: bbd5bb678321a0d6e58f1099321dfa73391c1b6f
- name: github.com/ChannelMeter/iso8601duration - name: github.com/ChannelMeter/iso8601duration
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94 version: 8da3af7a2a61a4eb5ae9bddec06bf637fa9593da
- name: github.com/fsnotify/fsnotify - name: github.com/fsnotify/fsnotify
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94 version: a8a77c9133d2d6fd8334f3260d06f60e8d80a5fb
- name: github.com/golang/protobuf - name: github.com/golang/protobuf
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94 version: 3852dcfda249c2097355a6aabb199a28d97b30df
subpackages: subpackages:
- proto - proto
- name: github.com/hashicorp/hcl - name: github.com/hashicorp/hcl
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94 version: 364df430845abef160a0bfb3a59979f746bf4956
subpackages: subpackages:
- hcl/ast - hcl/ast
- hcl/parser - hcl/parser
@ -25,9 +25,9 @@ imports:
- json/scanner - json/scanner
- json/token - json/token
- name: github.com/layeh/gopus - name: github.com/layeh/gopus
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94 version: 867541549ca5f8b4db2b92fd1dded8711256a27d
- name: github.com/layeh/gumble - name: github.com/layeh/gumble
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94 version: f0a4a2992fa9a969ef90d673374bc63a9b7948ad
subpackages: subpackages:
- gumble - gumble
- gumbleffmpeg - gumbleffmpeg
@ -36,27 +36,27 @@ imports:
- gumble/MumbleProto - gumble/MumbleProto
- gumble/varint - gumble/varint
- name: github.com/magiconair/properties - name: github.com/magiconair/properties
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94 version: e2f061ecfdaca9f35b2e2c12346ffc526f138137
- name: github.com/mitchellh/mapstructure - name: github.com/mitchellh/mapstructure
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94 version: d2dd0262208475919e1a362f675cfc0e7c10e905
- name: github.com/Sirupsen/logrus - name: github.com/Sirupsen/logrus
version: "" version: 4b6ea7319e214d98c938f12692336f7ca9348d6b
- name: github.com/spf13/cast - name: github.com/spf13/cast
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94 version: 27b586b42e29bec072fe7379259cc719e1289da6
- name: github.com/spf13/jwalterweatherman - name: github.com/spf13/jwalterweatherman
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94 version: 33c24e77fb80341fe7130ee7c594256ff08ccc46
- name: github.com/spf13/pflag - name: github.com/spf13/pflag
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94 version: 367864438f1b1a3c7db4da06a2f55b144e6784e0
- name: github.com/spf13/viper - name: github.com/spf13/viper
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94 version: c1ccc378a054ea8d4e38d8c67f6938d4760b53dd
- name: github.com/stretchr/testify - name: github.com/stretchr/testify
version: "" version: f390dcf405f7b83c997eac1b06768bb9f44dec18
- name: github.com/urfave/cli - name: github.com/urfave/cli
version: "" version: 01857ac33766ce0c93856370626f9799281c14f4
- name: golang.org/x/sys - name: golang.org/x/sys
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94 version: a408501be4d17ee978c04a618e7a1b22af058c0e
subpackages: subpackages:
- unix - unix
- name: gopkg.in/yaml.v2 - name: gopkg.in/yaml.v2
version: 50c5e6cb61d6f5a3c449ab0b12f1996160626a94 version: a83829b6f1293c91addabc89d0571c246397bbf4
devImports: [] devImports: []

10
main.go
View file

@ -32,7 +32,7 @@ func init() {
services.DJ = DJ services.DJ = DJ
bot.DJ = DJ bot.DJ = DJ
DJ.Version = "v3.0.10" DJ.Version = "v3.2.1"
logrus.SetLevel(logrus.WarnLevel) logrus.SetLevel(logrus.WarnLevel)
} }
@ -73,6 +73,11 @@ func main() {
Value: "", Value: "",
Usage: "channel the bot enters after connecting to the Mumble server", Usage: "channel the bot enters after connecting to the Mumble server",
}, },
cli.StringFlag{
Name: "p12",
Value: "",
Usage: "path to user p12 file for authenticating as a registered user",
},
cli.StringFlag{ cli.StringFlag{
Name: "cert, e", Name: "cert, e",
Value: "", Value: "",
@ -156,6 +161,9 @@ func main() {
if c.GlobalIsSet("channel") { if c.GlobalIsSet("channel") {
viper.Set("defaults.channel", c.String("channel")) viper.Set("defaults.channel", c.String("channel"))
} }
if c.GlobalIsSet("p12") {
viper.Set("connection.user_p12", c.String("p12"))
}
if c.GlobalIsSet("cert") { if c.GlobalIsSet("cert") {
viper.Set("connection.cert", c.String("cert")) viper.Set("connection.cert", c.String("cert"))
} }

20
raspberry.Dockerfile Normal file
View file

@ -0,0 +1,20 @@
FROM hypriot/rpi-alpine-scratch:v3.3
ENV GOPATH=/
RUN echo "https://dl-cdn.alpinelinux.org/alpine/v3.3/community" >> /etc/apk/repositories
RUN apk add --update ca-certificates go ffmpeg make build-base opus-dev python aria2
RUN apk upgrade
RUN wget https://yt-dl.org/downloads/latest/youtube-dl -O /bin/youtube-dl && chmod a+x /bin/youtube-dl
COPY . /src/github.com/matthieugrieger/mumbledj
COPY config.yaml /root/.config/mumbledj/config.yaml
WORKDIR /src/github.com/matthieugrieger/mumbledj
RUN make
RUN make install
RUN apk del go make build-base && rm -rf /var/cache/apk/*
ENTRYPOINT ["/usr/local/bin/mumbledj"]

View file

@ -52,7 +52,7 @@ func (sc *SoundCloud) CheckAPIKey() error {
if viper.GetString("api_keys.soundcloud") == "" { if viper.GetString("api_keys.soundcloud") == "" {
return errors.New("No SoundCloud API key has been provided") return errors.New("No SoundCloud API key has been provided")
} }
url := "http://api.soundcloud.com/tracks/vjflzpbkmerb?client_id=%s" url := "http://api.soundcloud.com/tracks/13158665?client_id=%s"
response, err := http.Get(fmt.Sprintf(url, viper.GetString("api_keys.soundcloud"))) response, err := http.Get(fmt.Sprintf(url, viper.GetString("api_keys.soundcloud")))
defer response.Body.Close() defer response.Body.Close()
if err != nil { if err != nil {
@ -162,7 +162,8 @@ func (sc *SoundCloud) GetTracks(url string, submitter *gumble.User) ([]interface
func (sc *SoundCloud) getTrack(obj *jason.Object, offset time.Duration, submitter *gumble.User) (bot.Track, error) { func (sc *SoundCloud) getTrack(obj *jason.Object, offset time.Duration, submitter *gumble.User) (bot.Track, error) {
title, _ := obj.GetString("title") title, _ := obj.GetString("title")
id, _ := obj.GetString("id") idInt, _ := obj.GetInt64("id")
id := strconv.FormatInt(idInt, 10)
url, _ := obj.GetString("permalink_url") url, _ := obj.GetString("permalink_url")
author, _ := obj.GetString("user", "username") author, _ := obj.GetString("user", "username")
authorURL, _ := obj.GetString("user", "permalink_url") authorURL, _ := obj.GetString("user", "permalink_url")

View file

@ -13,6 +13,8 @@ import (
"math" "math"
"net/http" "net/http"
"regexp" "regexp"
"strings"
"time"
"github.com/ChannelMeter/iso8601duration" "github.com/ChannelMeter/iso8601duration"
"github.com/antonholmquist/jason" "github.com/antonholmquist/jason"
@ -98,9 +100,12 @@ func (yt *YouTube) GetTracks(url string, submitter *gumble.User) ([]interfaces.T
tracks []interfaces.Track tracks []interfaces.Track
) )
dummyOffset, _ := time.ParseDuration("0s")
urlSplit := strings.Split(url, "?t=")
playlistURL = "https://www.googleapis.com/youtube/v3/playlists?part=snippet&id=%s&key=%s" playlistURL = "https://www.googleapis.com/youtube/v3/playlists?part=snippet&id=%s&key=%s"
playlistItemsURL = "https://www.googleapis.com/youtube/v3/playlistItems?part=snippet,contentDetails&playlistId=%s&maxResults=%d&key=%s&pageToken=%s" playlistItemsURL = "https://www.googleapis.com/youtube/v3/playlistItems?part=snippet,contentDetails&playlistId=%s&maxResults=%d&key=%s&pageToken=%s"
id, err = yt.getID(url) id, err = yt.getID(urlSplit[0])
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -161,7 +166,7 @@ func (yt *YouTube) GetTracks(url string, submitter *gumble.User) ([]interfaces.T
// Unfortunately we have to execute another API call for each video as the YouTube API does not // Unfortunately we have to execute another API call for each video as the YouTube API does not
// return video durations from the playlistItems endpoint... // return video durations from the playlistItems endpoint...
newTrack, _ := yt.getTrack(videoID, submitter) newTrack, _ := yt.getTrack(videoID, submitter, dummyOffset)
newTrack.Playlist = playlist newTrack.Playlist = playlist
tracks = append(tracks, newTrack) tracks = append(tracks, newTrack)
@ -182,7 +187,13 @@ func (yt *YouTube) GetTracks(url string, submitter *gumble.User) ([]interfaces.T
return tracks, nil return tracks, nil
} }
track, err = yt.getTrack(id, submitter) // Submitter added a track!
offset := dummyOffset
if len(urlSplit) == 2 {
offset, _ = time.ParseDuration(urlSplit[1])
}
track, err = yt.getTrack(id, submitter, offset)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -190,7 +201,7 @@ func (yt *YouTube) GetTracks(url string, submitter *gumble.User) ([]interfaces.T
return tracks, nil return tracks, nil
} }
func (yt *YouTube) getTrack(id string, submitter *gumble.User) (bot.Track, error) { func (yt *YouTube) getTrack(id string, submitter *gumble.User, offset time.Duration) (bot.Track, error) {
var ( var (
resp *http.Response resp *http.Response
err error err error
@ -209,6 +220,9 @@ func (yt *YouTube) getTrack(id string, submitter *gumble.User) (bot.Track, error
return bot.Track{}, err return bot.Track{}, err
} }
items, _ := v.GetObjectArray("items") items, _ := v.GetObjectArray("items")
if len(items) == 0 {
return bot.Track{}, errors.New("This YouTube video is private")
}
item := items[0] item := items[0]
title, _ := item.GetString("snippet", "title") title, _ := item.GetString("snippet", "title")
thumbnail, _ := item.GetString("snippet", "thumbnails", "high", "url") thumbnail, _ := item.GetString("snippet", "thumbnails", "high", "url")
@ -218,15 +232,16 @@ func (yt *YouTube) getTrack(id string, submitter *gumble.User) (bot.Track, error
duration := durationConverted.ToDuration() duration := durationConverted.ToDuration()
return bot.Track{ return bot.Track{
ID: id, ID: id,
URL: "https://youtube.com/watch?v=" + id, URL: "https://youtube.com/watch?v=" + id,
Title: title, Title: title,
Author: author, Author: author,
Submitter: submitter.Name, Submitter: submitter.Name,
Service: yt.ReadableName, Service: yt.ReadableName,
Filename: id + ".track", Filename: id + ".track",
ThumbnailURL: thumbnail, ThumbnailURL: thumbnail,
Duration: duration, Duration: duration,
Playlist: nil, PlaybackOffset: offset,
Playlist: nil,
}, nil }, nil
} }

View file

@ -11,6 +11,7 @@
Adrien Bustany <adrien@bustany.org> Adrien Bustany <adrien@bustany.org>
Amit Krishnan <amit.krishnan@oracle.com> Amit Krishnan <amit.krishnan@oracle.com>
Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Bruno Bigras <bigras.bruno@gmail.com>
Caleb Spare <cespare@gmail.com> Caleb Spare <cespare@gmail.com>
Case Nelson <case@teammating.com> Case Nelson <case@teammating.com>
Chris Howey <chris@howey.me> <howeyc@gmail.com> Chris Howey <chris@howey.me> <howeyc@gmail.com>

View file

@ -1,5 +1,9 @@
# Changelog # Changelog
## v1.3.1 / 2016-06-28
* windows: fix for double backslash when watching the root of a drive [#151](https://github.com/fsnotify/fsnotify/issues/151) (thanks @brunoqc)
## v1.3.0 / 2016-04-19 ## v1.3.0 / 2016-04-19
* Support linux/arm64 by [patching](https://go-review.googlesource.com/#/c/21971/) x/sys/unix and switching to to it from syscall (thanks @suihkulokki) [#135](https://github.com/fsnotify/fsnotify/pull/135) * Support linux/arm64 by [patching](https://go-review.googlesource.com/#/c/21971/) x/sys/unix and switching to to it from syscall (thanks @suihkulokki) [#135](https://github.com/fsnotify/fsnotify/pull/135)

View file

@ -306,7 +306,7 @@ func (w *Watcher) remWatch(pathname string) error {
watch.mask = 0 watch.mask = 0
} else { } else {
name := filepath.Base(pathname) name := filepath.Base(pathname)
w.sendEvent(watch.path+"\\"+name, watch.names[name]&sysFSIGNORED) w.sendEvent(filepath.Join(watch.path, name), watch.names[name]&sysFSIGNORED)
delete(watch.names, name) delete(watch.names, name)
} }
return w.startRead(watch) return w.startRead(watch)
@ -316,7 +316,7 @@ func (w *Watcher) remWatch(pathname string) error {
func (w *Watcher) deleteWatch(watch *watch) { func (w *Watcher) deleteWatch(watch *watch) {
for name, mask := range watch.names { for name, mask := range watch.names {
if mask&provisional == 0 { if mask&provisional == 0 {
w.sendEvent(watch.path+"\\"+name, mask&sysFSIGNORED) w.sendEvent(filepath.Join(watch.path, name), mask&sysFSIGNORED)
} }
delete(watch.names, name) delete(watch.names, name)
} }
@ -453,7 +453,7 @@ func (w *Watcher) readEvents() {
raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset])) raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset]))
buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName)) buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName))
name := syscall.UTF16ToString(buf[:raw.FileNameLength/2]) name := syscall.UTF16ToString(buf[:raw.FileNameLength/2])
fullname := watch.path + "\\" + name fullname := filepath.Join(watch.path, name)
var mask uint64 var mask uint64
switch raw.Action { switch raw.Action {
@ -491,7 +491,7 @@ func (w *Watcher) readEvents() {
} }
} }
if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME { if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME {
fullname = watch.path + "\\" + watch.rename fullname = filepath.Join(watch.path, watch.rename)
sendNameEvent() sendNameEvent()
} }

View file

@ -510,41 +510,63 @@ func (m *Marshaler) marshalValue(out *errWriter, prop *proto.Properties, v refle
return out.err return out.err
} }
// Unmarshaler is a configurable object for converting from a JSON
// representation to a protocol buffer object.
type Unmarshaler struct {
// Whether to allow messages to contain unknown fields, as opposed to
// failing to unmarshal.
AllowUnknownFields bool
}
// UnmarshalNext unmarshals the next protocol buffer from a JSON object stream. // UnmarshalNext unmarshals the next protocol buffer from a JSON object stream.
// This function is lenient and will decode any options permutations of the // This function is lenient and will decode any options permutations of the
// related Marshaler. // related Marshaler.
func UnmarshalNext(dec *json.Decoder, pb proto.Message) error { func (u *Unmarshaler) UnmarshalNext(dec *json.Decoder, pb proto.Message) error {
inputValue := json.RawMessage{} inputValue := json.RawMessage{}
if err := dec.Decode(&inputValue); err != nil { if err := dec.Decode(&inputValue); err != nil {
return err return err
} }
return unmarshalValue(reflect.ValueOf(pb).Elem(), inputValue, nil) return u.unmarshalValue(reflect.ValueOf(pb).Elem(), inputValue, nil)
}
// Unmarshal unmarshals a JSON object stream into a protocol
// buffer. This function is lenient and will decode any options
// permutations of the related Marshaler.
func (u *Unmarshaler) Unmarshal(r io.Reader, pb proto.Message) error {
dec := json.NewDecoder(r)
return u.UnmarshalNext(dec, pb)
}
// UnmarshalNext unmarshals the next protocol buffer from a JSON object stream.
// This function is lenient and will decode any options permutations of the
// related Marshaler.
func UnmarshalNext(dec *json.Decoder, pb proto.Message) error {
return new(Unmarshaler).UnmarshalNext(dec, pb)
} }
// Unmarshal unmarshals a JSON object stream into a protocol // Unmarshal unmarshals a JSON object stream into a protocol
// buffer. This function is lenient and will decode any options // buffer. This function is lenient and will decode any options
// permutations of the related Marshaler. // permutations of the related Marshaler.
func Unmarshal(r io.Reader, pb proto.Message) error { func Unmarshal(r io.Reader, pb proto.Message) error {
dec := json.NewDecoder(r) return new(Unmarshaler).Unmarshal(r, pb)
return UnmarshalNext(dec, pb)
} }
// UnmarshalString will populate the fields of a protocol buffer based // UnmarshalString will populate the fields of a protocol buffer based
// on a JSON string. This function is lenient and will decode any options // on a JSON string. This function is lenient and will decode any options
// permutations of the related Marshaler. // permutations of the related Marshaler.
func UnmarshalString(str string, pb proto.Message) error { func UnmarshalString(str string, pb proto.Message) error {
return Unmarshal(strings.NewReader(str), pb) return new(Unmarshaler).Unmarshal(strings.NewReader(str), pb)
} }
// unmarshalValue converts/copies a value into the target. // unmarshalValue converts/copies a value into the target.
// prop may be nil. // prop may be nil.
func unmarshalValue(target reflect.Value, inputValue json.RawMessage, prop *proto.Properties) error { func (u *Unmarshaler) unmarshalValue(target reflect.Value, inputValue json.RawMessage, prop *proto.Properties) error {
targetType := target.Type() targetType := target.Type()
// Allocate memory for pointer fields. // Allocate memory for pointer fields.
if targetType.Kind() == reflect.Ptr { if targetType.Kind() == reflect.Ptr {
target.Set(reflect.New(targetType.Elem())) target.Set(reflect.New(targetType.Elem()))
return unmarshalValue(target.Elem(), inputValue, prop) return u.unmarshalValue(target.Elem(), inputValue, prop)
} }
// Handle well-known types. // Handle well-known types.
@ -559,7 +581,7 @@ func unmarshalValue(target reflect.Value, inputValue json.RawMessage, prop *prot
// as the wrapped primitive type, except that null is allowed." // as the wrapped primitive type, except that null is allowed."
// encoding/json will turn JSON `null` into Go `nil`, // encoding/json will turn JSON `null` into Go `nil`,
// so we don't have to do any extra work. // so we don't have to do any extra work.
return unmarshalValue(target.Field(0), inputValue, prop) return u.unmarshalValue(target.Field(0), inputValue, prop)
case "Any": case "Any":
return fmt.Errorf("unmarshaling Any not supported yet") return fmt.Errorf("unmarshaling Any not supported yet")
case "Duration": case "Duration":
@ -657,7 +679,7 @@ func unmarshalValue(target reflect.Value, inputValue json.RawMessage, prop *prot
continue continue
} }
if err := unmarshalValue(target.Field(i), valueForField, sprops.Prop[i]); err != nil { if err := u.unmarshalValue(target.Field(i), valueForField, sprops.Prop[i]); err != nil {
return err return err
} }
} }
@ -670,12 +692,12 @@ func unmarshalValue(target reflect.Value, inputValue json.RawMessage, prop *prot
} }
nv := reflect.New(oop.Type.Elem()) nv := reflect.New(oop.Type.Elem())
target.Field(oop.Field).Set(nv) target.Field(oop.Field).Set(nv)
if err := unmarshalValue(nv.Elem().Field(0), raw, oop.Prop); err != nil { if err := u.unmarshalValue(nv.Elem().Field(0), raw, oop.Prop); err != nil {
return err return err
} }
} }
} }
if len(jsonFields) > 0 { if !u.AllowUnknownFields && len(jsonFields) > 0 {
// Pick any field to be the scapegoat. // Pick any field to be the scapegoat.
var f string var f string
for fname := range jsonFields { for fname := range jsonFields {
@ -696,7 +718,7 @@ func unmarshalValue(target reflect.Value, inputValue json.RawMessage, prop *prot
len := len(slc) len := len(slc)
target.Set(reflect.MakeSlice(targetType, len, len)) target.Set(reflect.MakeSlice(targetType, len, len))
for i := 0; i < len; i++ { for i := 0; i < len; i++ {
if err := unmarshalValue(target.Index(i), slc[i], prop); err != nil { if err := u.unmarshalValue(target.Index(i), slc[i], prop); err != nil {
return err return err
} }
} }
@ -725,14 +747,14 @@ func unmarshalValue(target reflect.Value, inputValue json.RawMessage, prop *prot
k = reflect.ValueOf(ks) k = reflect.ValueOf(ks)
} else { } else {
k = reflect.New(targetType.Key()).Elem() k = reflect.New(targetType.Key()).Elem()
if err := unmarshalValue(k, json.RawMessage(ks), keyprop); err != nil { if err := u.unmarshalValue(k, json.RawMessage(ks), keyprop); err != nil {
return err return err
} }
} }
// Unmarshal map value. // Unmarshal map value.
v := reflect.New(targetType.Elem()).Elem() v := reflect.New(targetType.Elem()).Elem()
if err := unmarshalValue(v, raw, valprop); err != nil { if err := u.unmarshalValue(v, raw, valprop); err != nil {
return err return err
} }
target.SetMapIndex(k, v) target.SetMapIndex(k, v)

View file

@ -36,6 +36,7 @@ import (
"encoding/json" "encoding/json"
"io" "io"
"reflect" "reflect"
"strings"
"testing" "testing"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
@ -410,66 +411,69 @@ func TestMarshaling(t *testing.T) {
} }
var unmarshalingTests = []struct { var unmarshalingTests = []struct {
desc string desc string
json string unmarshaler Unmarshaler
pb proto.Message json string
pb proto.Message
}{ }{
{"simple flat object", simpleObjectJSON, simpleObject}, {"simple flat object", Unmarshaler{}, simpleObjectJSON, simpleObject},
{"simple pretty object", simpleObjectPrettyJSON, simpleObject}, {"simple pretty object", Unmarshaler{}, simpleObjectPrettyJSON, simpleObject},
{"repeated fields flat object", repeatsObjectJSON, repeatsObject}, {"repeated fields flat object", Unmarshaler{}, repeatsObjectJSON, repeatsObject},
{"repeated fields pretty object", repeatsObjectPrettyJSON, repeatsObject}, {"repeated fields pretty object", Unmarshaler{}, repeatsObjectPrettyJSON, repeatsObject},
{"nested message/enum flat object", complexObjectJSON, complexObject}, {"nested message/enum flat object", Unmarshaler{}, complexObjectJSON, complexObject},
{"nested message/enum pretty object", complexObjectPrettyJSON, complexObject}, {"nested message/enum pretty object", Unmarshaler{}, complexObjectPrettyJSON, complexObject},
{"enum-string object", `{"color":"BLUE"}`, &pb.Widget{Color: pb.Widget_BLUE.Enum()}}, {"enum-string object", Unmarshaler{}, `{"color":"BLUE"}`, &pb.Widget{Color: pb.Widget_BLUE.Enum()}},
{"enum-value object", "{\n \"color\": 2\n}", &pb.Widget{Color: pb.Widget_BLUE.Enum()}}, {"enum-value object", Unmarshaler{}, "{\n \"color\": 2\n}", &pb.Widget{Color: pb.Widget_BLUE.Enum()}},
{"proto3 enum string", `{"hilarity":"PUNS"}`, &proto3pb.Message{Hilarity: proto3pb.Message_PUNS}}, {"unknown field with allowed option", Unmarshaler{AllowUnknownFields: true}, `{"unknown": "foo"}`, new(pb.Simple)},
{"proto3 enum value", `{"hilarity":1}`, &proto3pb.Message{Hilarity: proto3pb.Message_PUNS}}, {"proto3 enum string", Unmarshaler{}, `{"hilarity":"PUNS"}`, &proto3pb.Message{Hilarity: proto3pb.Message_PUNS}},
{"proto3 enum value", Unmarshaler{}, `{"hilarity":1}`, &proto3pb.Message{Hilarity: proto3pb.Message_PUNS}},
{"unknown enum value object", {"unknown enum value object",
Unmarshaler{},
"{\n \"color\": 1000,\n \"r_color\": [\n \"RED\"\n ]\n}", "{\n \"color\": 1000,\n \"r_color\": [\n \"RED\"\n ]\n}",
&pb.Widget{Color: pb.Widget_Color(1000).Enum(), RColor: []pb.Widget_Color{pb.Widget_RED}}}, &pb.Widget{Color: pb.Widget_Color(1000).Enum(), RColor: []pb.Widget_Color{pb.Widget_RED}}},
{"repeated proto3 enum", `{"rFunny":["PUNS","SLAPSTICK"]}`, {"repeated proto3 enum", Unmarshaler{}, `{"rFunny":["PUNS","SLAPSTICK"]}`,
&proto3pb.Message{RFunny: []proto3pb.Message_Humour{ &proto3pb.Message{RFunny: []proto3pb.Message_Humour{
proto3pb.Message_PUNS, proto3pb.Message_PUNS,
proto3pb.Message_SLAPSTICK, proto3pb.Message_SLAPSTICK,
}}}, }}},
{"repeated proto3 enum as int", `{"rFunny":[1,2]}`, {"repeated proto3 enum as int", Unmarshaler{}, `{"rFunny":[1,2]}`,
&proto3pb.Message{RFunny: []proto3pb.Message_Humour{ &proto3pb.Message{RFunny: []proto3pb.Message_Humour{
proto3pb.Message_PUNS, proto3pb.Message_PUNS,
proto3pb.Message_SLAPSTICK, proto3pb.Message_SLAPSTICK,
}}}, }}},
{"repeated proto3 enum as mix of strings and ints", `{"rFunny":["PUNS",2]}`, {"repeated proto3 enum as mix of strings and ints", Unmarshaler{}, `{"rFunny":["PUNS",2]}`,
&proto3pb.Message{RFunny: []proto3pb.Message_Humour{ &proto3pb.Message{RFunny: []proto3pb.Message_Humour{
proto3pb.Message_PUNS, proto3pb.Message_PUNS,
proto3pb.Message_SLAPSTICK, proto3pb.Message_SLAPSTICK,
}}}, }}},
{"unquoted int64 object", `{"oInt64":-314}`, &pb.Simple{OInt64: proto.Int64(-314)}}, {"unquoted int64 object", Unmarshaler{}, `{"oInt64":-314}`, &pb.Simple{OInt64: proto.Int64(-314)}},
{"unquoted uint64 object", `{"oUint64":123}`, &pb.Simple{OUint64: proto.Uint64(123)}}, {"unquoted uint64 object", Unmarshaler{}, `{"oUint64":123}`, &pb.Simple{OUint64: proto.Uint64(123)}},
{"map<int64, int32>", `{"nummy":{"1":2,"3":4}}`, &pb.Mappy{Nummy: map[int64]int32{1: 2, 3: 4}}}, {"map<int64, int32>", Unmarshaler{}, `{"nummy":{"1":2,"3":4}}`, &pb.Mappy{Nummy: map[int64]int32{1: 2, 3: 4}}},
{"map<string, string>", `{"strry":{"\"one\"":"two","three":"four"}}`, &pb.Mappy{Strry: map[string]string{`"one"`: "two", "three": "four"}}}, {"map<string, string>", Unmarshaler{}, `{"strry":{"\"one\"":"two","three":"four"}}`, &pb.Mappy{Strry: map[string]string{`"one"`: "two", "three": "four"}}},
{"map<int32, Object>", `{"objjy":{"1":{"dub":1}}}`, &pb.Mappy{Objjy: map[int32]*pb.Simple3{1: &pb.Simple3{Dub: 1}}}}, {"map<int32, Object>", Unmarshaler{}, `{"objjy":{"1":{"dub":1}}}`, &pb.Mappy{Objjy: map[int32]*pb.Simple3{1: &pb.Simple3{Dub: 1}}}},
// TODO: This is broken. // TODO: This is broken.
//{"map<string, enum>", `{"enumy":{"XIV":"ROMAN"}`, &pb.Mappy{Enumy: map[string]pb.Numeral{"XIV": pb.Numeral_ROMAN}}}, //{"map<string, enum>", Unmarshaler{}, `{"enumy":{"XIV":"ROMAN"}`, &pb.Mappy{Enumy: map[string]pb.Numeral{"XIV": pb.Numeral_ROMAN}}},
{"map<string, enum as int>", `{"enumy":{"XIV":2}}`, &pb.Mappy{Enumy: map[string]pb.Numeral{"XIV": pb.Numeral_ROMAN}}}, {"map<string, enum as int>", Unmarshaler{}, `{"enumy":{"XIV":2}}`, &pb.Mappy{Enumy: map[string]pb.Numeral{"XIV": pb.Numeral_ROMAN}}},
{"oneof", `{"salary":31000}`, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_Salary{31000}}}, {"oneof", Unmarshaler{}, `{"salary":31000}`, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_Salary{31000}}},
{"oneof spec name", `{"country":"Australia"}`, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_Country{"Australia"}}}, {"oneof spec name", Unmarshaler{}, `{"country":"Australia"}`, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_Country{"Australia"}}},
{"oneof orig_name", `{"Country":"Australia"}`, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_Country{"Australia"}}}, {"oneof orig_name", Unmarshaler{}, `{"Country":"Australia"}`, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_Country{"Australia"}}},
{"orig_name input", `{"o_bool":true}`, &pb.Simple{OBool: proto.Bool(true)}}, {"orig_name input", Unmarshaler{}, `{"o_bool":true}`, &pb.Simple{OBool: proto.Bool(true)}},
{"camelName input", `{"oBool":true}`, &pb.Simple{OBool: proto.Bool(true)}}, {"camelName input", Unmarshaler{}, `{"oBool":true}`, &pb.Simple{OBool: proto.Bool(true)}},
{"Duration", `{"dur":"3.000s"}`, &pb.KnownTypes{Dur: &durpb.Duration{Seconds: 3}}}, {"Duration", Unmarshaler{}, `{"dur":"3.000s"}`, &pb.KnownTypes{Dur: &durpb.Duration{Seconds: 3}}},
{"Timestamp", `{"ts":"2014-05-13T16:53:20.021Z"}`, &pb.KnownTypes{Ts: &tspb.Timestamp{Seconds: 14e8, Nanos: 21e6}}}, {"Timestamp", Unmarshaler{}, `{"ts":"2014-05-13T16:53:20.021Z"}`, &pb.KnownTypes{Ts: &tspb.Timestamp{Seconds: 14e8, Nanos: 21e6}}},
{"DoubleValue", `{"dbl":1.2}`, &pb.KnownTypes{Dbl: &wpb.DoubleValue{Value: 1.2}}}, {"DoubleValue", Unmarshaler{}, `{"dbl":1.2}`, &pb.KnownTypes{Dbl: &wpb.DoubleValue{Value: 1.2}}},
{"FloatValue", `{"flt":1.2}`, &pb.KnownTypes{Flt: &wpb.FloatValue{Value: 1.2}}}, {"FloatValue", Unmarshaler{}, `{"flt":1.2}`, &pb.KnownTypes{Flt: &wpb.FloatValue{Value: 1.2}}},
{"Int64Value", `{"i64":"-3"}`, &pb.KnownTypes{I64: &wpb.Int64Value{Value: -3}}}, {"Int64Value", Unmarshaler{}, `{"i64":"-3"}`, &pb.KnownTypes{I64: &wpb.Int64Value{Value: -3}}},
{"UInt64Value", `{"u64":"3"}`, &pb.KnownTypes{U64: &wpb.UInt64Value{Value: 3}}}, {"UInt64Value", Unmarshaler{}, `{"u64":"3"}`, &pb.KnownTypes{U64: &wpb.UInt64Value{Value: 3}}},
{"Int32Value", `{"i32":-4}`, &pb.KnownTypes{I32: &wpb.Int32Value{Value: -4}}}, {"Int32Value", Unmarshaler{}, `{"i32":-4}`, &pb.KnownTypes{I32: &wpb.Int32Value{Value: -4}}},
{"UInt32Value", `{"u32":4}`, &pb.KnownTypes{U32: &wpb.UInt32Value{Value: 4}}}, {"UInt32Value", Unmarshaler{}, `{"u32":4}`, &pb.KnownTypes{U32: &wpb.UInt32Value{Value: 4}}},
{"BoolValue", `{"bool":true}`, &pb.KnownTypes{Bool: &wpb.BoolValue{Value: true}}}, {"BoolValue", Unmarshaler{}, `{"bool":true}`, &pb.KnownTypes{Bool: &wpb.BoolValue{Value: true}}},
{"StringValue", `{"str":"plush"}`, &pb.KnownTypes{Str: &wpb.StringValue{Value: "plush"}}}, {"StringValue", Unmarshaler{}, `{"str":"plush"}`, &pb.KnownTypes{Str: &wpb.StringValue{Value: "plush"}}},
{"BytesValue", `{"bytes":"d293"}`, &pb.KnownTypes{Bytes: &wpb.BytesValue{Value: []byte("wow")}}}, {"BytesValue", Unmarshaler{}, `{"bytes":"d293"}`, &pb.KnownTypes{Bytes: &wpb.BytesValue{Value: []byte("wow")}}},
// `null` is also a permissible value. Let's just test one. // `null` is also a permissible value. Let's just test one.
{"null DoubleValue", `{"dbl":null}`, &pb.KnownTypes{Dbl: &wpb.DoubleValue{}}}, {"null DoubleValue", Unmarshaler{}, `{"dbl":null}`, &pb.KnownTypes{Dbl: &wpb.DoubleValue{}}},
} }
func TestUnmarshaling(t *testing.T) { func TestUnmarshaling(t *testing.T) {
@ -477,7 +481,7 @@ func TestUnmarshaling(t *testing.T) {
// Make a new instance of the type of our expected object. // Make a new instance of the type of our expected object.
p := reflect.New(reflect.TypeOf(tt.pb).Elem()).Interface().(proto.Message) p := reflect.New(reflect.TypeOf(tt.pb).Elem()).Interface().(proto.Message)
err := UnmarshalString(tt.json, p) err := tt.unmarshaler.Unmarshal(strings.NewReader(tt.json), p)
if err != nil { if err != nil {
t.Errorf("%s: %v", tt.desc, err) t.Errorf("%s: %v", tt.desc, err)
continue continue
@ -507,7 +511,7 @@ func TestUnmarshalNext(t *testing.T) {
// Make a new instance of the type of our expected object. // Make a new instance of the type of our expected object.
p := reflect.New(reflect.TypeOf(tt.pb).Elem()).Interface().(proto.Message) p := reflect.New(reflect.TypeOf(tt.pb).Elem()).Interface().(proto.Message)
err := UnmarshalNext(dec, p) err := tt.unmarshaler.UnmarshalNext(dec, p)
if err != nil { if err != nil {
t.Errorf("%s: %v", tt.desc, err) t.Errorf("%s: %v", tt.desc, err)
continue continue
@ -522,7 +526,7 @@ func TestUnmarshalNext(t *testing.T) {
} }
p := &pb.Simple{} p := &pb.Simple{}
err := UnmarshalNext(dec, p) err := new(Unmarshaler).UnmarshalNext(dec, p)
if err != io.EOF { if err != io.EOF {
t.Errorf("eof: got %v, expected io.EOF", err) t.Errorf("eof: got %v, expected io.EOF", err)
} }
@ -535,6 +539,7 @@ var unmarshalingShouldError = []struct {
}{ }{
{"a value", "666", new(pb.Simple)}, {"a value", "666", new(pb.Simple)},
{"gibberish", "{adskja123;l23=-=", new(pb.Simple)}, {"gibberish", "{adskja123;l23=-=", new(pb.Simple)},
{"unknown field", `{"unknown": "foo"}`, new(pb.Simple)},
{"unknown enum name", `{"hilarity":"DAVE"}`, new(proto3pb.Message)}, {"unknown enum name", `{"hilarity":"DAVE"}`, new(proto3pb.Message)},
} }

View file

@ -195,6 +195,9 @@ var mergeTests = []struct {
NameMapping: map[int32]string{6: "Nigel"}, NameMapping: map[int32]string{6: "Nigel"},
MsgMapping: map[int64]*pb.FloatingPoint{ MsgMapping: map[int64]*pb.FloatingPoint{
0x4001: &pb.FloatingPoint{F: proto.Float64(2.0)}, 0x4001: &pb.FloatingPoint{F: proto.Float64(2.0)},
0x4002: &pb.FloatingPoint{
F: proto.Float64(2.0),
},
}, },
ByteMapping: map[bool][]byte{true: []byte("wowsa")}, ByteMapping: map[bool][]byte{true: []byte("wowsa")},
}, },
@ -203,6 +206,12 @@ var mergeTests = []struct {
6: "Bruce", // should be overwritten 6: "Bruce", // should be overwritten
7: "Andrew", 7: "Andrew",
}, },
MsgMapping: map[int64]*pb.FloatingPoint{
0x4002: &pb.FloatingPoint{
F: proto.Float64(3.0),
Exact: proto.Bool(true),
}, // the entire message should be overwritten
},
}, },
want: &pb.MessageWithMap{ want: &pb.MessageWithMap{
NameMapping: map[int32]string{ NameMapping: map[int32]string{
@ -211,6 +220,9 @@ var mergeTests = []struct {
}, },
MsgMapping: map[int64]*pb.FloatingPoint{ MsgMapping: map[int64]*pb.FloatingPoint{
0x4001: &pb.FloatingPoint{F: proto.Float64(2.0)}, 0x4001: &pb.FloatingPoint{F: proto.Float64(2.0)},
0x4002: &pb.FloatingPoint{
F: proto.Float64(2.0),
},
}, },
ByteMapping: map[bool][]byte{true: []byte("wowsa")}, ByteMapping: map[bool][]byte{true: []byte("wowsa")},
}, },
@ -254,6 +266,27 @@ var mergeTests = []struct {
Union: &pb.Communique_Name{"Bobby Tables"}, Union: &pb.Communique_Name{"Bobby Tables"},
}, },
}, },
{
src: &proto3pb.Message{
Terrain: map[string]*proto3pb.Nested{
"kay_a": &proto3pb.Nested{Cute: true}, // replace
"kay_b": &proto3pb.Nested{Bunny: "rabbit"}, // insert
},
},
dst: &proto3pb.Message{
Terrain: map[string]*proto3pb.Nested{
"kay_a": &proto3pb.Nested{Bunny: "lost"}, // replaced
"kay_c": &proto3pb.Nested{Bunny: "bunny"}, // keep
},
},
want: &proto3pb.Message{
Terrain: map[string]*proto3pb.Nested{
"kay_a": &proto3pb.Nested{Cute: true},
"kay_b": &proto3pb.Nested{Bunny: "rabbit"},
"kay_c": &proto3pb.Nested{Bunny: "bunny"},
},
},
},
} }
func TestMerge(t *testing.T) { func TestMerge(t *testing.T) {

View file

@ -489,6 +489,34 @@ func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, e
return return
} }
// ExtensionDescs returns a new slice containing pb's extension descriptors, in undefined order.
// For non-registered extensions, ExtensionDescs returns an incomplete descriptor containing
// just the Field field, which defines the extension's field number.
func ExtensionDescs(pb Message) ([]*ExtensionDesc, error) {
epb, ok := extendable(pb)
if !ok {
return nil, fmt.Errorf("proto: %T is not an extendable proto.Message", pb)
}
registeredExtensions := RegisteredExtensions(pb)
emap, mu := epb.extensionsRead()
mu.Lock()
defer mu.Unlock()
extensions := make([]*ExtensionDesc, 0, len(emap))
for extid, e := range emap {
desc := e.desc
if desc == nil {
desc = registeredExtensions[extid]
if desc == nil {
desc = &ExtensionDesc{Field: extid}
}
}
extensions = append(extensions, desc)
}
return extensions, nil
}
// SetExtension sets the specified extension of pb to the specified value. // SetExtension sets the specified extension of pb to the specified value.
func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error { func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error {
epb, ok := extendable(pb) epb, ok := extendable(pb)

View file

@ -35,6 +35,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"reflect" "reflect"
"sort"
"testing" "testing"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
@ -45,7 +46,7 @@ func TestGetExtensionsWithMissingExtensions(t *testing.T) {
msg := &pb.MyMessage{} msg := &pb.MyMessage{}
ext1 := &pb.Ext{} ext1 := &pb.Ext{}
if err := proto.SetExtension(msg, pb.E_Ext_More, ext1); err != nil { if err := proto.SetExtension(msg, pb.E_Ext_More, ext1); err != nil {
t.Fatalf("Could not set ext1: %s", ext1) t.Fatalf("Could not set ext1: %s", err)
} }
exts, err := proto.GetExtensions(msg, []*proto.ExtensionDesc{ exts, err := proto.GetExtensions(msg, []*proto.ExtensionDesc{
pb.E_Ext_More, pb.E_Ext_More,
@ -62,6 +63,54 @@ func TestGetExtensionsWithMissingExtensions(t *testing.T) {
} }
} }
func TestExtensionDescsWithMissingExtensions(t *testing.T) {
msg := &pb.MyMessage{Count: proto.Int32(0)}
extdesc1 := pb.E_Ext_More
ext1 := &pb.Ext{}
if err := proto.SetExtension(msg, extdesc1, ext1); err != nil {
t.Fatalf("Could not set ext1: %s", err)
}
extdesc2 := &proto.ExtensionDesc{
ExtendedType: (*pb.MyMessage)(nil),
ExtensionType: (*bool)(nil),
Field: 123456789,
Name: "a.b",
Tag: "varint,123456789,opt",
}
ext2 := proto.Bool(false)
if err := proto.SetExtension(msg, extdesc2, ext2); err != nil {
t.Fatalf("Could not set ext2: %s", err)
}
b, err := proto.Marshal(msg)
if err != nil {
t.Fatalf("Could not marshal msg: %v", err)
}
if err := proto.Unmarshal(b, msg); err != nil {
t.Fatalf("Could not unmarshal into msg: %v", err)
}
descs, err := proto.ExtensionDescs(msg)
if err != nil {
t.Fatalf("proto.ExtensionDescs: got error %v", err)
}
sortExtDescs(descs)
wantDescs := []*proto.ExtensionDesc{extdesc1, &proto.ExtensionDesc{Field: extdesc2.Field}}
if !reflect.DeepEqual(descs, wantDescs) {
t.Errorf("proto.ExtensionDescs(msg) sorted extension ids: got %+v, want %+v", descs, wantDescs)
}
}
type ExtensionDescSlice []*proto.ExtensionDesc
func (s ExtensionDescSlice) Len() int { return len(s) }
func (s ExtensionDescSlice) Less(i, j int) bool { return s[i].Field < s[j].Field }
func (s ExtensionDescSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func sortExtDescs(s []*proto.ExtensionDesc) {
sort.Sort(ExtensionDescSlice(s))
}
func TestGetExtensionStability(t *testing.T) { func TestGetExtensionStability(t *testing.T) {
check := func(m *pb.MyMessage) bool { check := func(m *pb.MyMessage) bool {
ext1, err := proto.GetExtension(m, pb.E_Ext_More) ext1, err := proto.GetExtension(m, pb.E_Ext_More)

View file

@ -124,6 +124,7 @@ func (m *Message) GetManyThings() []*google_protobuf.Any {
type Nested struct { type Nested struct {
Bunny string `protobuf:"bytes,1,opt,name=bunny" json:"bunny,omitempty"` Bunny string `protobuf:"bytes,1,opt,name=bunny" json:"bunny,omitempty"`
Cute bool `protobuf:"varint,2,opt,name=cute" json:"cute,omitempty"`
} }
func (m *Nested) Reset() { *m = Nested{} } func (m *Nested) Reset() { *m = Nested{} }
@ -155,44 +156,44 @@ func init() {
} }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 617 bytes of a gzipped FileDescriptorProto // 621 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x92, 0x5d, 0x6b, 0xdb, 0x3c, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x53, 0x5b, 0x6f, 0xd3, 0x4c,
0x14, 0xc7, 0x1f, 0xc5, 0xa9, 0x93, 0x1e, 0x3b, 0xad, 0xd1, 0xd3, 0x81, 0x1a, 0xc6, 0xf0, 0x32, 0x10, 0xfd, 0x72, 0xa9, 0xe3, 0x8e, 0x9d, 0xd6, 0xda, 0xaf, 0x48, 0xdb, 0x88, 0x87, 0x12, 0x24,
0x18, 0x66, 0x2f, 0xee, 0xc8, 0x28, 0x94, 0x31, 0x36, 0xda, 0xae, 0x65, 0xa1, 0x69, 0x16, 0x9c, 0x54, 0x71, 0x71, 0x51, 0x10, 0x52, 0x85, 0x10, 0xa8, 0x2d, 0xad, 0x88, 0x9a, 0x86, 0x68, 0xd3,
0x76, 0x65, 0x57, 0x46, 0x49, 0x95, 0xc4, 0x2c, 0x96, 0x83, 0x2d, 0x0f, 0xfc, 0x75, 0xf6, 0x29, 0x52, 0xf1, 0x64, 0xad, 0xd3, 0x4d, 0x62, 0x11, 0xaf, 0x23, 0x7b, 0x8d, 0xe4, 0xbf, 0xc3, 0xaf,
0x77, 0x39, 0x24, 0x39, 0xa9, 0x5b, 0xb2, 0xed, 0xca, 0xd2, 0xf1, 0xef, 0xbc, 0xe8, 0xff, 0x3f, 0xe4, 0x91, 0xbd, 0x38, 0xad, 0x5b, 0x05, 0x78, 0xf2, 0xee, 0xcc, 0x39, 0x33, 0xb3, 0xe7, 0x8c,
0xb0, 0xbf, 0x4c, 0x13, 0x91, 0xbc, 0x0d, 0xd5, 0xe7, 0x40, 0x5f, 0x7c, 0xf5, 0xc1, 0x76, 0xf5, 0x61, 0x77, 0x99, 0x26, 0x22, 0x79, 0x13, 0xe8, 0xcf, 0x81, 0xb9, 0xf8, 0xfa, 0x83, 0xdc, 0x6a,
0x57, 0x7b, 0x7f, 0x96, 0x24, 0xb3, 0x05, 0xd3, 0xc8, 0x38, 0x9f, 0x1e, 0x50, 0x5e, 0x68, 0xb0, 0xaa, 0xb3, 0x3b, 0x4b, 0x92, 0xd9, 0x82, 0x19, 0x48, 0x98, 0x4f, 0x0f, 0x28, 0x2f, 0x0c, 0xb0,
0xfd, 0xbf, 0x60, 0x99, 0xb8, 0xa5, 0x82, 0x1e, 0xc8, 0x83, 0x0e, 0x76, 0x7e, 0x99, 0xd0, 0xb8, 0xf3, 0xbf, 0x60, 0x99, 0xb8, 0xa1, 0x82, 0x1e, 0xa8, 0x83, 0x09, 0x76, 0x7f, 0x59, 0xd0, 0xba,
0x64, 0x59, 0x46, 0x67, 0x0c, 0x63, 0xa8, 0x73, 0x1a, 0x33, 0x82, 0x5c, 0xe4, 0x6d, 0x07, 0xea, 0x60, 0x59, 0x46, 0x67, 0x0c, 0x21, 0x68, 0x72, 0x1a, 0x33, 0x5c, 0xdb, 0xab, 0xed, 0x6f, 0x12,
0x8c, 0x8f, 0xa0, 0x39, 0x8f, 0x16, 0x34, 0x8d, 0x44, 0x41, 0x6a, 0x2e, 0xf2, 0x76, 0xba, 0x8f, 0x7d, 0x46, 0x87, 0x60, 0xcf, 0xa3, 0x05, 0x4d, 0x23, 0x51, 0xe0, 0xba, 0x8c, 0x6f, 0xf5, 0x1e,
0xfd, 0x6a, 0x43, 0xbf, 0x4c, 0xf6, 0x3f, 0xe7, 0x71, 0x92, 0xa7, 0xc1, 0x9a, 0xc6, 0x2e, 0xd8, 0xfb, 0xd5, 0x86, 0x7e, 0x49, 0xf6, 0x3f, 0xe7, 0x71, 0x92, 0xa7, 0xe4, 0x16, 0x8d, 0xf6, 0xc0,
0x73, 0x16, 0xcd, 0xe6, 0x22, 0x8c, 0x78, 0x38, 0x89, 0x89, 0xe1, 0x22, 0xaf, 0x15, 0x80, 0x8e, 0x9d, 0xb3, 0x68, 0x36, 0x17, 0x41, 0xc4, 0x83, 0x49, 0x8c, 0x1b, 0x92, 0xdd, 0x26, 0x60, 0x62,
0xf5, 0xf8, 0x69, 0x2c, 0xfb, 0xc9, 0x71, 0x48, 0xdd, 0x45, 0x9e, 0x1d, 0xa8, 0x33, 0x7e, 0x0a, 0x7d, 0x7e, 0x12, 0xab, 0x7e, 0x6a, 0x1c, 0xdc, 0x94, 0x19, 0x97, 0xe8, 0x33, 0x7a, 0x02, 0x6e,
0x76, 0xca, 0xb2, 0x7c, 0x21, 0xc2, 0x49, 0x92, 0x73, 0x41, 0x1a, 0x2e, 0xf2, 0x8c, 0xc0, 0xd2, 0xca, 0xb2, 0x7c, 0x21, 0x82, 0x49, 0x92, 0x73, 0x81, 0x5b, 0x32, 0xd7, 0x20, 0x8e, 0x89, 0x9d,
0xb1, 0x53, 0x19, 0xc2, 0xcf, 0xa0, 0x25, 0xd2, 0x9c, 0x85, 0xd9, 0x24, 0x11, 0x59, 0x4c, 0x39, 0xa8, 0x10, 0x7a, 0x0a, 0x6d, 0x91, 0xe6, 0x2c, 0xc8, 0x26, 0x89, 0xc8, 0x62, 0xca, 0xb1, 0x2d,
0x69, 0xba, 0xc8, 0x6b, 0x06, 0xb6, 0x0c, 0x8e, 0xca, 0x18, 0xde, 0x83, 0xad, 0x6c, 0x92, 0xa4, 0x31, 0x36, 0x71, 0x55, 0x70, 0x5c, 0xc6, 0xd0, 0x0e, 0x6c, 0xc8, 0x7c, 0xca, 0xf0, 0xa6, 0x4c,
0x8c, 0x6c, 0xbb, 0xc8, 0xab, 0x05, 0xfa, 0x82, 0x1d, 0x30, 0xbe, 0xb3, 0x82, 0x6c, 0xb9, 0x86, 0xd6, 0x89, 0xb9, 0x20, 0x0f, 0x1a, 0xdf, 0x59, 0x81, 0x37, 0xf6, 0x1a, 0xfb, 0x4d, 0xa2, 0x8e,
0x57, 0x0f, 0xe4, 0x11, 0xbf, 0x02, 0x93, 0xb3, 0x4c, 0xb0, 0x5b, 0x62, 0xba, 0xc8, 0xb3, 0xba, 0xe8, 0x25, 0x58, 0x5c, 0xaa, 0xc1, 0x6e, 0xb0, 0x25, 0x81, 0x4e, 0x6f, 0xe7, 0xfe, 0xeb, 0x86,
0x7b, 0xf7, 0x5f, 0x37, 0x50, 0xff, 0x82, 0x92, 0xc1, 0x87, 0xd0, 0x48, 0xc3, 0x69, 0xce, 0x79, 0x3a, 0x47, 0x4a, 0x0c, 0x7a, 0x0b, 0xad, 0x34, 0x98, 0xe6, 0x9c, 0x17, 0xd8, 0x93, 0x35, 0xfe,
0x41, 0x1c, 0xd7, 0xf8, 0xa7, 0x18, 0x66, 0x7a, 0x2e, 0x59, 0xfc, 0x1e, 0x1a, 0x82, 0xa5, 0x29, 0x25, 0x86, 0x95, 0x9e, 0x29, 0x2c, 0x7a, 0x0f, 0x2d, 0xc1, 0xd2, 0x94, 0x46, 0x1c, 0x83, 0xa4,
0x8d, 0x38, 0x01, 0xd7, 0xf0, 0xac, 0x6e, 0x67, 0x73, 0xda, 0x95, 0x86, 0xce, 0xb8, 0x48, 0x8b, 0x39, 0xbd, 0xee, 0x7a, 0xda, 0xa5, 0x01, 0x9d, 0x72, 0x91, 0x16, 0x64, 0x45, 0x91, 0x16, 0x18,
0x60, 0x95, 0x82, 0x8f, 0x40, 0x5b, 0xdc, 0x0d, 0xa7, 0x11, 0x5b, 0xdc, 0x12, 0x4b, 0x0d, 0xfa, 0x8b, 0x7b, 0xc1, 0x34, 0x62, 0x8b, 0x1b, 0xec, 0xe8, 0x41, 0x1f, 0xf9, 0x2b, 0x3b, 0xfd, 0x71,
0xc8, 0x5f, 0xd9, 0xe9, 0x8f, 0xf2, 0xf1, 0x27, 0x36, 0xa5, 0xf9, 0x42, 0x64, 0x81, 0xa5, 0xd1, 0x1e, 0x7e, 0x62, 0x53, 0x2a, 0x05, 0xca, 0x88, 0x63, 0xa0, 0x67, 0x0a, 0x89, 0xfa, 0xb7, 0xcc,
0x73, 0x49, 0xe2, 0xde, 0x3a, 0xf3, 0x07, 0x5d, 0xe4, 0x8c, 0xb4, 0x54, 0xf3, 0xe7, 0x9b, 0x9b, 0x1f, 0x74, 0x91, 0x33, 0xdc, 0xd6, 0xcd, 0x9f, 0xad, 0x6f, 0x3e, 0xd2, 0xc8, 0xaf, 0x0a, 0x68,
0x0f, 0x15, 0xf9, 0x55, 0x82, 0x7a, 0x80, 0xb2, 0x94, 0x8a, 0xe0, 0x37, 0xd0, 0xa4, 0xbc, 0x10, 0x06, 0x28, 0x4b, 0xe9, 0x08, 0x7a, 0x0d, 0xb6, 0xdc, 0x24, 0x31, 0x8f, 0xf8, 0x0c, 0x6f, 0x95,
0xf3, 0x88, 0xcf, 0xc8, 0x4e, 0xa9, 0x94, 0x5e, 0x35, 0x7f, 0xb5, 0x6a, 0xfe, 0x31, 0x2f, 0x82, 0x4a, 0x99, 0x55, 0xf3, 0x57, 0xab, 0xe6, 0x1f, 0xf1, 0x82, 0xdc, 0xa2, 0xa4, 0x56, 0x8e, 0x34,
0x35, 0x85, 0x0f, 0xc1, 0x8a, 0x29, 0x2f, 0x42, 0x75, 0xcb, 0xc8, 0xae, 0xea, 0xbd, 0x39, 0x09, 0xa2, 0x08, 0xf4, 0x2d, 0xc3, 0xdb, 0xba, 0xf7, 0x7a, 0x12, 0x28, 0xe0, 0xa5, 0xc6, 0x75, 0x46,
0x24, 0x78, 0xa5, 0xb8, 0xf6, 0x10, 0xec, 0xaa, 0x0c, 0x2b, 0xcb, 0xf4, 0x4e, 0x2a, 0xcb, 0x5e, 0xe0, 0x56, 0x65, 0x58, 0x59, 0x66, 0x76, 0x52, 0x5b, 0xf6, 0x1c, 0x36, 0xcc, 0x73, 0xea, 0x7f,
0xc0, 0x96, 0x7e, 0x4e, 0xed, 0x2f, 0x8e, 0x69, 0xe4, 0x5d, 0xed, 0x08, 0xb5, 0xaf, 0xc1, 0x79, 0x71, 0xcc, 0x40, 0xde, 0xd5, 0x0f, 0x6b, 0x9d, 0x2b, 0xf0, 0x1e, 0xbe, 0x6d, 0x4d, 0xd5, 0x17,
0xf8, 0xb6, 0x0d, 0x55, 0x5f, 0xde, 0xaf, 0xfa, 0x07, 0x79, 0xef, 0xca, 0x76, 0x3e, 0x82, 0xa9, 0xf7, 0xab, 0xfe, 0x41, 0xde, 0xbb, 0xb2, 0xdd, 0x8f, 0x60, 0x19, 0x9b, 0x91, 0x03, 0xad, 0xab,
0x6d, 0xc6, 0x16, 0x34, 0xae, 0x07, 0x17, 0x83, 0x2f, 0x37, 0x03, 0xe7, 0x3f, 0xdc, 0x84, 0xfa, 0xe1, 0xf9, 0xf0, 0xcb, 0xf5, 0xd0, 0xfb, 0x0f, 0xd9, 0xd0, 0x1c, 0x5d, 0x0d, 0xc7, 0x5e, 0x0d,
0xf0, 0x7a, 0x30, 0x72, 0x10, 0x6e, 0xc1, 0xf6, 0xa8, 0x7f, 0x3c, 0x1c, 0x5d, 0xf5, 0x4e, 0x2f, 0xb5, 0x61, 0x73, 0x3c, 0x38, 0x1a, 0x8d, 0x2f, 0xfb, 0x27, 0xe7, 0x5e, 0x1d, 0x6d, 0x83, 0x73,
0x9c, 0x1a, 0xde, 0x05, 0xeb, 0xa4, 0xd7, 0xef, 0x87, 0x27, 0xc7, 0xbd, 0xfe, 0xd9, 0x37, 0xc7, 0xdc, 0x1f, 0x0c, 0x82, 0xe3, 0xa3, 0xfe, 0xe0, 0xf4, 0x9b, 0xd7, 0xe8, 0xf6, 0xc0, 0x32, 0xc3,
0xe8, 0x3c, 0x01, 0x53, 0x0f, 0x2b, 0x97, 0x75, 0xac, 0x96, 0x4a, 0xcf, 0xa3, 0x2f, 0x9d, 0x9f, 0xaa, 0x65, 0x0d, 0xf5, 0x52, 0x99, 0x79, 0xcc, 0x45, 0xfd, 0x1e, 0x93, 0x5c, 0x98, 0x81, 0x6c,
0x08, 0x76, 0x4a, 0x73, 0x6e, 0x22, 0x31, 0xbf, 0xa4, 0x4b, 0x3c, 0x04, 0x7b, 0x5c, 0x08, 0x16, 0xa2, 0xcf, 0xdd, 0x9f, 0x35, 0xd8, 0x2a, 0x0d, 0xbb, 0x8e, 0xc4, 0xfc, 0x82, 0x2e, 0x91, 0x14,
0xc6, 0x74, 0xb9, 0x94, 0x4e, 0x20, 0x25, 0xea, 0xeb, 0x8d, 0x86, 0x96, 0x39, 0xfe, 0x49, 0x21, 0x2c, 0x2c, 0x04, 0x0b, 0x62, 0xba, 0x5c, 0x2a, 0x77, 0x6a, 0x5a, 0xe8, 0x57, 0x6b, 0x4d, 0x2e,
0xd8, 0xa5, 0xe6, 0x4b, 0x5f, 0xc7, 0x77, 0x91, 0xf6, 0x07, 0x70, 0x1e, 0x02, 0x55, 0x71, 0x9a, 0x39, 0xfe, 0xb1, 0x24, 0x5c, 0x18, 0x7c, 0xe9, 0x75, 0x78, 0x17, 0xe9, 0x7c, 0x00, 0xef, 0x21,
0x5a, 0x9c, 0xbd, 0xaa, 0x38, 0x76, 0x45, 0x85, 0xb1, 0xa9, 0x5b, 0xff, 0x0e, 0x00, 0x00, 0xff, 0xa0, 0x2a, 0x98, 0x6d, 0x04, 0xdb, 0xa9, 0x0a, 0xe6, 0x56, 0x94, 0x09, 0x2d, 0xd3, 0xfa, 0x77,
0xff, 0x54, 0x4a, 0xfa, 0x41, 0xa1, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x78, 0xc9, 0x75, 0x36, 0xb5, 0x04, 0x00, 0x00,
} }

View file

@ -66,6 +66,7 @@ message Message {
message Nested { message Nested {
string bunny = 1; string bunny = 1;
bool cute = 2;
} }
message MessageWithMap { message MessageWithMap {

View file

@ -53,9 +53,7 @@ var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file // This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against. // is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the const _ = proto.ProtoPackageIsVersion1
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type FOO int32 type FOO int32
@ -1268,12 +1266,12 @@ func (m *InnerMessage) GetConnected() bool {
} }
type OtherMessage struct { type OtherMessage struct {
Key *int64 `protobuf:"varint,1,opt,name=key" json:"key,omitempty"` Key *int64 `protobuf:"varint,1,opt,name=key" json:"key,omitempty"`
Value []byte `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"` Value []byte `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"`
Weight *float32 `protobuf:"fixed32,3,opt,name=weight" json:"weight,omitempty"` Weight *float32 `protobuf:"fixed32,3,opt,name=weight" json:"weight,omitempty"`
Inner *InnerMessage `protobuf:"bytes,4,opt,name=inner" json:"inner,omitempty"` Inner *InnerMessage `protobuf:"bytes,4,opt,name=inner" json:"inner,omitempty"`
proto.XXX_InternalExtensions `json:"-"` XXX_extensions map[int32]proto.Extension `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *OtherMessage) Reset() { *m = OtherMessage{} } func (m *OtherMessage) Reset() { *m = OtherMessage{} }
@ -1288,6 +1286,12 @@ var extRange_OtherMessage = []proto.ExtensionRange{
func (*OtherMessage) ExtensionRangeArray() []proto.ExtensionRange { func (*OtherMessage) ExtensionRangeArray() []proto.ExtensionRange {
return extRange_OtherMessage return extRange_OtherMessage
} }
func (m *OtherMessage) ExtensionMap() map[int32]proto.Extension {
if m.XXX_extensions == nil {
m.XXX_extensions = make(map[int32]proto.Extension)
}
return m.XXX_extensions
}
func (m *OtherMessage) GetKey() int64 { func (m *OtherMessage) GetKey() int64 {
if m != nil && m.Key != nil { if m != nil && m.Key != nil {
@ -1346,10 +1350,10 @@ type MyMessage struct {
Bikeshed *MyMessage_Color `protobuf:"varint,7,opt,name=bikeshed,enum=testdata.MyMessage_Color" json:"bikeshed,omitempty"` Bikeshed *MyMessage_Color `protobuf:"varint,7,opt,name=bikeshed,enum=testdata.MyMessage_Color" json:"bikeshed,omitempty"`
Somegroup *MyMessage_SomeGroup `protobuf:"group,8,opt,name=SomeGroup,json=somegroup" json:"somegroup,omitempty"` Somegroup *MyMessage_SomeGroup `protobuf:"group,8,opt,name=SomeGroup,json=somegroup" json:"somegroup,omitempty"`
// This field becomes [][]byte in the generated code. // This field becomes [][]byte in the generated code.
RepBytes [][]byte `protobuf:"bytes,10,rep,name=rep_bytes,json=repBytes" json:"rep_bytes,omitempty"` RepBytes [][]byte `protobuf:"bytes,10,rep,name=rep_bytes,json=repBytes" json:"rep_bytes,omitempty"`
Bigfloat *float64 `protobuf:"fixed64,11,opt,name=bigfloat" json:"bigfloat,omitempty"` Bigfloat *float64 `protobuf:"fixed64,11,opt,name=bigfloat" json:"bigfloat,omitempty"`
proto.XXX_InternalExtensions `json:"-"` XXX_extensions map[int32]proto.Extension `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *MyMessage) Reset() { *m = MyMessage{} } func (m *MyMessage) Reset() { *m = MyMessage{} }
@ -1364,6 +1368,12 @@ var extRange_MyMessage = []proto.ExtensionRange{
func (*MyMessage) ExtensionRangeArray() []proto.ExtensionRange { func (*MyMessage) ExtensionRangeArray() []proto.ExtensionRange {
return extRange_MyMessage return extRange_MyMessage
} }
func (m *MyMessage) ExtensionMap() map[int32]proto.Extension {
if m.XXX_extensions == nil {
m.XXX_extensions = make(map[int32]proto.Extension)
}
return m.XXX_extensions
}
func (m *MyMessage) GetCount() int32 { func (m *MyMessage) GetCount() int32 {
if m != nil && m.Count != nil { if m != nil && m.Count != nil {
@ -1541,8 +1551,8 @@ func (m *ComplexExtension) GetThird() []int32 {
} }
type DefaultsMessage struct { type DefaultsMessage struct {
proto.XXX_InternalExtensions `json:"-"` XXX_extensions map[int32]proto.Extension `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *DefaultsMessage) Reset() { *m = DefaultsMessage{} } func (m *DefaultsMessage) Reset() { *m = DefaultsMessage{} }
@ -1557,10 +1567,16 @@ var extRange_DefaultsMessage = []proto.ExtensionRange{
func (*DefaultsMessage) ExtensionRangeArray() []proto.ExtensionRange { func (*DefaultsMessage) ExtensionRangeArray() []proto.ExtensionRange {
return extRange_DefaultsMessage return extRange_DefaultsMessage
} }
func (m *DefaultsMessage) ExtensionMap() map[int32]proto.Extension {
if m.XXX_extensions == nil {
m.XXX_extensions = make(map[int32]proto.Extension)
}
return m.XXX_extensions
}
type MyMessageSet struct { type MyMessageSet struct {
proto.XXX_InternalExtensions `json:"-"` XXX_extensions map[int32]proto.Extension `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *MyMessageSet) Reset() { *m = MyMessageSet{} } func (m *MyMessageSet) Reset() { *m = MyMessageSet{} }
@ -1569,16 +1585,16 @@ func (*MyMessageSet) ProtoMessage() {}
func (*MyMessageSet) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } func (*MyMessageSet) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} }
func (m *MyMessageSet) Marshal() ([]byte, error) { func (m *MyMessageSet) Marshal() ([]byte, error) {
return proto.MarshalMessageSet(&m.XXX_InternalExtensions) return proto.MarshalMessageSet(m.ExtensionMap())
} }
func (m *MyMessageSet) Unmarshal(buf []byte) error { func (m *MyMessageSet) Unmarshal(buf []byte) error {
return proto.UnmarshalMessageSet(buf, &m.XXX_InternalExtensions) return proto.UnmarshalMessageSet(buf, m.ExtensionMap())
} }
func (m *MyMessageSet) MarshalJSON() ([]byte, error) { func (m *MyMessageSet) MarshalJSON() ([]byte, error) {
return proto.MarshalMessageSetJSON(&m.XXX_InternalExtensions) return proto.MarshalMessageSetJSON(m.XXX_extensions)
} }
func (m *MyMessageSet) UnmarshalJSON(buf []byte) error { func (m *MyMessageSet) UnmarshalJSON(buf []byte) error {
return proto.UnmarshalMessageSetJSON(buf, &m.XXX_InternalExtensions) return proto.UnmarshalMessageSetJSON(buf, m.XXX_extensions)
} }
// ensure MyMessageSet satisfies proto.Marshaler and proto.Unmarshaler // ensure MyMessageSet satisfies proto.Marshaler and proto.Unmarshaler
@ -1592,6 +1608,12 @@ var extRange_MyMessageSet = []proto.ExtensionRange{
func (*MyMessageSet) ExtensionRangeArray() []proto.ExtensionRange { func (*MyMessageSet) ExtensionRangeArray() []proto.ExtensionRange {
return extRange_MyMessageSet return extRange_MyMessageSet
} }
func (m *MyMessageSet) ExtensionMap() map[int32]proto.Extension {
if m.XXX_extensions == nil {
m.XXX_extensions = make(map[int32]proto.Extension)
}
return m.XXX_extensions
}
type Empty struct { type Empty struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
@ -2035,6 +2057,7 @@ func (m *GroupNew_G) GetY() int32 {
type FloatingPoint struct { type FloatingPoint struct {
F *float64 `protobuf:"fixed64,1,req,name=f" json:"f,omitempty"` F *float64 `protobuf:"fixed64,1,req,name=f" json:"f,omitempty"`
Exact *bool `protobuf:"varint,2,opt,name=exact" json:"exact,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
@ -2050,6 +2073,13 @@ func (m *FloatingPoint) GetF() float64 {
return 0 return 0
} }
func (m *FloatingPoint) GetExact() bool {
if m != nil && m.Exact != nil {
return *m.Exact
}
return false
}
type MessageWithMap struct { type MessageWithMap struct {
NameMapping map[int32]string `protobuf:"bytes,1,rep,name=name_mapping,json=nameMapping" json:"name_mapping,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` NameMapping map[int32]string `protobuf:"bytes,1,rep,name=name_mapping,json=nameMapping" json:"name_mapping,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
MsgMapping map[int64]*FloatingPoint `protobuf:"bytes,2,rep,name=msg_mapping,json=msgMapping" json:"msg_mapping,omitempty" protobuf_key:"zigzag64,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` MsgMapping map[int64]*FloatingPoint `protobuf:"bytes,2,rep,name=msg_mapping,json=msgMapping" json:"msg_mapping,omitempty" protobuf_key:"zigzag64,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
@ -3726,41 +3756,39 @@ func init() {
proto.RegisterExtension(E_X250) proto.RegisterExtension(E_X250)
} }
func init() { proto.RegisterFile("test.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 4335 bytes of a gzipped FileDescriptorProto // 4349 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x5a, 0x4b, 0x77, 0x1b, 0x47, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x5a, 0x4b, 0x77, 0x1b, 0x47,
0x76, 0x76, 0xe3, 0x8d, 0x02, 0x48, 0x80, 0x2d, 0x59, 0x82, 0xa8, 0x87, 0x65, 0xcc, 0xd8, 0x96, 0x76, 0x76, 0xe3, 0x8d, 0x02, 0x48, 0x80, 0x2d, 0x59, 0x82, 0xa8, 0x87, 0x65, 0xcc, 0xd8, 0x96,
0x64, 0x9b, 0x43, 0x34, 0x40, 0x52, 0x82, 0x27, 0x73, 0x8e, 0x28, 0x91, 0x1c, 0x9e, 0x11, 0x09, 0x64, 0x9b, 0x23, 0x34, 0x40, 0x52, 0x82, 0x27, 0x73, 0x8e, 0x28, 0x91, 0x1c, 0x9e, 0x11, 0x09,
0xa6, 0x49, 0xdb, 0x67, 0x9c, 0x05, 0x0e, 0x28, 0x36, 0x40, 0x58, 0x00, 0x1a, 0x02, 0xc0, 0x48, 0xa6, 0x49, 0xdb, 0x67, 0x9c, 0x05, 0x0e, 0x48, 0x36, 0x40, 0x58, 0x00, 0x1a, 0x02, 0xc0, 0x48,
0x4c, 0x36, 0xd9, 0x24, 0xdb, 0xbc, 0x36, 0xd9, 0x66, 0x95, 0x55, 0x92, 0x73, 0xf2, 0x27, 0x12, 0x4c, 0x36, 0xd9, 0x24, 0xdb, 0xbc, 0x36, 0xd9, 0x66, 0x95, 0x55, 0x92, 0x73, 0xf2, 0x27, 0x12,
0xdb, 0xf3, 0xf4, 0x3c, 0xf3, 0x9c, 0xbc, 0x1f, 0x93, 0xf7, 0x6b, 0x26, 0xc9, 0x26, 0x73, 0xef, 0xdb, 0xf3, 0xf4, 0x3c, 0xf3, 0x9c, 0xbc, 0x1f, 0x93, 0xf7, 0x6b, 0x26, 0xc9, 0x26, 0x73, 0xef,
0xad, 0xdb, 0xdd, 0xd5, 0x0d, 0x74, 0x93, 0xd2, 0xc2, 0x44, 0x57, 0x7d, 0xdf, 0xad, 0x5b, 0xd5, 0xad, 0xdb, 0xdd, 0xd5, 0x0d, 0x74, 0x93, 0xd2, 0xc2, 0x44, 0x57, 0x7d, 0xdf, 0xad, 0x5b, 0xd5,
0x5f, 0xdd, 0x5b, 0xb7, 0xda, 0x42, 0x4c, 0xac, 0xf1, 0x64, 0x69, 0x38, 0xb2, 0x27, 0xb6, 0x9e, 0x5f, 0xdd, 0x5b, 0xb7, 0xda, 0x42, 0x4c, 0xac, 0xf1, 0x64, 0x69, 0x38, 0xb2, 0x27, 0xb6, 0x9e,
0xc1, 0xdf, 0x47, 0xad, 0x49, 0xab, 0x7c, 0x5b, 0xa4, 0xb6, 0xec, 0x8d, 0xc1, 0x49, 0x5f, 0x7f, 0xc1, 0xdf, 0x47, 0xad, 0x49, 0xab, 0x7c, 0x5b, 0xa4, 0x36, 0xed, 0xf5, 0xc1, 0x49, 0x5f, 0x7f,
0x45, 0xc4, 0xdb, 0xb6, 0x5d, 0xd2, 0x6e, 0xc6, 0x6e, 0xcd, 0x1b, 0x73, 0x4b, 0x0e, 0x62, 0x69, 0x45, 0xc4, 0xdb, 0xb6, 0x5d, 0xd2, 0x6e, 0xc6, 0x6e, 0xcd, 0x1b, 0x73, 0x4b, 0x0e, 0x62, 0x69,
0xb3, 0xd1, 0x30, 0xb1, 0xa7, 0xbc, 0x26, 0x72, 0x5b, 0xf6, 0x01, 0x34, 0x6f, 0x76, 0xad, 0xde, 0xa3, 0xd1, 0x30, 0xb1, 0xa7, 0xbc, 0x2a, 0x72, 0x9b, 0xf6, 0x3e, 0x34, 0x6f, 0x74, 0xad, 0xde,
0x91, 0x7e, 0x51, 0x24, 0x1f, 0xb5, 0x0e, 0xad, 0x1e, 0x31, 0xb2, 0x66, 0xb2, 0x87, 0x0f, 0xba, 0x91, 0x7e, 0x51, 0x24, 0x1f, 0xb7, 0x0e, 0xac, 0x1e, 0x31, 0xb2, 0x66, 0xb2, 0x87, 0x0f, 0xba,
0x2e, 0x12, 0x07, 0xa7, 0x43, 0xab, 0x14, 0xa3, 0xc6, 0xc4, 0x04, 0x7e, 0x97, 0x7f, 0xf9, 0x06, 0x2e, 0x12, 0xfb, 0xa7, 0x43, 0xab, 0x14, 0xa3, 0xc6, 0xc4, 0x04, 0x7e, 0x97, 0x7f, 0xf9, 0x06,
0x0e, 0x82, 0x4c, 0xfd, 0xb6, 0x48, 0x7c, 0xa9, 0x3b, 0x38, 0xe2, 0x51, 0x5e, 0xf6, 0x46, 0x91, 0x0e, 0x82, 0x4c, 0xfd, 0xb6, 0x48, 0x7c, 0xa9, 0x3b, 0x38, 0xe2, 0x51, 0x5e, 0xf6, 0x46, 0x91,
0xfd, 0x4b, 0x5f, 0xda, 0xde, 0x7d, 0x68, 0x26, 0x9e, 0x00, 0x04, 0xed, 0x1f, 0xb4, 0x0e, 0x7b, 0xfd, 0x4b, 0x5f, 0xda, 0xda, 0x79, 0x64, 0x26, 0x9e, 0x00, 0x04, 0xed, 0xef, 0xb7, 0x0e, 0x7a,
0x68, 0x4a, 0x43, 0xfb, 0x13, 0x7c, 0xc0, 0xd6, 0xbd, 0xd6, 0xa8, 0xd5, 0x2f, 0xc5, 0xa1, 0x35, 0x68, 0x4a, 0x43, 0xfb, 0x13, 0x7c, 0xc0, 0xd6, 0xdd, 0xd6, 0xa8, 0xd5, 0x2f, 0xc5, 0xa1, 0x35,
0x69, 0x26, 0x87, 0xf8, 0xa0, 0xbf, 0x23, 0xe6, 0x4c, 0xeb, 0xe9, 0x49, 0x77, 0x64, 0x1d, 0x91, 0x69, 0x26, 0x87, 0xf8, 0xa0, 0xbf, 0x23, 0xe6, 0x4c, 0xeb, 0xe9, 0x49, 0x77, 0x64, 0x1d, 0x91,
0x73, 0xa5, 0x04, 0xd8, 0xcf, 0x4d, 0xdb, 0xa7, 0x4e, 0x73, 0x6e, 0xa4, 0x62, 0x25, 0x79, 0x68, 0x73, 0xa5, 0x04, 0xd8, 0xcf, 0x4d, 0xdb, 0xa7, 0x4e, 0x73, 0x6e, 0xa4, 0x62, 0x25, 0x79, 0x68,
0xb5, 0x26, 0x0e, 0x39, 0x79, 0x33, 0x1e, 0x49, 0x56, 0xb0, 0x48, 0x6e, 0x0c, 0x27, 0x5d, 0x7b, 0xb5, 0x26, 0x0e, 0x39, 0x79, 0x33, 0x1e, 0x49, 0x56, 0xb0, 0x48, 0x6e, 0x0c, 0x27, 0x5d, 0x7b,
0xd0, 0xea, 0x49, 0x72, 0x0a, 0xfc, 0x0a, 0x27, 0xdb, 0x2a, 0x56, 0x7f, 0x5d, 0x14, 0x36, 0x9b, 0xd0, 0xea, 0x49, 0x72, 0x0a, 0xfc, 0x0a, 0x27, 0xdb, 0x2a, 0x56, 0x7f, 0x5d, 0x14, 0x36, 0x9a,
0xeb, 0xb6, 0xdd, 0x6b, 0x3a, 0x1e, 0x95, 0x04, 0x38, 0x9e, 0x31, 0xe7, 0xda, 0xd8, 0xea, 0x4c, 0x6b, 0xb6, 0xdd, 0x6b, 0x3a, 0x1e, 0x95, 0x04, 0x38, 0x9e, 0x31, 0xe7, 0xda, 0xd8, 0xea, 0x4c,
0x49, 0xbf, 0x25, 0x8a, 0x9b, 0xcd, 0xed, 0xc1, 0xa4, 0x6a, 0x78, 0xc0, 0x1c, 0x00, 0x93, 0xe6, 0x49, 0xbf, 0x25, 0x8a, 0x1b, 0xcd, 0xad, 0xc1, 0xa4, 0x6a, 0x78, 0xc0, 0x1c, 0x00, 0x93, 0xe6,
0x7c, 0x9b, 0x9a, 0xa7, 0x90, 0xab, 0x35, 0x0f, 0x99, 0x07, 0x64, 0x5c, 0x22, 0x57, 0x6b, 0x2e, 0x7c, 0x9b, 0x9a, 0xa7, 0x90, 0x2b, 0x35, 0x0f, 0x99, 0x07, 0x64, 0x5c, 0x22, 0x57, 0x6a, 0x2e,
0xf2, 0x2d, 0xa1, 0x6f, 0x36, 0x37, 0xbb, 0xcf, 0xad, 0x23, 0xd5, 0xea, 0x1c, 0x60, 0xd3, 0x66, 0xf2, 0x2d, 0xa1, 0x6f, 0x34, 0x37, 0xba, 0xcf, 0xad, 0x23, 0xd5, 0xea, 0x1c, 0x60, 0xd3, 0x66,
0xb1, 0xcd, 0x1d, 0x33, 0xd0, 0xaa, 0xe5, 0x79, 0x40, 0xa7, 0x1c, 0xb4, 0x62, 0xfb, 0x8e, 0x58, 0xb1, 0xcd, 0x1d, 0x33, 0xd0, 0xaa, 0xe5, 0x79, 0x40, 0xa7, 0x1c, 0xb4, 0x62, 0xfb, 0x8e, 0x58,
0xd8, 0x6c, 0xbe, 0xdb, 0xf5, 0x3b, 0x5c, 0x00, 0xf0, 0x9c, 0x59, 0x68, 0xcb, 0xf6, 0x69, 0xac, 0xd8, 0x68, 0xbe, 0xdb, 0xf5, 0x3b, 0x5c, 0x00, 0xf0, 0x9c, 0x59, 0x68, 0xcb, 0xf6, 0x69, 0xac,
0x6a, 0xb8, 0x08, 0xd8, 0x04, 0x63, 0x15, 0xbb, 0x34, 0xbb, 0xcd, 0x9e, 0xdd, 0x9a, 0x78, 0xd0, 0x6a, 0xb8, 0x08, 0xd8, 0x04, 0x63, 0x15, 0xbb, 0x34, 0xbb, 0x8d, 0x9e, 0xdd, 0x9a, 0x78, 0xd0,
0x05, 0x80, 0xc6, 0x60, 0x76, 0xd4, 0xec, 0xb7, 0xfa, 0xd0, 0x3e, 0x01, 0xc9, 0x78, 0x50, 0x1d, 0x05, 0x80, 0xc6, 0x60, 0x76, 0xd4, 0xec, 0xb7, 0xfa, 0xc8, 0x3e, 0x01, 0xc9, 0x78, 0x50, 0x1d,
0xa0, 0x1a, 0x58, 0x95, 0xed, 0x7e, 0xec, 0xfe, 0x64, 0xd4, 0x1d, 0x74, 0x3c, 0xec, 0x05, 0xd2, 0xa0, 0x1a, 0x58, 0x95, 0xed, 0x7e, 0xec, 0xde, 0x64, 0xd4, 0x1d, 0x74, 0x3c, 0xec, 0x05, 0xd2,
0x6f, 0xa1, 0x2d, 0xdb, 0xfd, 0x1e, 0xac, 0x9f, 0xc2, 0xcb, 0xf5, 0xa0, 0x16, 0x40, 0xf3, 0xe0, 0x6f, 0xa1, 0x2d, 0xdb, 0xfd, 0x1e, 0xac, 0x9d, 0xc2, 0xcb, 0xf5, 0xa0, 0x16, 0x40, 0xf3, 0xe0,
0x01, 0x35, 0x07, 0xac, 0x06, 0xd6, 0xa0, 0x0d, 0xd0, 0x05, 0xb4, 0x3a, 0x63, 0x0d, 0xf6, 0x03, 0x01, 0x35, 0x07, 0xac, 0x06, 0xd6, 0xa0, 0x0d, 0xd0, 0x05, 0xb4, 0x3a, 0x63, 0x0d, 0xf6, 0x02,
0x6b, 0xd0, 0x01, 0xac, 0xce, 0x58, 0x65, 0x0d, 0x54, 0xcd, 0x48, 0x21, 0x96, 0x2e, 0x82, 0x5e, 0x6b, 0xd0, 0x01, 0xac, 0xce, 0x58, 0x65, 0x0d, 0x54, 0xcd, 0x48, 0x21, 0x96, 0x2e, 0x82, 0x5e,
0x3d, 0xcd, 0xc8, 0x46, 0xbf, 0x66, 0x18, 0xf8, 0x32, 0x00, 0x15, 0xcd, 0x04, 0x90, 0x34, 0x38, 0x3d, 0xcd, 0xc8, 0x46, 0xbf, 0x66, 0x18, 0xf8, 0x32, 0x00, 0x15, 0xcd, 0x04, 0x90, 0x34, 0x38,
0x23, 0x2f, 0x01, 0x52, 0xd1, 0x0c, 0x23, 0x03, 0x9a, 0x61, 0xec, 0x65, 0xc0, 0xfa, 0x34, 0x33, 0x23, 0x2f, 0x01, 0x52, 0xd1, 0x0c, 0x23, 0x03, 0x9a, 0x61, 0xec, 0x65, 0xc0, 0xfa, 0x34, 0x33,
@ -3779,226 +3807,227 @@ var fileDescriptor0 = []byte{
0x1b, 0x17, 0xfa, 0xdb, 0x98, 0x0b, 0x5d, 0xd9, 0xb8, 0x50, 0x55, 0x36, 0x2e, 0xf6, 0x77, 0x10, 0x1b, 0x17, 0xfa, 0xdb, 0x98, 0x0b, 0x5d, 0xd9, 0xb8, 0x50, 0x55, 0x36, 0x2e, 0xf6, 0x77, 0x10,
0xeb, 0xc9, 0x66, 0x1a, 0xac, 0xae, 0xc2, 0xef, 0x22, 0xd8, 0x93, 0x8d, 0x0b, 0x5e, 0x22, 0x27, 0xeb, 0xc9, 0x66, 0x1a, 0xac, 0xae, 0xc2, 0xef, 0x22, 0xd8, 0x93, 0x8d, 0x0b, 0x5e, 0x22, 0x27,
0x50, 0x36, 0x47, 0x56, 0xbb, 0x75, 0xd2, 0x43, 0x89, 0xdd, 0x42, 0xdd, 0xd4, 0x13, 0x93, 0xd1, 0x50, 0x36, 0x47, 0x56, 0xbb, 0x75, 0xd2, 0x43, 0x89, 0xdd, 0x42, 0xdd, 0xd4, 0x13, 0x93, 0xd1,
0x89, 0x85, 0x9e, 0x40, 0xe7, 0x43, 0xa7, 0x0f, 0xf0, 0x0b, 0x8e, 0x7c, 0x3c, 0xc2, 0x6d, 0xd4, 0x89, 0x85, 0x9e, 0x40, 0xe7, 0x23, 0xa7, 0x0f, 0xf0, 0x0b, 0x8e, 0x7c, 0x3c, 0xc2, 0x6d, 0xd4,
0x4f, 0x3d, 0x06, 0xd2, 0x2d, 0x48, 0x0d, 0x4d, 0xe3, 0xc1, 0x17, 0x0f, 0x7f, 0x07, 0x55, 0x54, 0x4f, 0x3d, 0x06, 0xd2, 0x2d, 0x48, 0x0d, 0x4d, 0xe3, 0xc1, 0x17, 0x0f, 0x7f, 0x07, 0x55, 0x54,
0x8f, 0x81, 0x7a, 0x0b, 0x52, 0x49, 0x1e, 0xbe, 0x2a, 0x2e, 0x78, 0x52, 0xf2, 0x18, 0x6f, 0xa2, 0x8f, 0x81, 0x7a, 0x0b, 0x52, 0x49, 0x1e, 0xbe, 0x2a, 0x2e, 0x78, 0x52, 0xf2, 0x18, 0x6f, 0xa2,
0x96, 0xea, 0xf1, 0xaa, 0xb1, 0x6c, 0x2e, 0x38, 0x82, 0x9a, 0x45, 0xf2, 0x0d, 0xf3, 0x16, 0x4a, 0x96, 0xea, 0xf1, 0xaa, 0x71, 0xd7, 0x5c, 0x70, 0x04, 0x35, 0x8b, 0xe4, 0x1b, 0xe6, 0x2d, 0x94,
0xaa, 0x1e, 0x5f, 0xad, 0xb9, 0x24, 0x75, 0x24, 0x03, 0x65, 0xc8, 0xc2, 0xf2, 0x38, 0x6f, 0xa3, 0x54, 0x3d, 0xbe, 0x52, 0x73, 0x49, 0xea, 0x48, 0x06, 0xca, 0x90, 0x85, 0xe5, 0x71, 0xde, 0x46,
0xb2, 0xea, 0x09, 0x18, 0x68, 0x19, 0xc4, 0x28, 0xbb, 0x67, 0x70, 0x7c, 0xe3, 0x2c, 0xa1, 0xc2, 0x65, 0xd5, 0x13, 0x30, 0xd0, 0x5d, 0x10, 0xa3, 0xec, 0x9e, 0xc1, 0xf1, 0x8d, 0xb3, 0x84, 0x0a,
0xea, 0x09, 0x18, 0xc7, 0xe1, 0xf8, 0xc7, 0x59, 0x70, 0x84, 0xe6, 0x51, 0x3e, 0x87, 0x4a, 0xab, 0xab, 0x27, 0x60, 0x1c, 0x87, 0xe3, 0x1f, 0x67, 0xc1, 0x11, 0x9a, 0x47, 0xf9, 0x1c, 0x2a, 0xad,
0xa7, 0xaa, 0x95, 0x5a, 0x65, 0xe5, 0x1e, 0xac, 0x02, 0xf5, 0x7b, 0x9c, 0x1a, 0x8e, 0xc3, 0x92, 0x9e, 0xaa, 0x56, 0x6a, 0x95, 0xe5, 0xfb, 0xb0, 0x0a, 0xd4, 0xef, 0x71, 0x6a, 0x38, 0x0e, 0x4b,
0xf3, 0x48, 0xcb, 0xa8, 0xb9, 0x7a, 0xca, 0x58, 0xab, 0xdc, 0x35, 0xee, 0xc2, 0x48, 0x12, 0xe0, 0xce, 0x23, 0xdd, 0x45, 0xcd, 0xd5, 0x53, 0xc6, 0x6a, 0xe5, 0x9e, 0x71, 0x0f, 0x46, 0x92, 0x00,
0xb1, 0xbe, 0x80, 0x2c, 0x16, 0x9f, 0xc7, 0xaa, 0xa0, 0xfa, 0xea, 0xc5, 0x63, 0xab, 0xd7, 0xb3, 0x8f, 0xf5, 0x05, 0x64, 0xb1, 0xf8, 0x3c, 0x56, 0x05, 0xd5, 0x57, 0x2f, 0x1e, 0x5b, 0xbd, 0x9e,
0xdf, 0xba, 0x59, 0x7e, 0x66, 0x8f, 0x7a, 0x47, 0xaf, 0x96, 0x05, 0xf0, 0x25, 0x54, 0x1d, 0x75, 0xfd, 0xd6, 0xcd, 0xf2, 0x33, 0x7b, 0xd4, 0x3b, 0x7a, 0xb5, 0x2c, 0x80, 0x2f, 0xa1, 0xea, 0xa8,
0xc1, 0x11, 0xa4, 0x47, 0xff, 0x55, 0x3c, 0x87, 0xe5, 0xeb, 0xe9, 0xf5, 0x6e, 0x67, 0x60, 0x8f, 0x0b, 0x8e, 0x20, 0x3d, 0xfa, 0xaf, 0xe2, 0x39, 0x2c, 0x5f, 0x4f, 0xaf, 0x75, 0x3b, 0x03, 0x7b,
0x2d, 0xf0, 0x95, 0x10, 0x81, 0x35, 0xd9, 0x0f, 0xae, 0xe3, 0xaf, 0x21, 0x6d, 0xa1, 0x1e, 0x7f, 0x6c, 0x81, 0xaf, 0x84, 0x08, 0xac, 0xc9, 0x5e, 0x70, 0x1d, 0x7f, 0x0d, 0x69, 0x0b, 0xf5, 0xf8,
0x1b, 0x44, 0x51, 0x64, 0x85, 0xce, 0xe0, 0xf8, 0xd6, 0xf1, 0xd7, 0x91, 0xa3, 0x03, 0x07, 0x84, 0xdb, 0x20, 0x8a, 0x22, 0x2b, 0x74, 0x06, 0xc7, 0xb7, 0x8e, 0xbf, 0x8e, 0x1c, 0x1d, 0x38, 0x20,
0x51, 0x64, 0xa1, 0x7a, 0x9c, 0x35, 0x71, 0x29, 0x90, 0x17, 0x9b, 0xc3, 0xd6, 0xe3, 0x27, 0xc0, 0x8c, 0x22, 0x0b, 0xd5, 0xe3, 0xac, 0x8a, 0x4b, 0x81, 0xbc, 0xd8, 0x1c, 0xb6, 0x0e, 0x9f, 0x00,
0x33, 0x30, 0x3d, 0xae, 0xc7, 0x8a, 0x9a, 0x79, 0xc1, 0x97, 0x22, 0xf7, 0xa8, 0x5b, 0xbf, 0x27, 0xcf, 0xc0, 0xf4, 0xb8, 0x16, 0x2b, 0x6a, 0xe6, 0x05, 0x5f, 0x8a, 0xdc, 0xa5, 0x6e, 0xfd, 0xbe,
0x2e, 0x07, 0x13, 0xa5, 0xc3, 0xac, 0x62, 0xbe, 0x24, 0xe6, 0x45, 0x7f, 0xce, 0x0c, 0x50, 0x95, 0xb8, 0x1c, 0x4c, 0x94, 0x0e, 0xb3, 0x8a, 0xf9, 0x92, 0x98, 0x17, 0xfd, 0x39, 0x33, 0x40, 0x55,
0x00, 0xec, 0x50, 0x6b, 0x98, 0x40, 0x3d, 0xaa, 0x17, 0x89, 0x99, 0xfa, 0x53, 0xe2, 0xca, 0x74, 0x02, 0xb0, 0x43, 0xad, 0x61, 0x02, 0xf5, 0xa8, 0x5e, 0x24, 0x66, 0xea, 0x4f, 0x89, 0x2b, 0xd3,
0x2a, 0x75, 0xc8, 0x2b, 0x98, 0x51, 0x89, 0x7c, 0x29, 0x98, 0x55, 0xa7, 0xe8, 0x33, 0xc6, 0x5e, 0xa9, 0xd4, 0x21, 0x2f, 0x63, 0x46, 0x25, 0xf2, 0xa5, 0x60, 0x56, 0x9d, 0xa2, 0xcf, 0x18, 0x7b,
0xc5, 0x14, 0xab, 0xd2, 0xa7, 0x46, 0x7f, 0x47, 0x94, 0xa6, 0x92, 0xad, 0xc3, 0x5e, 0xc3, 0x9c, 0x05, 0x53, 0xac, 0x4a, 0x9f, 0x1a, 0xfd, 0x1d, 0x51, 0x9a, 0x4a, 0xb6, 0x0e, 0x7b, 0x15, 0x73,
0x4b, 0xec, 0x97, 0x03, 0x79, 0x37, 0x48, 0x9e, 0x31, 0xf4, 0x5d, 0x4c, 0xc2, 0x0a, 0x79, 0x6a, 0x2e, 0xb1, 0x5f, 0x0e, 0xe4, 0xdd, 0x20, 0x79, 0xc6, 0xd0, 0xf7, 0x30, 0x09, 0x2b, 0xe4, 0xa9,
0x64, 0x5a, 0x32, 0x7f, 0x3a, 0x76, 0xb8, 0xf7, 0x30, 0x2b, 0xf3, 0x92, 0xf9, 0x32, 0xb3, 0x3a, 0x91, 0x69, 0xc9, 0xfc, 0xe9, 0xd8, 0xe1, 0xde, 0xc7, 0xac, 0xcc, 0x4b, 0xe6, 0xcb, 0xcc, 0xea,
0x6e, 0x20, 0x3f, 0x3b, 0xdc, 0x3a, 0xa6, 0x69, 0x1e, 0xd7, 0x9f, 0xaa, 0x99, 0xfc, 0x79, 0x24, 0xb8, 0x81, 0xfc, 0xec, 0x70, 0xeb, 0x98, 0xa6, 0x79, 0x5c, 0x7f, 0xaa, 0x66, 0xf2, 0xe7, 0x91,
0xef, 0xcf, 0x9e, 0xf1, 0x8f, 0xe2, 0x98, 0x60, 0x99, 0xbd, 0x3f, 0x6b, 0xca, 0x2e, 0x7b, 0xc6, 0xbc, 0x37, 0x7b, 0xc6, 0x3f, 0x8a, 0x63, 0x82, 0x65, 0xf6, 0xde, 0xac, 0x29, 0xbb, 0xec, 0x19,
0x94, 0x7f, 0x8c, 0x6c, 0x5d, 0x61, 0x4f, 0xcd, 0xf9, 0xa1, 0x70, 0x2b, 0x8e, 0xce, 0xc8, 0x3e, 0x53, 0xfe, 0x31, 0xb2, 0x75, 0x85, 0x3d, 0x35, 0xe7, 0x47, 0xc2, 0xad, 0x38, 0x3a, 0x23, 0xfb,
0x19, 0x96, 0x36, 0xe1, 0x68, 0x27, 0x8c, 0x1b, 0x53, 0xd5, 0x8f, 0x73, 0xc8, 0xdb, 0x42, 0x94, 0x64, 0x58, 0xda, 0x80, 0xa3, 0x9d, 0x30, 0x6e, 0x4c, 0x55, 0x3f, 0xce, 0x21, 0x6f, 0x13, 0x51,
0xe9, 0x27, 0x49, 0x2b, 0xd2, 0xae, 0xb4, 0xb2, 0x07, 0xe3, 0xce, 0xb6, 0x22, 0x51, 0xae, 0x15, 0xa6, 0x9f, 0x24, 0xad, 0x48, 0xbb, 0xd2, 0xca, 0x2e, 0x8c, 0x3b, 0xdb, 0x8a, 0x44, 0xb9, 0x56,
0x85, 0x84, 0x56, 0x9c, 0xa0, 0x2f, 0xad, 0x7c, 0x00, 0x9b, 0x6a, 0x96, 0x15, 0x27, 0x05, 0xb0, 0x14, 0x12, 0x5a, 0x71, 0x82, 0xbe, 0xb4, 0xf2, 0x01, 0x6c, 0xaa, 0x59, 0x56, 0x9c, 0x14, 0xc0,
0x15, 0x1f, 0x69, 0x71, 0xc5, 0xab, 0xb7, 0xa8, 0x5f, 0xff, 0x6c, 0xb0, 0x00, 0xdb, 0xa2, 0xf3, 0x56, 0x7c, 0xa4, 0xc5, 0x65, 0xaf, 0xde, 0xa2, 0x7e, 0xfd, 0xb3, 0xc1, 0x02, 0x6c, 0x93, 0xce,
0xb3, 0xbf, 0xd2, 0x92, 0x34, 0xc5, 0xb9, 0x69, 0xda, 0x4f, 0x87, 0xd0, 0x7c, 0xde, 0x4c, 0xd3, 0xcf, 0xfe, 0x4a, 0x4b, 0xd2, 0x14, 0xe7, 0xa6, 0x69, 0x3f, 0x1d, 0x42, 0xf3, 0x79, 0x33, 0x4d,
0x7e, 0x66, 0x06, 0xad, 0xfc, 0x1b, 0x1a, 0x14, 0x9b, 0x50, 0x4f, 0xea, 0x19, 0x91, 0x78, 0xaf, 0xfb, 0x99, 0x19, 0xb4, 0xf2, 0x6f, 0x68, 0x50, 0x6c, 0x42, 0x3d, 0xa9, 0x67, 0x44, 0xe2, 0xbd,
0xb1, 0xfd, 0xb0, 0xf8, 0x12, 0xfe, 0x5a, 0x6f, 0x34, 0x1e, 0x15, 0x35, 0x3d, 0x2b, 0x92, 0xeb, 0xc6, 0xd6, 0xa3, 0xe2, 0x4b, 0xf8, 0x6b, 0xad, 0xd1, 0x78, 0x5c, 0xd4, 0xf4, 0xac, 0x48, 0xae,
0x5f, 0x3e, 0xd8, 0xd8, 0x2f, 0xc6, 0xf4, 0x82, 0xc8, 0x6d, 0x6e, 0xef, 0x6e, 0x6d, 0x98, 0x7b, 0x7d, 0x79, 0x7f, 0x7d, 0xaf, 0x18, 0xd3, 0x0b, 0x22, 0xb7, 0xb1, 0xb5, 0xb3, 0xb9, 0x6e, 0xee,
0xe6, 0xf6, 0xee, 0x41, 0x31, 0x8e, 0x7d, 0x9b, 0x8f, 0x1a, 0xf7, 0x0f, 0x8a, 0x09, 0x3d, 0x2d, 0x9a, 0x5b, 0x3b, 0xfb, 0xc5, 0x38, 0xf6, 0x6d, 0x3c, 0x6e, 0x3c, 0xd8, 0x2f, 0x26, 0xf4, 0xb4,
0xe2, 0xd8, 0x96, 0xd4, 0x85, 0x48, 0xed, 0x1f, 0x40, 0xff, 0x56, 0x31, 0x85, 0x56, 0x0e, 0xb6, 0x88, 0x63, 0x5b, 0x52, 0x17, 0x22, 0xb5, 0xb7, 0x0f, 0xfd, 0x9b, 0xc5, 0x14, 0x5a, 0xd9, 0xdf,
0x77, 0x36, 0x8a, 0x69, 0x44, 0x1e, 0xbc, 0xbb, 0xf7, 0x68, 0xa3, 0x98, 0xc1, 0x9f, 0xf7, 0x4d, 0xda, 0x5e, 0x2f, 0xa6, 0x11, 0xb9, 0xff, 0xee, 0xee, 0xe3, 0xf5, 0x62, 0x06, 0x7f, 0x3e, 0x30,
0xf3, 0xfe, 0x97, 0x8b, 0x59, 0x24, 0xed, 0xdc, 0xdf, 0x2b, 0x0a, 0xea, 0xbe, 0xbf, 0x0e, 0xdd, 0xcd, 0x07, 0x5f, 0x2e, 0x66, 0x91, 0xb4, 0xfd, 0x60, 0xb7, 0x28, 0xa8, 0xfb, 0xc1, 0x1a, 0x74,
0x39, 0x3d, 0x2f, 0x32, 0x9b, 0xef, 0xee, 0x3e, 0x38, 0xd8, 0x6e, 0xec, 0x16, 0xf3, 0xe5, 0xdf, 0xe7, 0xf4, 0xbc, 0xc8, 0x6c, 0xbc, 0xbb, 0xf3, 0x70, 0x7f, 0xab, 0xb1, 0x53, 0xcc, 0x97, 0x7f,
0x8c, 0x09, 0xb1, 0x65, 0xef, 0x3f, 0xe9, 0x0e, 0xa9, 0x2a, 0xbe, 0x2e, 0xc4, 0x18, 0x7e, 0x37, 0x33, 0x26, 0xc4, 0xa6, 0xbd, 0xf7, 0xa4, 0x3b, 0xa4, 0xaa, 0xf8, 0xba, 0x10, 0x63, 0xf8, 0xdd,
0x49, 0x7a, 0x5c, 0xd9, 0x65, 0xb1, 0x85, 0x82, 0x8e, 0xfe, 0xaa, 0xc8, 0x53, 0x77, 0x5b, 0x86, 0x24, 0xe9, 0x71, 0x65, 0x97, 0xc5, 0x16, 0x0a, 0x3a, 0xfa, 0xab, 0x22, 0x4f, 0xdd, 0x6d, 0x19,
0x02, 0x2a, 0xe8, 0xd2, 0x66, 0x0e, 0xdb, 0x38, 0x3a, 0xf8, 0x21, 0xab, 0x35, 0xaa, 0xe3, 0x52, 0x0a, 0xa8, 0xa0, 0x4b, 0x9b, 0x39, 0x6c, 0xe3, 0xe8, 0xe0, 0x87, 0xac, 0xd4, 0xa8, 0x8e, 0x4b,
0x0a, 0x64, 0xb5, 0x06, 0xf5, 0x3d, 0x3d, 0x36, 0xc7, 0x14, 0xd6, 0xa9, 0x76, 0xcb, 0x9a, 0x34, 0x29, 0x90, 0x95, 0x1a, 0xd4, 0xf7, 0xf4, 0xd8, 0x1c, 0x53, 0x58, 0xa7, 0xda, 0x2d, 0x6b, 0xd2,
0xae, 0x0c, 0xf4, 0x20, 0x72, 0x1a, 0x53, 0xca, 0xa2, 0x30, 0x2d, 0x51, 0xc7, 0xdd, 0x25, 0xfc, 0xb8, 0x32, 0xd0, 0x83, 0xc8, 0x69, 0x4c, 0x29, 0x8b, 0xc2, 0xb4, 0x44, 0x1d, 0x77, 0x97, 0xf0,
0x21, 0x65, 0xe1, 0x11, 0x16, 0x1b, 0x22, 0xeb, 0xb6, 0xe3, 0x58, 0xd4, 0xca, 0x33, 0x2a, 0xd2, 0x87, 0x94, 0x85, 0x47, 0x58, 0x6c, 0x88, 0xac, 0xdb, 0x8e, 0x63, 0x51, 0x2b, 0xcf, 0xa8, 0x48,
0x8c, 0x04, 0x35, 0xb9, 0x53, 0x92, 0x00, 0xf6, 0x66, 0x81, 0xbc, 0x91, 0x24, 0xe9, 0x4e, 0xf9, 0x33, 0x12, 0xd4, 0xe4, 0x4e, 0x49, 0x02, 0xd8, 0x9b, 0x05, 0xf2, 0x46, 0x92, 0xa4, 0x3b, 0xe5,
0xba, 0x98, 0xdb, 0xb5, 0x07, 0x72, 0x0b, 0xd1, 0x2a, 0xe5, 0x85, 0xd6, 0x2a, 0x69, 0x54, 0xc2, 0xeb, 0x62, 0x6e, 0xc7, 0x1e, 0xc8, 0x2d, 0x44, 0xab, 0x94, 0x17, 0x5a, 0xab, 0xa4, 0x51, 0x09,
0x68, 0xad, 0xf2, 0x0d, 0x21, 0x94, 0xbe, 0xa2, 0xd0, 0x0e, 0x65, 0x1f, 0x6d, 0x44, 0xed, 0xb0, 0xa3, 0xb5, 0xca, 0x37, 0x84, 0x50, 0xfa, 0x8a, 0x42, 0x3b, 0x90, 0x7d, 0xb4, 0x11, 0xb5, 0x83,
0xfc, 0xa6, 0x48, 0xed, 0xb4, 0x9e, 0x1f, 0xb4, 0x3a, 0x30, 0x96, 0xe8, 0xb5, 0xc6, 0x13, 0x58, 0xf2, 0x9b, 0x22, 0xb5, 0xdd, 0x7a, 0xbe, 0xdf, 0xea, 0xc0, 0x58, 0xa2, 0xd7, 0x1a, 0x4f, 0x60,
0x1b, 0x94, 0xca, 0xff, 0xc3, 0x3f, 0x8d, 0x4e, 0x5c, 0x59, 0x6c, 0x95, 0x52, 0x79, 0x2a, 0x44, 0x6d, 0x50, 0x2a, 0xff, 0x0f, 0xff, 0x34, 0x3a, 0x71, 0x65, 0xb1, 0x55, 0x4a, 0xe5, 0xa9, 0x10,
0xa3, 0x77, 0xb4, 0x63, 0x8d, 0xc7, 0xad, 0x8e, 0x05, 0xe7, 0x85, 0xd4, 0x00, 0x8c, 0x5a, 0x78, 0x8d, 0xde, 0xd1, 0xb6, 0x35, 0x1e, 0xb7, 0x3a, 0x16, 0x9c, 0x17, 0x52, 0x03, 0x30, 0x6a, 0xe1,
0x4d, 0x81, 0xc5, 0xfc, 0x55, 0x6f, 0x15, 0x3c, 0xd4, 0xd2, 0x2e, 0x41, 0x4c, 0x86, 0x82, 0x07, 0x35, 0x05, 0x16, 0xf3, 0x57, 0xbd, 0x55, 0xf0, 0x50, 0x4b, 0x3b, 0x04, 0x31, 0x19, 0x0a, 0x1e,
0xf1, 0xc1, 0x49, 0x9f, 0x2e, 0x2b, 0x92, 0x26, 0xfe, 0x5c, 0xbc, 0x26, 0x52, 0x12, 0x83, 0x97, 0xc4, 0x07, 0x27, 0x7d, 0xba, 0xac, 0x48, 0x9a, 0xf8, 0x73, 0xf1, 0x9a, 0x48, 0x49, 0x0c, 0x5e,
0x22, 0x83, 0x56, 0xdf, 0x2a, 0xc9, 0x71, 0xe9, 0x77, 0xf9, 0x57, 0x34, 0x21, 0x76, 0xad, 0x67, 0x8a, 0x0c, 0x5a, 0x7d, 0xab, 0x24, 0xc7, 0xa5, 0xdf, 0xe5, 0x5f, 0xd1, 0x84, 0xd8, 0xb1, 0x9e,
0xe7, 0x18, 0xd3, 0x43, 0x45, 0x8c, 0x19, 0x97, 0x63, 0xbe, 0x13, 0x35, 0x26, 0xea, 0xac, 0x6d, 0x9d, 0x63, 0x4c, 0x0f, 0x15, 0x31, 0x66, 0x5c, 0x8e, 0xf9, 0x4e, 0xd4, 0x98, 0xa8, 0xb3, 0xb6,
0xdb, 0x47, 0x4d, 0xf9, 0x8a, 0xe5, 0xbd, 0x4a, 0x16, 0x5b, 0xe8, 0xad, 0x95, 0x3f, 0x10, 0xf9, 0x6d, 0x1f, 0x35, 0xe5, 0x2b, 0x96, 0xf7, 0x2a, 0x59, 0x6c, 0xa1, 0xb7, 0x56, 0xfe, 0x40, 0xe4,
0xed, 0xc1, 0xc0, 0x1a, 0x39, 0x3e, 0x81, 0x89, 0x63, 0x7b, 0x3c, 0xe1, 0x0b, 0x1e, 0xfa, 0xad, 0xb7, 0x06, 0x03, 0x6b, 0xe4, 0xf8, 0x04, 0x26, 0x8e, 0xed, 0xf1, 0x84, 0x2f, 0x78, 0xe8, 0xb7,
0x97, 0x44, 0x62, 0x68, 0x8f, 0x26, 0x72, 0x9e, 0xf5, 0x04, 0x9c, 0x69, 0x96, 0x4d, 0x6a, 0xd1, 0x5e, 0x12, 0x89, 0xa1, 0x3d, 0x9a, 0xc8, 0x79, 0xd6, 0x13, 0x70, 0xa6, 0xb9, 0x6b, 0x52, 0x8b,
0xaf, 0x89, 0xec, 0x63, 0x1b, 0xe8, 0x8f, 0x71, 0x12, 0x71, 0xaa, 0x2d, 0xbc, 0x86, 0xf2, 0x2f, 0x7e, 0x4d, 0x64, 0x0f, 0x6d, 0xa0, 0x1f, 0xe2, 0x24, 0xe2, 0x54, 0x5b, 0x78, 0x0d, 0xe5, 0x5f,
0x69, 0x22, 0xdf, 0x98, 0x1c, 0x7b, 0xc6, 0xc1, 0xf7, 0x27, 0xd6, 0x29, 0xb9, 0x07, 0xbe, 0xc3, 0xd2, 0x44, 0xbe, 0x31, 0x39, 0xf6, 0x8c, 0x83, 0xef, 0x4f, 0xac, 0x53, 0x72, 0x0f, 0x7c, 0x87,
0x4f, 0xbc, 0xda, 0xf9, 0xd9, 0x56, 0xef, 0x44, 0x5e, 0xf8, 0xe4, 0x4d, 0xf9, 0xa0, 0x5f, 0x12, 0x9f, 0x78, 0xb5, 0xf3, 0xb3, 0xad, 0xde, 0x89, 0xbc, 0xf0, 0xc9, 0x9b, 0xf2, 0x41, 0xbf, 0x24,
0xa9, 0x67, 0x56, 0xb7, 0x73, 0x3c, 0x21, 0x9b, 0x31, 0x93, 0x9f, 0xa0, 0x4c, 0x48, 0x76, 0xd1, 0x52, 0xcf, 0xac, 0x6e, 0xe7, 0x78, 0x42, 0x36, 0x63, 0x26, 0x3f, 0x41, 0x99, 0x90, 0xec, 0xa2,
0xd9, 0x52, 0x82, 0xd6, 0xeb, 0x92, 0xb7, 0x5e, 0xea, 0x1c, 0x4c, 0x09, 0xba, 0x93, 0xc9, 0x1c, 0xb3, 0xa5, 0x04, 0xad, 0xd7, 0x25, 0x6f, 0xbd, 0xd4, 0x39, 0x98, 0x12, 0x74, 0x27, 0x93, 0x39,
0x15, 0x7f, 0x01, 0xfe, 0xc5, 0xca, 0x6d, 0x71, 0xd1, 0x89, 0x1d, 0xbe, 0xc9, 0xee, 0x8a, 0x52, 0x2a, 0xfe, 0x02, 0xfc, 0x8b, 0x95, 0xdb, 0xe2, 0xa2, 0x13, 0x3b, 0x7c, 0x93, 0xdd, 0x11, 0xa5,
0xcf, 0xb2, 0x41, 0x24, 0x10, 0x65, 0x7a, 0xa7, 0xcd, 0x67, 0xf6, 0xa0, 0xd9, 0x1a, 0x34, 0xed, 0x9e, 0x65, 0x83, 0x48, 0x20, 0xca, 0xf4, 0x4e, 0x9b, 0xcf, 0xec, 0x41, 0xb3, 0x35, 0x68, 0xda,
0xf1, 0xe3, 0xd6, 0x88, 0x16, 0x20, 0x7c, 0x88, 0x8b, 0xc0, 0xdb, 0x94, 0xb4, 0xf7, 0xed, 0xc1, 0xe3, 0xc3, 0xd6, 0x88, 0x16, 0x20, 0x7c, 0x88, 0x8b, 0xc0, 0xdb, 0x90, 0xb4, 0xf7, 0xed, 0xc1,
0xfd, 0x41, 0x03, 0x39, 0xe5, 0xdf, 0x4f, 0x88, 0xec, 0xce, 0xa9, 0x63, 0x1d, 0xe6, 0xf6, 0xd8, 0x83, 0x41, 0x03, 0x39, 0xe5, 0xdf, 0x4f, 0x88, 0xec, 0xf6, 0xa9, 0x63, 0x1d, 0xe6, 0x76, 0x68,
0x3e, 0x19, 0xc8, 0xb5, 0x4c, 0x9a, 0xf2, 0xc1, 0x7d, 0x47, 0x31, 0xe5, 0x1d, 0x01, 0xf2, 0xe9, 0x9f, 0x0c, 0xe4, 0x5a, 0x26, 0x4d, 0xf9, 0xe0, 0xbe, 0xa3, 0x98, 0xf2, 0x8e, 0x00, 0xf9, 0xf4,
0x89, 0x3d, 0xb1, 0x68, 0xba, 0x59, 0x53, 0x3e, 0xe0, 0x6a, 0x0d, 0xad, 0x09, 0xcc, 0x15, 0x2b, 0xc4, 0x9e, 0x58, 0x34, 0xdd, 0xac, 0x29, 0x1f, 0x70, 0xb5, 0x86, 0xd6, 0x04, 0xe6, 0x8a, 0x15,
0x4c, 0xfc, 0xe9, 0xcd, 0x3f, 0x79, 0x8e, 0xf9, 0xc3, 0x39, 0x3b, 0x65, 0xe3, 0xea, 0x8f, 0x4b, 0x26, 0xfe, 0xf4, 0xe6, 0x9f, 0x3c, 0xc7, 0xfc, 0xe1, 0x9c, 0x9d, 0xb2, 0x71, 0xf5, 0xc7, 0xa5,
0x29, 0xba, 0xdc, 0x52, 0xe0, 0xea, 0x5b, 0x31, 0x19, 0xa5, 0x6f, 0x8b, 0x85, 0x67, 0x56, 0xb3, 0x14, 0x5d, 0x6e, 0x29, 0x70, 0xf5, 0xad, 0x98, 0x8c, 0xd2, 0xb7, 0xc4, 0xc2, 0x33, 0xab, 0xd9,
0x7f, 0x02, 0xdb, 0xa6, 0x63, 0xc3, 0x19, 0x0c, 0xa2, 0xf6, 0x08, 0x82, 0x0a, 0x8e, 0xa4, 0xc4, 0x3f, 0x81, 0x6d, 0xd3, 0xb1, 0xe1, 0x0c, 0x06, 0x51, 0x7b, 0x04, 0x41, 0x05, 0x47, 0x52, 0x62,
0x84, 0x59, 0x0b, 0x69, 0xce, 0x3f, 0xb3, 0x76, 0x80, 0xb7, 0x65, 0x3f, 0x24, 0x16, 0x28, 0x3b, 0xc2, 0xac, 0x85, 0x34, 0xe7, 0x9f, 0x59, 0xdb, 0xc0, 0xdb, 0xb4, 0x1f, 0x11, 0x0b, 0x94, 0x9d,
0x0b, 0x29, 0xa8, 0x29, 0x9d, 0xcd, 0x07, 0x47, 0xf7, 0x51, 0x33, 0x00, 0xa4, 0x06, 0x7d, 0x45, 0x85, 0x14, 0xd4, 0x94, 0xce, 0xe6, 0x83, 0xa3, 0xfb, 0xa8, 0x19, 0x00, 0x52, 0x83, 0xbe, 0x2c,
0x64, 0x0e, 0xbb, 0x4f, 0xac, 0xf1, 0x31, 0x68, 0x29, 0x0d, 0xc3, 0xce, 0x1b, 0x57, 0x3c, 0x8e, 0x32, 0x07, 0xdd, 0x27, 0xd6, 0xf8, 0x18, 0xb4, 0x94, 0x86, 0x61, 0xe7, 0x8d, 0x2b, 0x1e, 0xc7,
0xbb, 0xac, 0x4b, 0x0f, 0xec, 0x9e, 0x3d, 0x32, 0x5d, 0x28, 0x1c, 0x11, 0xb2, 0x63, 0xbb, 0x6f, 0x5d, 0xd6, 0xa5, 0x87, 0x76, 0xcf, 0x1e, 0x99, 0x2e, 0x14, 0x8e, 0x08, 0xd9, 0xb1, 0xdd, 0xb7,
0x49, 0x7d, 0x67, 0x28, 0xb3, 0x5d, 0x9f, 0xc5, 0xdb, 0x07, 0x90, 0x13, 0xc1, 0x1c, 0xbc, 0x7e, 0xa4, 0xbe, 0x33, 0x94, 0xd9, 0xae, 0xcf, 0xe2, 0xed, 0x01, 0xc8, 0x89, 0x60, 0x0e, 0x5e, 0xbf,
0x55, 0x3a, 0x7a, 0x88, 0xe7, 0xd7, 0x92, 0xa0, 0xfa, 0x1c, 0x1d, 0xa2, 0xf3, 0xac, 0xbe, 0x88, 0x2a, 0x1d, 0x3d, 0xc0, 0xf3, 0x6b, 0x49, 0x50, 0x7d, 0x8e, 0x0e, 0xd1, 0x79, 0x56, 0x5f, 0x44,
0x0e, 0x75, 0xda, 0x78, 0x2c, 0x81, 0x00, 0x8d, 0xc5, 0x9d, 0xfb, 0xbc, 0xf8, 0x16, 0x84, 0x3e, 0x87, 0x3a, 0x6d, 0x3c, 0x96, 0x40, 0x80, 0xc6, 0xe2, 0xce, 0x7d, 0x5e, 0x7c, 0x0b, 0x42, 0x9f,
0xc7, 0xa0, 0x17, 0xfa, 0x64, 0xb8, 0xc9, 0x52, 0x3c, 0x90, 0xa1, 0x4f, 0xc6, 0x9a, 0xd7, 0x44, 0x63, 0xd0, 0x0b, 0x7d, 0x32, 0xdc, 0x64, 0x29, 0x1e, 0xc8, 0xd0, 0x27, 0x63, 0xcd, 0x6b, 0x22,
0x92, 0xdc, 0xc6, 0x34, 0x61, 0x6e, 0x60, 0x56, 0x82, 0x34, 0xb1, 0x65, 0x6e, 0x6c, 0xec, 0x42, 0x49, 0x6e, 0x63, 0x9a, 0x30, 0xd7, 0x31, 0x2b, 0x41, 0x9a, 0xd8, 0x34, 0xd7, 0xd7, 0x77, 0x20,
0x5a, 0xc2, 0x04, 0xf5, 0xe8, 0xdd, 0x8d, 0x62, 0x4c, 0x51, 0xec, 0x6f, 0x69, 0x22, 0xbe, 0xf1, 0x2d, 0x61, 0x82, 0x7a, 0xfc, 0xee, 0x7a, 0x31, 0xa6, 0x28, 0xf6, 0xb7, 0x34, 0x11, 0x5f, 0x7f,
0x9c, 0xd4, 0x82, 0xd3, 0x70, 0x76, 0x34, 0xfe, 0x36, 0x56, 0x45, 0xa2, 0x6f, 0x8f, 0x2c, 0xfd, 0x4e, 0x6a, 0xc1, 0x69, 0x38, 0x3b, 0x1a, 0x7f, 0x1b, 0x2b, 0x22, 0xd1, 0xb7, 0x47, 0x96, 0x7e,
0xc2, 0x8c, 0x59, 0x96, 0x3a, 0xf4, 0xbe, 0x94, 0xab, 0x5c, 0xb0, 0x62, 0x12, 0xde, 0x78, 0x43, 0x61, 0xc6, 0x2c, 0x4b, 0x1d, 0x7a, 0x5f, 0xca, 0x55, 0x2e, 0x58, 0x31, 0x09, 0x6f, 0xbc, 0x21,
0x24, 0x26, 0x16, 0xd8, 0x9c, 0xc9, 0x3b, 0x96, 0x03, 0x20, 0xc0, 0x80, 0x30, 0x0a, 0x71, 0xe5, 0x12, 0x13, 0x0b, 0x6c, 0xce, 0xe4, 0x1d, 0xcb, 0x01, 0x10, 0x60, 0x40, 0x18, 0x85, 0xb8, 0x72,
0x10, 0x5e, 0xc9, 0x4c, 0x68, 0x97, 0xa6, 0xc7, 0x90, 0xf2, 0x7b, 0xa2, 0xf8, 0xc0, 0xee, 0x0f, 0x00, 0xaf, 0x64, 0x26, 0xb4, 0x4b, 0xd3, 0x63, 0x48, 0xf9, 0x3d, 0x51, 0x7c, 0x68, 0xf7, 0x87,
0x7b, 0xd6, 0x73, 0x18, 0xc9, 0x1a, 0x8c, 0x21, 0x65, 0xa3, 0x9e, 0xdb, 0xdd, 0x11, 0x45, 0x11, 0x3d, 0xeb, 0x39, 0x8c, 0x64, 0x0d, 0xc6, 0x90, 0xb2, 0x51, 0xcf, 0xed, 0xee, 0x88, 0xa2, 0x08,
0xba, 0xb0, 0xa5, 0x07, 0xdc, 0xd5, 0x63, 0x0b, 0xa2, 0xc3, 0x11, 0x07, 0x4c, 0x7e, 0x42, 0xf4, 0x5d, 0xd8, 0xd2, 0x03, 0xee, 0xea, 0xb1, 0x05, 0xd1, 0xe1, 0x88, 0x03, 0x26, 0x3f, 0x21, 0x7a,
0xe4, 0xb8, 0x3b, 0xc2, 0x00, 0x82, 0x71, 0x5e, 0x3e, 0x94, 0xb7, 0x44, 0x81, 0x0f, 0xfa, 0x63, 0x72, 0xdc, 0x1d, 0x61, 0x00, 0xc1, 0x38, 0x2f, 0x1f, 0xca, 0x9b, 0xa2, 0xc0, 0x07, 0xfd, 0x31,
0x1e, 0xb8, 0x7c, 0x47, 0xe4, 0x9d, 0x26, 0xba, 0xbd, 0x86, 0x85, 0xfb, 0x60, 0xc3, 0x6c, 0xc0, 0x0f, 0x5c, 0xbe, 0x23, 0xf2, 0x4e, 0x13, 0xdd, 0x5e, 0xc3, 0xc2, 0x7d, 0xb0, 0x6e, 0x36, 0x60,
0x6a, 0xc2, 0xb2, 0x36, 0x76, 0x37, 0x60, 0x2d, 0xe1, 0xc7, 0xc1, 0xfb, 0x0d, 0xdf, 0x52, 0x5e, 0x35, 0x61, 0x59, 0x1b, 0x3b, 0xeb, 0xb0, 0x96, 0xf0, 0x63, 0xff, 0xfd, 0x86, 0x6f, 0x29, 0xaf,
0x13, 0x79, 0xd7, 0xf7, 0x7d, 0x6b, 0x42, 0x3d, 0x98, 0x10, 0xd2, 0xf5, 0x58, 0x46, 0x2b, 0xa7, 0x89, 0xbc, 0xeb, 0xfb, 0x9e, 0x35, 0xa1, 0x1e, 0x4c, 0x08, 0xe9, 0x7a, 0x2c, 0xa3, 0x95, 0xd3,
0x45, 0x72, 0xa3, 0x3f, 0x9c, 0x9c, 0x96, 0x7f, 0x5e, 0xe4, 0x18, 0xf4, 0xa8, 0x0b, 0xce, 0xae, 0x22, 0xb9, 0xde, 0x1f, 0x4e, 0x4e, 0xcb, 0x3f, 0x2f, 0x72, 0x0c, 0x7a, 0xdc, 0x05, 0x67, 0x57,
0x89, 0x74, 0x9f, 0xe7, 0xab, 0xd1, 0x99, 0x4b, 0xd5, 0x94, 0x87, 0x73, 0x7e, 0x9b, 0x0e, 0x7a, 0x45, 0xba, 0xcf, 0xf3, 0xd5, 0xe8, 0xcc, 0xa5, 0x6a, 0xca, 0xc3, 0x39, 0xbf, 0x4d, 0x07, 0xbd,
0xb1, 0x2a, 0xd2, 0x4a, 0x2c, 0xe5, 0xad, 0x1e, 0x53, 0xb7, 0xba, 0x0c, 0x0a, 0x71, 0x25, 0x28, 0x58, 0x15, 0x69, 0x25, 0x96, 0xf2, 0x56, 0x8f, 0xa9, 0x5b, 0x5d, 0x06, 0x85, 0xb8, 0x12, 0x14,
0x94, 0x77, 0x44, 0x5a, 0x66, 0xc0, 0x31, 0x65, 0x75, 0x59, 0xaf, 0x49, 0x31, 0xc9, 0x37, 0x9f, 0xca, 0xdb, 0x22, 0x2d, 0x33, 0xe0, 0x98, 0xb2, 0xba, 0xac, 0xd7, 0xa4, 0x98, 0xe4, 0x9b, 0xcf,
0x93, 0x6d, 0xf2, 0x0a, 0x19, 0xe4, 0x46, 0x82, 0x65, 0x84, 0x0c, 0x9d, 0x82, 0x9a, 0xa4, 0xdc, 0xc9, 0x36, 0x79, 0x85, 0x0c, 0x72, 0x23, 0xc1, 0x32, 0x42, 0x86, 0x4e, 0x41, 0x4d, 0x52, 0x6e,
0x7e, 0x2f, 0x29, 0x32, 0xce, 0x4a, 0x81, 0xc4, 0x53, 0xb2, 0x48, 0x22, 0x53, 0x4e, 0x11, 0x9f, 0xbf, 0x97, 0x14, 0x19, 0x67, 0xa5, 0x40, 0xe2, 0x29, 0x59, 0x24, 0x91, 0x29, 0xa7, 0x88, 0x4f,
0xa4, 0xb2, 0x08, 0x3a, 0xd3, 0x5c, 0x08, 0x71, 0x74, 0xc7, 0x8a, 0x3d, 0x25, 0x0b, 0x1f, 0xb7, 0x52, 0x59, 0x04, 0x9d, 0x69, 0x2e, 0x84, 0x38, 0xba, 0x63, 0xc5, 0x9e, 0x92, 0x85, 0x8f, 0xdb,
0x13, 0xce, 0x16, 0x71, 0xb7, 0x3c, 0x4f, 0xc9, 0xd2, 0x46, 0xbf, 0x29, 0xb2, 0x6e, 0x31, 0x43, 0x09, 0x67, 0x8b, 0xb8, 0x5b, 0x9e, 0xa7, 0x64, 0x69, 0xa3, 0xdf, 0x14, 0x59, 0xb7, 0x98, 0xa1,
0xf1, 0x98, 0x6b, 0xf1, 0x8c, 0x53, 0xbd, 0x28, 0x08, 0x30, 0x90, 0xf4, 0x0a, 0xef, 0x4c, 0xdb, 0x78, 0xcc, 0xb5, 0x78, 0xc6, 0xa9, 0x5e, 0x14, 0x04, 0x18, 0x48, 0x7a, 0x85, 0x77, 0xa6, 0xed,
0x3b, 0x9e, 0x64, 0x9c, 0x92, 0x84, 0xee, 0xd0, 0x9d, 0x2a, 0x3b, 0xcd, 0x45, 0x88, 0x07, 0x00, 0x1d, 0x4f, 0x32, 0x4e, 0x49, 0x42, 0x77, 0xe8, 0x4e, 0x95, 0x9d, 0xe6, 0x22, 0xc4, 0x03, 0x80,
0x0b, 0x69, 0xa5, 0xa4, 0x4e, 0x73, 0xa1, 0x01, 0x80, 0x34, 0x97, 0x16, 0xb4, 0xf5, 0xbd, 0xfa, 0x85, 0xb4, 0x52, 0x52, 0xa7, 0xb9, 0xd0, 0x00, 0x40, 0x9a, 0x4b, 0x0b, 0xda, 0xfa, 0x5e, 0xfd,
0x39, 0x25, 0xcb, 0x09, 0x58, 0xce, 0x8c, 0x53, 0x40, 0xd0, 0xbe, 0xf4, 0x8a, 0xe5, 0x34, 0x17, 0x9c, 0x92, 0xe5, 0x04, 0x2c, 0x67, 0xc6, 0x29, 0x20, 0x68, 0x5f, 0x7a, 0xc5, 0x72, 0x9a, 0x8b,
0x0d, 0xfa, 0x9b, 0x08, 0x91, 0xcb, 0x0f, 0x21, 0x60, 0x76, 0x65, 0x9c, 0xe6, 0xca, 0x18, 0x26, 0x06, 0xfd, 0x4d, 0x84, 0xc8, 0xe5, 0x87, 0x10, 0x30, 0xbb, 0x32, 0x4e, 0x73, 0x65, 0x0c, 0x93,
0x95, 0xe6, 0x82, 0x98, 0x42, 0x82, 0x52, 0x05, 0xa7, 0x64, 0x15, 0xac, 0xdf, 0x20, 0x73, 0x72, 0x4a, 0x73, 0x41, 0x4c, 0x21, 0x41, 0xa9, 0x82, 0x53, 0xb2, 0x0a, 0xd6, 0x6f, 0x90, 0x39, 0x39,
0x52, 0x79, 0xaf, 0xe2, 0x4d, 0x73, 0x95, 0xe1, 0xf5, 0xd3, 0x91, 0xcd, 0xad, 0x6e, 0xd3, 0x5c, 0xa9, 0xbc, 0x57, 0xf1, 0xa6, 0xb9, 0xca, 0xf0, 0xfa, 0xe9, 0xc8, 0xe6, 0x56, 0xb7, 0x69, 0xae,
0x47, 0xe8, 0xab, 0xf8, 0xbe, 0x50, 0xdf, 0x70, 0x5c, 0xc3, 0x20, 0x58, 0xf2, 0x84, 0xe7, 0xbc, 0x23, 0xf4, 0x15, 0x7c, 0x5f, 0xa8, 0x6f, 0x38, 0xae, 0x61, 0x10, 0x2c, 0x79, 0xc2, 0x73, 0xde,
0x53, 0x19, 0x03, 0xeb, 0x32, 0x82, 0xc0, 0xab, 0xa4, 0xdd, 0xb0, 0x88, 0xbc, 0xbd, 0xee, 0xa0, 0xa9, 0x8c, 0x81, 0x75, 0x19, 0x41, 0xe0, 0x55, 0xd2, 0x6e, 0x58, 0x44, 0xde, 0x6e, 0x77, 0xd0,
0x0d, 0xe7, 0x38, 0x5c, 0x89, 0x38, 0xfc, 0x84, 0x3e, 0x6c, 0x91, 0x1a, 0xd8, 0xc5, 0xbe, 0x22, 0x86, 0x73, 0x1c, 0xae, 0x44, 0x1c, 0x7e, 0x42, 0x1f, 0xb6, 0x48, 0x0d, 0xec, 0x60, 0x5f, 0x91,
0xf5, 0x25, 0xde, 0x96, 0x9d, 0xd8, 0x04, 0xe9, 0x3d, 0x09, 0x9d, 0xad, 0x01, 0x1c, 0xc8, 0x88, 0xfa, 0x12, 0x6f, 0xcb, 0x4e, 0x6c, 0x82, 0xf4, 0x9e, 0x84, 0xce, 0xd6, 0x00, 0x0e, 0x64, 0xc4,
0x37, 0x68, 0x0d, 0xcc, 0x44, 0x1b, 0x1a, 0xf4, 0x37, 0x44, 0x7c, 0x7c, 0x72, 0x58, 0xd2, 0x83, 0x1b, 0xb4, 0x06, 0x66, 0xa2, 0x0d, 0x0d, 0xfa, 0x1b, 0x22, 0x3e, 0x3e, 0x39, 0x28, 0xe9, 0xc1,
0x9f, 0x37, 0xf6, 0x4f, 0x0e, 0x1d, 0x57, 0x4c, 0x44, 0x80, 0xfd, 0x0c, 0x08, 0xb4, 0xf9, 0x73, 0xcf, 0x1b, 0x7b, 0x27, 0x07, 0x8e, 0x2b, 0x26, 0x22, 0xc0, 0x7e, 0x06, 0x04, 0xda, 0xfc, 0x39,
0xd6, 0xc8, 0x2e, 0x5d, 0xa0, 0x25, 0x7c, 0xc9, 0x4c, 0x43, 0xcb, 0x07, 0xd0, 0x70, 0xce, 0xe0, 0x6b, 0x64, 0x97, 0x2e, 0xd0, 0x12, 0xbe, 0x64, 0xa6, 0xa1, 0xe5, 0x03, 0x68, 0x38, 0x67, 0xf0,
0x07, 0x87, 0xbb, 0x9c, 0x62, 0x17, 0x4e, 0xe8, 0xda, 0x40, 0x9e, 0x14, 0xea, 0xda, 0x9a, 0xa9, 0x83, 0xc3, 0x5d, 0x4e, 0xb1, 0x0b, 0x27, 0x74, 0x6d, 0x20, 0x4f, 0x0a, 0x75, 0x6d, 0xd5, 0xd4,
0x0d, 0xca, 0x07, 0x22, 0xef, 0x14, 0x12, 0x34, 0x5f, 0x03, 0x77, 0x12, 0x98, 0xa5, 0xfd, 0x39, 0x06, 0xe5, 0x7d, 0x91, 0x77, 0x0a, 0x09, 0x9a, 0xaf, 0x81, 0x3b, 0x09, 0xcc, 0xd2, 0xfe, 0x9c,
0x6f, 0x5c, 0x53, 0x53, 0x94, 0x07, 0xe3, 0x74, 0x21, 0xa1, 0xe5, 0x62, 0xc0, 0x15, 0xad, 0xfc, 0x37, 0xae, 0xa9, 0x29, 0xca, 0x83, 0x71, 0xba, 0x90, 0xd0, 0x72, 0x31, 0xe0, 0x8a, 0x56, 0xfe,
0x03, 0x38, 0xa3, 0xec, 0x40, 0x74, 0x74, 0x2f, 0x4d, 0x61, 0x83, 0x1e, 0xc2, 0xce, 0x18, 0x93, 0x01, 0x9c, 0x51, 0xb6, 0x21, 0x3a, 0xba, 0x97, 0xa6, 0xb0, 0x41, 0x0f, 0x60, 0x67, 0x8c, 0xc9,
0xd9, 0x8c, 0x29, 0x1f, 0xf4, 0xd7, 0x44, 0x9e, 0x7e, 0x38, 0x05, 0x60, 0xcc, 0xbd, 0x5f, 0xc8, 0x6c, 0xc6, 0x94, 0x0f, 0xfa, 0x6b, 0x22, 0x4f, 0x3f, 0x9c, 0x02, 0x30, 0xe6, 0xde, 0x2f, 0xe4,
0x51, 0x3b, 0x57, 0x7d, 0xb0, 0xe3, 0xe1, 0x25, 0x8e, 0x39, 0x92, 0xd1, 0x6f, 0xfd, 0x33, 0x22, 0xa8, 0x9d, 0xab, 0x3e, 0xd8, 0xf1, 0xf0, 0x12, 0xc7, 0x1c, 0xc9, 0xe8, 0xb7, 0xfe, 0x19, 0x91,
0x87, 0x7f, 0x1d, 0x66, 0xc2, 0x3d, 0xb0, 0x0a, 0x6c, 0x66, 0xe2, 0x1b, 0x62, 0x8e, 0xde, 0xbe, 0xc3, 0xbf, 0x0e, 0x33, 0xe1, 0x1e, 0x58, 0x05, 0x36, 0x33, 0xf1, 0x0d, 0x31, 0x47, 0x6f, 0xdf,
0x0b, 0x4b, 0xbb, 0x77, 0x09, 0x79, 0xd9, 0xc1, 0xc0, 0x92, 0x48, 0xcb, 0x50, 0x30, 0xa6, 0x4f, 0x85, 0xa5, 0xdd, 0xbb, 0x84, 0xbc, 0xec, 0x60, 0x60, 0x49, 0xa4, 0x65, 0x28, 0x18, 0xd3, 0x27,
0x56, 0x59, 0xd3, 0x79, 0xc4, 0xf0, 0x4a, 0x95, 0x80, 0x4c, 0xf7, 0x69, 0x93, 0x9f, 0xca, 0xf7, 0xab, 0xac, 0xe9, 0x3c, 0x62, 0x78, 0xa5, 0x4a, 0x40, 0xa6, 0xfb, 0xb4, 0xc9, 0x4f, 0xe5, 0x07,
0x45, 0x86, 0xb2, 0x14, 0x1c, 0x63, 0xf5, 0xb2, 0xd0, 0x3a, 0x25, 0x8b, 0x72, 0xe4, 0x45, 0xe5, 0x22, 0x43, 0x59, 0x0a, 0x8e, 0xb1, 0x7a, 0x59, 0x68, 0x9d, 0x92, 0x45, 0x39, 0xf2, 0xa2, 0x72,
0x98, 0xcf, 0xdd, 0x4b, 0x5b, 0xa6, 0xd6, 0x59, 0x5c, 0x10, 0xda, 0x16, 0x9e, 0xbb, 0x9f, 0x73, 0xcc, 0xe7, 0xee, 0xa5, 0x4d, 0x53, 0xeb, 0x2c, 0x2e, 0x08, 0x6d, 0x13, 0xcf, 0xdd, 0xcf, 0x39,
0x98, 0xd6, 0x9e, 0x97, 0x1b, 0x6c, 0x02, 0x4e, 0xa5, 0x51, 0x26, 0xa0, 0x5b, 0x9a, 0x78, 0x65, 0x4c, 0x6b, 0xcf, 0xcb, 0x0d, 0x36, 0x01, 0xa7, 0xd2, 0x28, 0x13, 0xd0, 0x2d, 0x4d, 0xbc, 0x32,
0xca, 0x04, 0x3e, 0x9d, 0xf2, 0xf7, 0x3b, 0xed, 0x14, 0xcf, 0xf9, 0xb4, 0x3d, 0xc1, 0xf1, 0x3d, 0x65, 0x02, 0x9f, 0x4e, 0xf9, 0xfb, 0x9d, 0x76, 0x5a, 0xae, 0x8a, 0x39, 0xda, 0x9e, 0xe0, 0xf8,
0x1b, 0xe6, 0x87, 0xdd, 0x6d, 0x3a, 0x27, 0xc1, 0x39, 0xbe, 0x5d, 0xfe, 0x34, 0x21, 0xe6, 0x39, 0xae, 0x0d, 0xf3, 0xc3, 0xee, 0x36, 0x9d, 0x93, 0xe0, 0x1c, 0xdf, 0xc6, 0x77, 0x60, 0x3d, 0x6f,
0x88, 0xbe, 0xdf, 0x9d, 0x1c, 0xef, 0xb4, 0x86, 0xfa, 0x23, 0x91, 0xc7, 0xf8, 0xd9, 0xec, 0xb7, 0x1d, 0xca, 0x13, 0x27, 0xbc, 0x03, 0x7a, 0x28, 0x7f, 0x9a, 0x10, 0xf3, 0x1c, 0x5a, 0xdf, 0xef,
0x86, 0x43, 0xdc, 0xa8, 0x1a, 0x1d, 0x2a, 0x6e, 0x4f, 0x05, 0x65, 0xc6, 0x2f, 0xed, 0x02, 0x78, 0x4e, 0x8e, 0xb7, 0x5b, 0x43, 0xfd, 0xb1, 0xc8, 0x63, 0x54, 0x6d, 0xf6, 0x5b, 0xc3, 0x21, 0x6e,
0x47, 0x62, 0x37, 0x06, 0x93, 0xd1, 0xa9, 0x99, 0x1b, 0x78, 0x2d, 0x70, 0xd4, 0xc9, 0xf5, 0xc7, 0x5f, 0x8d, 0x8e, 0x1a, 0xb7, 0xa7, 0x42, 0x35, 0xe3, 0x97, 0x76, 0x00, 0xbc, 0x2d, 0xb1, 0xeb,
0x1d, 0xd7, 0x58, 0x8c, 0x8c, 0xdd, 0x0a, 0x35, 0xb6, 0x33, 0xee, 0xf8, 0x6c, 0x89, 0xbe, 0xdb, 0x83, 0xc9, 0xe8, 0xd4, 0xcc, 0x0d, 0xbc, 0x16, 0x38, 0x00, 0xe5, 0xfa, 0xe3, 0x8e, 0x6b, 0x2c,
0x80, 0x8e, 0x61, 0xe4, 0x75, 0x6d, 0xc5, 0xcf, 0x70, 0x0c, 0x83, 0x84, 0xdf, 0xb1, 0x43, 0xaf, 0x46, 0xc6, 0x6e, 0x85, 0x1a, 0xdb, 0x1e, 0x77, 0x7c, 0xb6, 0x44, 0xdf, 0x6d, 0x40, 0xc7, 0x30,
0x05, 0x4a, 0x75, 0x81, 0x1b, 0x69, 0x62, 0x63, 0x91, 0x44, 0x5a, 0xc9, 0x19, 0xaf, 0x87, 0xda, 0x1e, 0xbb, 0xb6, 0xe2, 0x67, 0x38, 0x86, 0xa1, 0xc3, 0xef, 0xd8, 0x81, 0xd7, 0x02, 0x05, 0xbc,
0x82, 0x98, 0x74, 0x60, 0xc3, 0x7f, 0xa4, 0x21, 0xdc, 0x82, 0xf4, 0xb8, 0xf8, 0x05, 0x51, 0x0c, 0xc0, 0xed, 0x35, 0xb1, 0xb1, 0x74, 0x22, 0x05, 0xe5, 0x8c, 0xd7, 0x43, 0x6d, 0x41, 0xa4, 0xda,
0xce, 0x5f, 0x3d, 0x7b, 0x27, 0x67, 0x9c, 0xbd, 0xb3, 0x7c, 0xf6, 0xae, 0xc7, 0xee, 0x6a, 0x8b, 0xb7, 0xe1, 0x3f, 0xd2, 0x10, 0x6e, 0x4c, 0x7a, 0x5c, 0xfc, 0x82, 0x28, 0x06, 0xe7, 0xaf, 0x9e,
0xef, 0x89, 0x42, 0x60, 0xca, 0x2a, 0x5d, 0x97, 0xf4, 0xb7, 0x55, 0x7a, 0xce, 0xb8, 0xac, 0x7c, 0xc8, 0x93, 0x33, 0x4e, 0xe4, 0x59, 0x3e, 0x91, 0xd7, 0x63, 0xf7, 0xb4, 0xc5, 0xf7, 0x44, 0x21,
0x3d, 0x56, 0x5f, 0xad, 0x6a, 0x17, 0xfc, 0x0a, 0x4e, 0x5f, 0x35, 0x9c, 0x89, 0xa8, 0x09, 0x88, 0x30, 0x65, 0x95, 0xae, 0x4b, 0xfa, 0xdb, 0x2a, 0x3d, 0x67, 0x5c, 0x56, 0xbe, 0x29, 0xab, 0x2f,
0xff, 0x8e, 0x98, 0xf3, 0x4d, 0x59, 0x25, 0x67, 0xcf, 0x98, 0x54, 0xf9, 0x17, 0x93, 0x22, 0xd9, 0x5c, 0xb5, 0x0b, 0x7e, 0x05, 0xa7, 0xaf, 0x1a, 0xce, 0x44, 0x54, 0x0a, 0xc4, 0x7f, 0x47, 0xcc,
0x18, 0x58, 0x76, 0x5b, 0xbf, 0xec, 0xcf, 0x88, 0x5f, 0x7c, 0xc9, 0xc9, 0x86, 0x57, 0x02, 0xd9, 0xf9, 0xa6, 0xac, 0x92, 0xb3, 0x67, 0x4c, 0xaa, 0xfc, 0x8b, 0x49, 0x91, 0x6c, 0x0c, 0x2c, 0xbb,
0x10, 0x7a, 0x9c, 0x5c, 0x78, 0x25, 0x90, 0x0b, 0x9d, 0x2e, 0x08, 0xd8, 0xd7, 0xa7, 0x32, 0x21, 0xad, 0x5f, 0xf6, 0xe7, 0xc9, 0x2f, 0xbe, 0xe4, 0xe4, 0xc8, 0x2b, 0x81, 0x1c, 0x09, 0x3d, 0x4e,
0x74, 0x7a, 0x69, 0xf0, 0xfa, 0x54, 0x1a, 0xf4, 0xba, 0x81, 0x7d, 0x35, 0x98, 0x03, 0xa1, 0xd7, 0x86, 0xbc, 0x12, 0xc8, 0x90, 0x4e, 0x17, 0x84, 0xf1, 0xeb, 0x53, 0xf9, 0x11, 0x3a, 0xbd, 0xe4,
0xcd, 0x7f, 0x57, 0x83, 0xf9, 0xcf, 0xed, 0x04, 0xe6, 0x95, 0x40, 0xee, 0x23, 0x97, 0x64, 0xd6, 0x78, 0x7d, 0x2a, 0x39, 0x7a, 0xdd, 0xc0, 0xbe, 0x1a, 0xcc, 0x8c, 0xd0, 0xeb, 0x66, 0xc5, 0xab,
0xbb, 0x1a, 0xcc, 0x7a, 0xc4, 0xe3, 0x7c, 0x77, 0x35, 0x98, 0xef, 0xa8, 0x93, 0xf3, 0xdb, 0x95, 0xc1, 0xac, 0xe8, 0x76, 0x02, 0xf3, 0x4a, 0x20, 0x23, 0x92, 0x4b, 0x32, 0x17, 0x5e, 0x0d, 0xe6,
0x40, 0x7e, 0x23, 0xa3, 0x32, 0xb1, 0x5d, 0x0d, 0x26, 0x36, 0xc9, 0x53, 0x3c, 0x55, 0xb3, 0x9a, 0x42, 0xe2, 0x71, 0x16, 0xbc, 0x1a, 0xcc, 0x82, 0xd4, 0xc9, 0x59, 0xef, 0x4a, 0x20, 0xeb, 0x91,
0xdb, 0x09, 0x9e, 0x1a, 0x81, 0x94, 0x16, 0x7e, 0xae, 0xa7, 0x77, 0x41, 0xe1, 0xbd, 0x86, 0xcb, 0x51, 0x99, 0xee, 0xae, 0x06, 0xd3, 0x9d, 0xe4, 0x29, 0x9e, 0xaa, 0xb9, 0xce, 0xed, 0x04, 0x4f,
0xe6, 0x1c, 0x39, 0x0b, 0x11, 0x1f, 0xd8, 0x69, 0x35, 0x9d, 0x23, 0x97, 0x21, 0xd2, 0x6d, 0x2e, 0x8d, 0x40, 0xa2, 0x0b, 0x3f, 0xed, 0xd3, 0xbb, 0xa0, 0xa0, 0x5f, 0xc3, 0x65, 0x73, 0x0e, 0xa2,
0x75, 0x8b, 0x14, 0xa3, 0x14, 0x59, 0xd2, 0xcb, 0x5f, 0xda, 0x6c, 0x52, 0xac, 0xa2, 0x79, 0xc9, 0x85, 0x88, 0xcf, 0xee, 0xb4, 0x9a, 0xce, 0x41, 0xcc, 0x10, 0xe9, 0x36, 0x17, 0xc0, 0x45, 0x8a,
0xd3, 0xfb, 0x2d, 0x08, 0x46, 0xcd, 0x47, 0xad, 0x51, 0x07, 0x80, 0xcd, 0x83, 0x56, 0xc7, 0xbd, 0x5c, 0x8a, 0x2c, 0xe9, 0xe5, 0x2f, 0x6d, 0x34, 0x29, 0x82, 0xd1, 0xbc, 0xe4, 0x99, 0xfe, 0x16,
0x2e, 0xc0, 0xf7, 0x9f, 0x6b, 0x73, 0x0f, 0xde, 0x2a, 0x5c, 0x72, 0xc4, 0x75, 0x44, 0xbd, 0x1a, 0x84, 0xa8, 0xe6, 0xe3, 0xd6, 0xa8, 0x03, 0xc0, 0xe6, 0x7e, 0xab, 0xe3, 0x5e, 0x22, 0xe0, 0xfb,
0xcb, 0x6b, 0xf1, 0x32, 0x2e, 0x9a, 0x34, 0x46, 0x51, 0x6f, 0x81, 0xa3, 0xde, 0x3a, 0x9c, 0x2e, 0xcf, 0xb5, 0xb9, 0x07, 0xef, 0x1a, 0x2e, 0x39, 0xe2, 0x3a, 0xa2, 0x5e, 0x8d, 0xe5, 0xb5, 0x78,
0x4f, 0x06, 0x70, 0x22, 0x5e, 0xcf, 0x8a, 0xf4, 0xc4, 0x1e, 0xf5, 0x5b, 0x13, 0xbb, 0xfc, 0x43, 0x19, 0x17, 0x4d, 0x1a, 0xa3, 0x58, 0xb8, 0xc0, 0xb1, 0x70, 0x0d, 0xce, 0x9c, 0x27, 0x03, 0x38,
0x4d, 0x08, 0x38, 0x31, 0xf7, 0xa1, 0xe3, 0x29, 0xd4, 0xba, 0x90, 0xf6, 0xfa, 0xad, 0x27, 0x10, 0x27, 0xaf, 0x65, 0x45, 0x7a, 0x62, 0x8f, 0xfa, 0xad, 0x89, 0x5d, 0xfe, 0xa1, 0x26, 0x04, 0x9c,
0x3f, 0xac, 0xe6, 0xe3, 0x91, 0xb3, 0x0f, 0xb2, 0xd8, 0xb4, 0x63, 0x3d, 0x00, 0x89, 0x97, 0x9c, 0xa3, 0xfb, 0xd0, 0xf1, 0x14, 0x2a, 0x60, 0x48, 0x86, 0xfd, 0xd6, 0x13, 0x88, 0x1f, 0x56, 0xf3,
0xc3, 0x38, 0x69, 0x87, 0x24, 0xc9, 0x87, 0xf3, 0x8b, 0x7c, 0xbc, 0x4c, 0xf1, 0x3b, 0x74, 0x0e, 0x70, 0xe4, 0xec, 0x83, 0x2c, 0x36, 0x6d, 0x5b, 0x0f, 0x41, 0xe2, 0x25, 0xe7, 0x88, 0x4e, 0xda,
0x98, 0xb2, 0x62, 0x48, 0xf3, 0xdb, 0xa3, 0x27, 0x94, 0xfc, 0xc4, 0xea, 0x0f, 0x9b, 0x8f, 0x49, 0x21, 0x49, 0xf2, 0x91, 0xfd, 0x22, 0x1f, 0x3a, 0x53, 0xfc, 0x0e, 0x9d, 0x63, 0xa7, 0xac, 0x23,
0x2a, 0x28, 0x87, 0x24, 0x3e, 0x3f, 0x80, 0x5d, 0x1c, 0x87, 0xd4, 0x48, 0x22, 0x39, 0xe3, 0xbd, 0xd2, 0xfc, 0xf6, 0xe8, 0x09, 0x25, 0x3f, 0xb1, 0xfa, 0xc3, 0xe6, 0x21, 0x49, 0x05, 0xe5, 0x90,
0x20, 0x0e, 0xf2, 0x60, 0x1c, 0x62, 0x1f, 0xc9, 0x26, 0x67, 0x2c, 0x28, 0x27, 0x02, 0x99, 0x84, 0xc4, 0xe7, 0x87, 0xb0, 0x8b, 0xe3, 0x90, 0x30, 0x49, 0x24, 0x67, 0xbc, 0x17, 0xc4, 0x41, 0x76,
0x10, 0x06, 0xfd, 0xee, 0xbc, 0xef, 0x14, 0x44, 0x7c, 0xb3, 0xd1, 0xc0, 0x2c, 0x0f, 0x7f, 0x2a, 0x8c, 0x43, 0xec, 0x23, 0xd9, 0xe4, 0x8c, 0x05, 0xe5, 0x9c, 0x20, 0x53, 0x13, 0xc2, 0xa0, 0xdf,
0x45, 0xad, 0xfe, 0x39, 0x91, 0xe9, 0x8c, 0x2c, 0x0b, 0xc3, 0xc3, 0xec, 0xea, 0xe2, 0x43, 0xca, 0x9d, 0xf7, 0x9d, 0x82, 0x88, 0x6f, 0x34, 0x1a, 0x98, 0xfb, 0xe1, 0x4f, 0xa5, 0xa8, 0xd5, 0x3f,
0x6a, 0x2e, 0xa8, 0x0e, 0x47, 0xe3, 0xc7, 0xb2, 0xbe, 0xd0, 0x43, 0x0a, 0xd8, 0xd2, 0x1f, 0xc8, 0x27, 0x32, 0x9d, 0x91, 0x65, 0x61, 0x78, 0x98, 0x5d, 0x73, 0x7c, 0x48, 0xb9, 0xce, 0x05, 0xd5,
0xeb, 0x93, 0x45, 0xaf, 0x3b, 0x58, 0x91, 0x98, 0x8e, 0x8d, 0xfa, 0x1e, 0x14, 0x7c, 0xcd, 0xb3, 0xe1, 0xc0, 0x7c, 0x28, 0xab, 0x0e, 0x3d, 0xa4, 0xac, 0x2d, 0xfd, 0x81, 0xbc, 0x54, 0x59, 0xf4,
0x0c, 0x7e, 0x24, 0xb3, 0x4b, 0x94, 0xc1, 0xcc, 0x88, 0x9b, 0xea, 0x1b, 0x62, 0x61, 0x60, 0x3b, 0xba, 0x83, 0x75, 0x8a, 0xe9, 0xd8, 0xa8, 0xef, 0x42, 0x19, 0xd8, 0x3c, 0xcb, 0xe0, 0x47, 0x32,
0x9f, 0x2c, 0x9a, 0x47, 0x72, 0x8f, 0x5d, 0x99, 0x3e, 0xb4, 0x39, 0xc6, 0x2d, 0xf9, 0x99, 0x70, 0xbb, 0x44, 0x19, 0xcc, 0x8c, 0xb8, 0xa9, 0xbe, 0x2e, 0x16, 0x06, 0xb6, 0xf3, 0x21, 0xa3, 0x79,
0x60, 0x73, 0x87, 0xdc, 0x95, 0xf5, 0x07, 0xa2, 0xa8, 0x98, 0xa1, 0x22, 0x33, 0xca, 0x4a, 0x5b, 0x24, 0xf7, 0xd8, 0x95, 0xe9, 0xa3, 0x9c, 0x63, 0xdc, 0x92, 0x1f, 0x0f, 0x07, 0x36, 0x77, 0xc8,
0x7e, 0x97, 0x74, 0xad, 0xd0, 0xbe, 0x0f, 0x18, 0x91, 0x3b, 0x33, 0xc2, 0x48, 0x47, 0x7e, 0xe4, 0x5d, 0x59, 0x7f, 0x28, 0x8a, 0x8a, 0x19, 0x2a, 0x3d, 0xa3, 0xac, 0xb4, 0xe5, 0xd7, 0x4a, 0xd7,
0x75, 0x8d, 0x50, 0xa8, 0x9b, 0x36, 0x82, 0xb1, 0x26, 0xdc, 0xc8, 0xb1, 0xfc, 0xfe, 0xab, 0x1a, 0x0a, 0xed, 0xfb, 0x80, 0x11, 0xb9, 0x33, 0x23, 0x8c, 0x74, 0xe4, 0xa7, 0x5f, 0xd7, 0x08, 0x85,
0x59, 0xad, 0x05, 0x56, 0xe5, 0xe4, 0x4c, 0x57, 0xba, 0xf2, 0xf3, 0xad, 0x6b, 0x45, 0x06, 0xc0, 0xba, 0x69, 0x23, 0x18, 0x6b, 0xc2, 0x8d, 0x1c, 0xcb, 0xaf, 0xc2, 0xaa, 0x91, 0x95, 0x5a, 0x60,
0x19, 0x66, 0xa2, 0x9d, 0xf9, 0x50, 0x7e, 0xd9, 0xf5, 0x99, 0x99, 0xf2, 0x66, 0x7c, 0xa6, 0x37, 0x55, 0x4e, 0xce, 0x74, 0xa5, 0x2b, 0x3f, 0xea, 0xba, 0x56, 0x64, 0x00, 0x9c, 0x61, 0x26, 0xda,
0x4f, 0xe4, 0x67, 0x54, 0xd7, 0xcc, 0xfe, 0x2c, 0x6f, 0xc6, 0x67, 0x7a, 0xd3, 0x93, 0x1f, 0x58, 0x99, 0x0f, 0xe5, 0xf7, 0x5e, 0x9f, 0x99, 0x29, 0x6f, 0xc6, 0x67, 0x7a, 0xf3, 0x44, 0x7e, 0x5c,
0x7d, 0x66, 0xc0, 0x9b, 0x2d, 0xa1, 0xab, 0xaf, 0x9a, 0xf3, 0x44, 0x84, 0x9d, 0xbe, 0xfc, 0x6c, 0x75, 0xcd, 0xec, 0xcd, 0xf2, 0x66, 0x7c, 0xa6, 0x37, 0x3d, 0xf9, 0xd9, 0xd5, 0x67, 0x06, 0xbc,
0xee, 0xbd, 0x6c, 0x49, 0x99, 0x65, 0x28, 0xda, 0xa1, 0x81, 0xfc, 0xa2, 0xee, 0x37, 0x04, 0x1e, 0xd9, 0x14, 0xba, 0xfa, 0xaa, 0x39, 0x4f, 0x44, 0xd8, 0xe9, 0xcb, 0x8f, 0xe9, 0xde, 0xcb, 0x96,
0x6d, 0x8b, 0x0b, 0xea, 0xc4, 0xce, 0xe1, 0x92, 0x0d, 0x96, 0x0a, 0xe6, 0x82, 0x37, 0x35, 0xe6, 0x94, 0x59, 0x86, 0xa2, 0x1d, 0x1a, 0xc8, 0xef, 0xec, 0x7e, 0x43, 0xe0, 0xd1, 0x96, 0xb8, 0xa0,
0xcc, 0x34, 0x15, 0xed, 0xd4, 0x10, 0x4c, 0x15, 0xa7, 0x4c, 0x81, 0x57, 0xf7, 0x45, 0x41, 0x31, 0x4e, 0xec, 0x1c, 0x2e, 0xd9, 0x60, 0xa9, 0x60, 0x2e, 0x78, 0x53, 0x63, 0xce, 0x4c, 0x53, 0xd1,
0x75, 0x48, 0x19, 0x3a, 0xdc, 0xcc, 0x53, 0xf9, 0xbf, 0x36, 0xb8, 0x66, 0x30, 0xa3, 0x07, 0xdf, 0x4e, 0x0d, 0xc1, 0x54, 0x71, 0xca, 0x14, 0x78, 0xf5, 0x40, 0x14, 0x14, 0x53, 0x07, 0x94, 0xa1,
0x18, 0xe7, 0xb8, 0x70, 0x23, 0x23, 0xf9, 0x5d, 0xde, 0xf3, 0x85, 0x18, 0x81, 0x2d, 0x41, 0x95, 0xc3, 0xcd, 0x3c, 0x95, 0xff, 0xc3, 0x83, 0x6b, 0x06, 0x33, 0x7a, 0xf0, 0x8d, 0x71, 0x8e, 0x0b,
0x76, 0x94, 0x95, 0xb1, 0xfc, 0x62, 0xef, 0xb9, 0x82, 0x84, 0x7a, 0xd7, 0x37, 0x1d, 0x0b, 0x93, 0x37, 0x32, 0x92, 0x5f, 0xeb, 0x3d, 0x5f, 0x88, 0x11, 0xd8, 0x12, 0x54, 0x7f, 0x47, 0x59, 0x19,
0x5c, 0x84, 0x8d, 0x09, 0x45, 0xe4, 0xd7, 0x43, 0x01, 0x4b, 0xea, 0x55, 0x88, 0x32, 0x6d, 0x7c, 0xcb, 0xef, 0xf8, 0x9e, 0x2b, 0x48, 0xa8, 0x77, 0x7d, 0xd3, 0xb1, 0x30, 0xc9, 0x45, 0xd8, 0x98,
0x84, 0x97, 0x30, 0x7f, 0xfe, 0x80, 0xf4, 0x91, 0x26, 0xeb, 0xe2, 0xea, 0x12, 0x96, 0xce, 0xe6, 0x50, 0x44, 0x7e, 0x3d, 0x14, 0xb0, 0xa4, 0x5e, 0x90, 0x28, 0xd3, 0xc6, 0x47, 0x78, 0x09, 0xf3,
0xdc, 0x91, 0x2f, 0x2e, 0x6d, 0x88, 0xb9, 0x73, 0x07, 0xa5, 0x8f, 0x35, 0x59, 0x5d, 0xa2, 0x25, 0xe7, 0x0f, 0x48, 0x1f, 0x69, 0xb2, 0x5a, 0xae, 0x2e, 0x61, 0x41, 0x6d, 0xce, 0x1d, 0xf9, 0xe2,
0x33, 0x7f, 0xe4, 0x8f, 0x4c, 0x73, 0xe7, 0x0e, 0x4b, 0x9f, 0x68, 0xf2, 0x2a, 0xa2, 0x66, 0xb8, 0xd2, 0xba, 0x98, 0x3b, 0x77, 0x50, 0xfa, 0x58, 0x93, 0x35, 0x27, 0x5a, 0x32, 0xf3, 0x47, 0xfe,
0x46, 0x9c, 0xc8, 0x34, 0x77, 0xee, 0xb0, 0xf4, 0x15, 0x59, 0x3b, 0xc6, 0x6a, 0x55, 0xd5, 0x08, 0xc8, 0x34, 0x77, 0xee, 0xb0, 0xf4, 0x89, 0x26, 0x2f, 0x28, 0x6a, 0x86, 0x6b, 0xc4, 0x89, 0x4c,
0xc5, 0x82, 0xf9, 0xf3, 0x87, 0xa5, 0xaf, 0x6a, 0x74, 0x2d, 0x11, 0xab, 0xd5, 0xdc, 0x75, 0x71, 0x73, 0xe7, 0x0e, 0x4b, 0x5f, 0x91, 0x15, 0x65, 0xac, 0x56, 0x55, 0x8d, 0x50, 0x2c, 0x98, 0x3f,
0x23, 0xd3, 0xfc, 0xf9, 0xc3, 0xd2, 0xd7, 0x34, 0xba, 0xbc, 0x88, 0xd5, 0x56, 0x7c, 0x66, 0xfc, 0x7f, 0x58, 0xfa, 0xaa, 0x46, 0x97, 0x15, 0xb1, 0x5a, 0xcd, 0x5d, 0x17, 0x37, 0x32, 0xcd, 0x9f,
0xde, 0x9c, 0x1d, 0x96, 0xbe, 0xae, 0xd1, 0x7d, 0x42, 0xac, 0xb6, 0xea, 0x9a, 0xd9, 0x9f, 0xf2, 0x3f, 0x2c, 0x7d, 0x4d, 0xa3, 0x2b, 0x8d, 0x58, 0x6d, 0xd9, 0x67, 0xc6, 0xef, 0xcd, 0xd9, 0x61,
0xe6, 0xec, 0xb0, 0xf4, 0x0d, 0x3a, 0xc5, 0x83, 0x99, 0x35, 0x9f, 0x19, 0x8a, 0x4c, 0x85, 0x17, 0xe9, 0xeb, 0x1a, 0xdd, 0x32, 0xc4, 0x6a, 0x2b, 0xae, 0x99, 0xbd, 0x29, 0x6f, 0xce, 0x0e, 0x4b,
0x08, 0x4b, 0xdf, 0xd4, 0xe8, 0xda, 0x27, 0x56, 0xbb, 0x6b, 0x3a, 0xa3, 0x7b, 0x91, 0xa9, 0xf0, 0xdf, 0xa0, 0x53, 0x3c, 0x98, 0x59, 0xf5, 0x99, 0xa1, 0xc8, 0x54, 0x78, 0x81, 0xb0, 0xf4, 0x4d,
0x02, 0x61, 0xe9, 0x53, 0x8d, 0x6e, 0x87, 0x62, 0xb5, 0x7b, 0x7e, 0x43, 0x14, 0x99, 0x8a, 0x2f, 0x8d, 0x2e, 0x83, 0x62, 0xb5, 0x7b, 0xa6, 0x33, 0xba, 0x17, 0x99, 0x0a, 0x2f, 0x10, 0x96, 0x3e,
0x12, 0x96, 0xbe, 0x85, 0x96, 0x0a, 0xf5, 0xd8, 0xca, 0xb2, 0xe9, 0x38, 0xa0, 0x44, 0xa6, 0xe2, 0xd5, 0xe8, 0xce, 0x28, 0x56, 0xbb, 0xef, 0x37, 0x44, 0x91, 0xa9, 0xf8, 0x22, 0x61, 0xe9, 0x5b,
0x8b, 0x84, 0xa5, 0x6f, 0xa3, 0xa9, 0x22, 0x98, 0xaa, 0x04, 0x4c, 0x81, 0x57, 0x0f, 0x44, 0xfe, 0x68, 0xa9, 0x50, 0x8f, 0x2d, 0xdf, 0x35, 0x1d, 0x07, 0x94, 0xc8, 0x54, 0x7c, 0x91, 0xb0, 0xf4,
0xbc, 0x61, 0xe9, 0x3b, 0xea, 0xad, 0x5b, 0xee, 0x48, 0x89, 0x4d, 0x7b, 0xca, 0x3b, 0x3b, 0x33, 0x6d, 0x34, 0x55, 0x04, 0x53, 0x95, 0x80, 0x29, 0xf0, 0xea, 0xa1, 0xc8, 0x9f, 0x37, 0x2c, 0x7d,
0x30, 0x7d, 0x97, 0x6a, 0x9c, 0xfa, 0xdc, 0x17, 0xe5, 0xcd, 0x94, 0x24, 0x78, 0xaf, 0x4f, 0x86, 0x47, 0xbd, 0x8b, 0xcb, 0x1d, 0x29, 0xb1, 0x69, 0x57, 0x79, 0x67, 0x67, 0x06, 0xa6, 0xef, 0x52,
0xa9, 0x1d, 0x6f, 0x7f, 0x9c, 0x19, 0xa3, 0xbe, 0xa7, 0xd1, 0xf5, 0x55, 0x9e, 0x0d, 0x12, 0xde, 0x8d, 0x53, 0x9f, 0xfb, 0xa2, 0xbc, 0xaf, 0x92, 0x04, 0xef, 0xf5, 0xc9, 0x30, 0xb5, 0xed, 0xed,
0xdd, 0x29, 0x32, 0x60, 0x7d, 0xe8, 0xcd, 0xf2, 0xac, 0x68, 0xf5, 0x7d, 0xed, 0x45, 0xc2, 0x55, 0x8f, 0x33, 0x63, 0xd4, 0xf7, 0x34, 0xba, 0xd4, 0xca, 0xb3, 0x41, 0xc2, 0xbb, 0x3b, 0x45, 0x06,
0x1d, 0x6f, 0x6b, 0xdd, 0xc5, 0xa0, 0x96, 0xcf, 0x8b, 0xc4, 0x73, 0x63, 0xb9, 0xa2, 0x1e, 0xc9, 0xac, 0x0f, 0xbd, 0x59, 0x9e, 0x15, 0xad, 0xbe, 0xaf, 0xbd, 0x48, 0xb8, 0xaa, 0xe3, 0x1d, 0xae,
0xd4, 0x5b, 0x5b, 0x19, 0xa4, 0x72, 0x46, 0x41, 0xb9, 0xd8, 0xc6, 0x6b, 0x5b, 0x93, 0x58, 0xcc, 0xbb, 0x18, 0xd4, 0xf2, 0x79, 0x91, 0x78, 0x6e, 0xdc, 0xad, 0xa8, 0x47, 0x32, 0xf5, 0x2e, 0x57,
0x36, 0x42, 0xd9, 0x1f, 0x47, 0xb0, 0x0d, 0x66, 0x57, 0x43, 0xd9, 0x9f, 0x44, 0xb0, 0xab, 0xcc, 0x06, 0xa9, 0x9c, 0x51, 0x50, 0xae, 0xbb, 0xf1, 0x32, 0xd7, 0x24, 0x16, 0xb3, 0x8d, 0x50, 0xf6,
0xae, 0x85, 0xb2, 0xbf, 0x12, 0xc1, 0xae, 0x31, 0x7b, 0x25, 0x94, 0xfd, 0xd5, 0x08, 0xf6, 0x0a, 0xc7, 0x11, 0x6c, 0x83, 0xd9, 0xd5, 0x50, 0xf6, 0x27, 0x11, 0xec, 0x2a, 0xb3, 0x6b, 0xa1, 0xec,
0xb3, 0x57, 0x43, 0xd9, 0x5f, 0x8b, 0x60, 0xaf, 0x32, 0x7b, 0x2d, 0x94, 0xfd, 0xf5, 0x08, 0xf6, 0xaf, 0x44, 0xb0, 0x6b, 0xcc, 0x5e, 0x0e, 0x65, 0x7f, 0x35, 0x82, 0xbd, 0xcc, 0xec, 0x95, 0x50,
0x1a, 0xb3, 0xef, 0x86, 0xb2, 0xbf, 0x11, 0xc1, 0xbe, 0xcb, 0xec, 0x7b, 0xa1, 0xec, 0x6f, 0x46, 0xf6, 0xd7, 0x22, 0xd8, 0x2b, 0xcc, 0x5e, 0x0d, 0x65, 0x7f, 0x3d, 0x82, 0xbd, 0xca, 0xec, 0x7b,
0xb0, 0xef, 0x49, 0x76, 0x65, 0x39, 0x94, 0xfd, 0x69, 0x38, 0xbb, 0xb2, 0xcc, 0xec, 0x70, 0xad, 0xa1, 0xec, 0x6f, 0x44, 0xb0, 0xef, 0x31, 0xfb, 0x7e, 0x28, 0xfb, 0x9b, 0x11, 0xec, 0xfb, 0x92,
0x7d, 0x2b, 0x82, 0xcd, 0x5a, 0xab, 0x84, 0x6b, 0xed, 0xdb, 0x11, 0x6c, 0xd6, 0x5a, 0x25, 0x5c, 0x5d, 0xb9, 0x1b, 0xca, 0xfe, 0x34, 0x9c, 0x5d, 0xb9, 0xcb, 0xec, 0x70, 0xad, 0x7d, 0x2b, 0x82,
0x6b, 0xdf, 0x89, 0x60, 0xb3, 0xd6, 0x2a, 0xe1, 0x5a, 0xfb, 0x6e, 0x04, 0x9b, 0xb5, 0x56, 0x09, 0xcd, 0x5a, 0xab, 0x84, 0x6b, 0xed, 0xdb, 0x11, 0x6c, 0xd6, 0x5a, 0x25, 0x5c, 0x6b, 0xdf, 0x89,
0xd7, 0xda, 0xf7, 0x22, 0xd8, 0xac, 0xb5, 0x4a, 0xb8, 0xd6, 0xbe, 0x1f, 0xc1, 0x66, 0xad, 0x55, 0x60, 0xb3, 0xd6, 0x2a, 0xe1, 0x5a, 0xfb, 0x6e, 0x04, 0x9b, 0xb5, 0x56, 0x09, 0xd7, 0xda, 0xf7,
0xc2, 0xb5, 0xf6, 0x87, 0x11, 0x6c, 0xd6, 0x5a, 0x25, 0x5c, 0x6b, 0x7f, 0x14, 0xc1, 0x66, 0xad, 0x22, 0xd8, 0xac, 0xb5, 0x4a, 0xb8, 0xd6, 0xbe, 0x1f, 0xc1, 0x66, 0xad, 0x55, 0xc2, 0xb5, 0xf6,
0x55, 0xc2, 0xb5, 0xf6, 0xc7, 0x11, 0x6c, 0xd6, 0x9a, 0x11, 0xae, 0xb5, 0x3f, 0x09, 0x67, 0x1b, 0x87, 0x11, 0x6c, 0xd6, 0x5a, 0x25, 0x5c, 0x6b, 0x7f, 0x14, 0xc1, 0x66, 0xad, 0x55, 0xc2, 0xb5,
0xac, 0x35, 0x23, 0x5c, 0x6b, 0x7f, 0x1a, 0xc1, 0x66, 0xad, 0x19, 0xe1, 0x5a, 0xfb, 0xb3, 0x08, 0xf6, 0xc7, 0x11, 0x6c, 0xd6, 0x9a, 0x11, 0xae, 0xb5, 0x3f, 0x09, 0x67, 0x1b, 0xac, 0x35, 0x23,
0x36, 0x6b, 0xcd, 0x08, 0xd7, 0xda, 0x0f, 0x22, 0xd8, 0xac, 0x35, 0x23, 0x5c, 0x6b, 0x7f, 0x1e, 0x5c, 0x6b, 0x7f, 0x1a, 0xc1, 0x66, 0xad, 0x19, 0xe1, 0x5a, 0xfb, 0xb3, 0x08, 0x36, 0x6b, 0xcd,
0xc1, 0x66, 0xad, 0x19, 0xe1, 0x5a, 0xfb, 0x8b, 0x08, 0x36, 0x6b, 0xcd, 0x08, 0xd7, 0xda, 0x5f, 0x08, 0xd7, 0xda, 0x0f, 0x22, 0xd8, 0xac, 0x35, 0x23, 0x5c, 0x6b, 0x7f, 0x1e, 0xc1, 0x66, 0xad,
0x46, 0xb0, 0x59, 0x6b, 0x46, 0xb8, 0xd6, 0xfe, 0x2a, 0x82, 0xcd, 0x5a, 0x33, 0xc2, 0xb5, 0xf6, 0x19, 0xe1, 0x5a, 0xfb, 0x8b, 0x08, 0x36, 0x6b, 0xcd, 0x08, 0xd7, 0xda, 0x5f, 0x46, 0xb0, 0x59,
0xd7, 0x11, 0x6c, 0xd6, 0x9a, 0x11, 0xae, 0xb5, 0xbf, 0x89, 0x60, 0xb3, 0xd6, 0xaa, 0xe1, 0x5a, 0x6b, 0x46, 0xb8, 0xd6, 0xfe, 0x2a, 0x82, 0xcd, 0x5a, 0x33, 0xc2, 0xb5, 0xf6, 0xd7, 0x11, 0x6c,
0xfb, 0xdb, 0x70, 0x76, 0x95, 0xb5, 0x56, 0x0d, 0xd7, 0xda, 0xdf, 0x45, 0xb0, 0x59, 0x6b, 0xd5, 0xd6, 0x9a, 0x11, 0xae, 0xb5, 0xbf, 0x89, 0x60, 0xb3, 0xd6, 0xaa, 0xe1, 0x5a, 0xfb, 0xdb, 0x70,
0x70, 0xad, 0xfd, 0x7d, 0x04, 0x9b, 0xb5, 0x56, 0x0d, 0xd7, 0xda, 0x3f, 0x44, 0xb0, 0x59, 0x6b, 0x76, 0x95, 0xb5, 0x56, 0x0d, 0xd7, 0xda, 0xdf, 0x45, 0xb0, 0x59, 0x6b, 0xd5, 0x70, 0xad, 0xfd,
0xd5, 0x70, 0xad, 0xfd, 0x30, 0x82, 0xcd, 0x5a, 0xab, 0x86, 0x6b, 0xed, 0x1f, 0x23, 0xd8, 0xac, 0x7d, 0x04, 0x9b, 0xb5, 0x56, 0x0d, 0xd7, 0xda, 0x3f, 0x44, 0xb0, 0x59, 0x6b, 0xd5, 0x70, 0xad,
0xb5, 0x6a, 0xb8, 0xd6, 0xfe, 0x29, 0x82, 0xcd, 0x5a, 0xab, 0x86, 0x6b, 0xed, 0x9f, 0x23, 0xd8, 0xfd, 0x30, 0x82, 0xcd, 0x5a, 0xab, 0x86, 0x6b, 0xed, 0x1f, 0x23, 0xd8, 0xac, 0xb5, 0x6a, 0xb8,
0xac, 0xb5, 0x6a, 0xb8, 0xd6, 0xfe, 0x25, 0x82, 0xcd, 0x5a, 0xab, 0x86, 0x6b, 0xed, 0x5f, 0x23, 0xd6, 0xfe, 0x29, 0x82, 0xcd, 0x5a, 0xab, 0x86, 0x6b, 0xed, 0x9f, 0x23, 0xd8, 0xac, 0xb5, 0x6a,
0xd8, 0xac, 0xb5, 0x5a, 0xb8, 0xd6, 0xfe, 0x2d, 0x9c, 0x5d, 0x63, 0xad, 0xd5, 0xc2, 0xb5, 0xf6, 0xb8, 0xd6, 0xfe, 0x25, 0x82, 0xcd, 0x5a, 0xab, 0x86, 0x6b, 0xed, 0x5f, 0x23, 0xd8, 0xac, 0xb5,
0xef, 0x11, 0x6c, 0xd6, 0x5a, 0x2d, 0x5c, 0x6b, 0xff, 0x11, 0xc1, 0x66, 0xad, 0xd5, 0xc2, 0xb5, 0x5a, 0xb8, 0xd6, 0xfe, 0x2d, 0x9c, 0x5d, 0x63, 0xad, 0xd5, 0xc2, 0xb5, 0xf6, 0xef, 0x11, 0x6c,
0xf6, 0x9f, 0x11, 0x6c, 0xd6, 0x5a, 0x2d, 0x5c, 0x6b, 0xff, 0x15, 0xc1, 0x66, 0xad, 0xd5, 0xc2, 0xd6, 0x5a, 0x2d, 0x5c, 0x6b, 0xff, 0x11, 0xc1, 0x66, 0xad, 0xd5, 0xc2, 0xb5, 0xf6, 0x9f, 0x11,
0xb5, 0xf6, 0xdf, 0x11, 0x6c, 0xd6, 0x5a, 0x2d, 0x5c, 0x6b, 0x3f, 0x8a, 0x60, 0xb3, 0xd6, 0x6a, 0x6c, 0xd6, 0x5a, 0x2d, 0x5c, 0x6b, 0xff, 0x15, 0xc1, 0x66, 0xad, 0xd5, 0xc2, 0xb5, 0xf6, 0xdf,
0xe1, 0x5a, 0xfb, 0x71, 0x04, 0x9b, 0xb5, 0x56, 0x0b, 0xd7, 0xda, 0xff, 0x44, 0xb0, 0x59, 0x6b, 0x11, 0x6c, 0xd6, 0x5a, 0x2d, 0x5c, 0x6b, 0x3f, 0x8a, 0x60, 0xb3, 0xd6, 0x6a, 0xe1, 0x5a, 0xfb,
0xb5, 0x70, 0xad, 0xfd, 0x6f, 0x04, 0x9b, 0xb5, 0xb6, 0x12, 0xae, 0xb5, 0xff, 0x0b, 0x67, 0xaf, 0x71, 0x04, 0x9b, 0xb5, 0x56, 0x0b, 0xd7, 0xda, 0xff, 0x44, 0xb0, 0x59, 0x6b, 0xb5, 0x70, 0xad,
0x2c, 0xff, 0x24, 0x00, 0x00, 0xff, 0xff, 0x81, 0x23, 0xc6, 0xe6, 0xc6, 0x38, 0x00, 0x00, 0xfd, 0x6f, 0x04, 0x9b, 0xb5, 0xb6, 0x1c, 0xae, 0xb5, 0xff, 0x0b, 0x67, 0x2f, 0xdf, 0xfd, 0x49,
0x00, 0x00, 0x00, 0xff, 0xff, 0x0d, 0x56, 0x3d, 0xa6, 0xdc, 0x38, 0x00, 0x00,
} }

View file

@ -488,6 +488,7 @@ message GroupNew {
message FloatingPoint { message FloatingPoint {
required double f = 1; required double f = 1;
optional bool exact = 2;
} }
message MessageWithMap { message MessageWithMap {

View file

@ -42,9 +42,7 @@ var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file // This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against. // is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the const _ = proto.ProtoPackageIsVersion1
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type FieldDescriptorProto_Type int32 type FieldDescriptorProto_Type int32
@ -945,9 +943,9 @@ type FileOptions struct {
// Namespace for generated classes; defaults to the package. // Namespace for generated classes; defaults to the package.
CsharpNamespace *string `protobuf:"bytes,37,opt,name=csharp_namespace,json=csharpNamespace" json:"csharp_namespace,omitempty"` CsharpNamespace *string `protobuf:"bytes,37,opt,name=csharp_namespace,json=csharpNamespace" json:"csharp_namespace,omitempty"`
// The parser stores options it doesn't recognize here. See above. // The parser stores options it doesn't recognize here. See above.
UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"`
proto.XXX_InternalExtensions `json:"-"` XXX_extensions map[int32]proto.Extension `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *FileOptions) Reset() { *m = FileOptions{} } func (m *FileOptions) Reset() { *m = FileOptions{} }
@ -962,6 +960,12 @@ var extRange_FileOptions = []proto.ExtensionRange{
func (*FileOptions) ExtensionRangeArray() []proto.ExtensionRange { func (*FileOptions) ExtensionRangeArray() []proto.ExtensionRange {
return extRange_FileOptions return extRange_FileOptions
} }
func (m *FileOptions) ExtensionMap() map[int32]proto.Extension {
if m.XXX_extensions == nil {
m.XXX_extensions = make(map[int32]proto.Extension)
}
return m.XXX_extensions
}
const Default_FileOptions_JavaMultipleFiles bool = false const Default_FileOptions_JavaMultipleFiles bool = false
const Default_FileOptions_JavaGenerateEqualsAndHash bool = false const Default_FileOptions_JavaGenerateEqualsAndHash bool = false
@ -1130,9 +1134,9 @@ type MessageOptions struct {
// parser. // parser.
MapEntry *bool `protobuf:"varint,7,opt,name=map_entry,json=mapEntry" json:"map_entry,omitempty"` MapEntry *bool `protobuf:"varint,7,opt,name=map_entry,json=mapEntry" json:"map_entry,omitempty"`
// The parser stores options it doesn't recognize here. See above. // The parser stores options it doesn't recognize here. See above.
UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"`
proto.XXX_InternalExtensions `json:"-"` XXX_extensions map[int32]proto.Extension `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *MessageOptions) Reset() { *m = MessageOptions{} } func (m *MessageOptions) Reset() { *m = MessageOptions{} }
@ -1147,6 +1151,12 @@ var extRange_MessageOptions = []proto.ExtensionRange{
func (*MessageOptions) ExtensionRangeArray() []proto.ExtensionRange { func (*MessageOptions) ExtensionRangeArray() []proto.ExtensionRange {
return extRange_MessageOptions return extRange_MessageOptions
} }
func (m *MessageOptions) ExtensionMap() map[int32]proto.Extension {
if m.XXX_extensions == nil {
m.XXX_extensions = make(map[int32]proto.Extension)
}
return m.XXX_extensions
}
const Default_MessageOptions_MessageSetWireFormat bool = false const Default_MessageOptions_MessageSetWireFormat bool = false
const Default_MessageOptions_NoStandardDescriptorAccessor bool = false const Default_MessageOptions_NoStandardDescriptorAccessor bool = false
@ -1246,9 +1256,9 @@ type FieldOptions struct {
// For Google-internal migration only. Do not use. // For Google-internal migration only. Do not use.
Weak *bool `protobuf:"varint,10,opt,name=weak,def=0" json:"weak,omitempty"` Weak *bool `protobuf:"varint,10,opt,name=weak,def=0" json:"weak,omitempty"`
// The parser stores options it doesn't recognize here. See above. // The parser stores options it doesn't recognize here. See above.
UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"`
proto.XXX_InternalExtensions `json:"-"` XXX_extensions map[int32]proto.Extension `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *FieldOptions) Reset() { *m = FieldOptions{} } func (m *FieldOptions) Reset() { *m = FieldOptions{} }
@ -1263,6 +1273,12 @@ var extRange_FieldOptions = []proto.ExtensionRange{
func (*FieldOptions) ExtensionRangeArray() []proto.ExtensionRange { func (*FieldOptions) ExtensionRangeArray() []proto.ExtensionRange {
return extRange_FieldOptions return extRange_FieldOptions
} }
func (m *FieldOptions) ExtensionMap() map[int32]proto.Extension {
if m.XXX_extensions == nil {
m.XXX_extensions = make(map[int32]proto.Extension)
}
return m.XXX_extensions
}
const Default_FieldOptions_Ctype FieldOptions_CType = FieldOptions_STRING const Default_FieldOptions_Ctype FieldOptions_CType = FieldOptions_STRING
const Default_FieldOptions_Jstype FieldOptions_JSType = FieldOptions_JS_NORMAL const Default_FieldOptions_Jstype FieldOptions_JSType = FieldOptions_JS_NORMAL
@ -1329,9 +1345,9 @@ type EnumOptions struct {
// is a formalization for deprecating enums. // is a formalization for deprecating enums.
Deprecated *bool `protobuf:"varint,3,opt,name=deprecated,def=0" json:"deprecated,omitempty"` Deprecated *bool `protobuf:"varint,3,opt,name=deprecated,def=0" json:"deprecated,omitempty"`
// The parser stores options it doesn't recognize here. See above. // The parser stores options it doesn't recognize here. See above.
UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"`
proto.XXX_InternalExtensions `json:"-"` XXX_extensions map[int32]proto.Extension `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *EnumOptions) Reset() { *m = EnumOptions{} } func (m *EnumOptions) Reset() { *m = EnumOptions{} }
@ -1346,6 +1362,12 @@ var extRange_EnumOptions = []proto.ExtensionRange{
func (*EnumOptions) ExtensionRangeArray() []proto.ExtensionRange { func (*EnumOptions) ExtensionRangeArray() []proto.ExtensionRange {
return extRange_EnumOptions return extRange_EnumOptions
} }
func (m *EnumOptions) ExtensionMap() map[int32]proto.Extension {
if m.XXX_extensions == nil {
m.XXX_extensions = make(map[int32]proto.Extension)
}
return m.XXX_extensions
}
const Default_EnumOptions_Deprecated bool = false const Default_EnumOptions_Deprecated bool = false
@ -1377,9 +1399,9 @@ type EnumValueOptions struct {
// this is a formalization for deprecating enum values. // this is a formalization for deprecating enum values.
Deprecated *bool `protobuf:"varint,1,opt,name=deprecated,def=0" json:"deprecated,omitempty"` Deprecated *bool `protobuf:"varint,1,opt,name=deprecated,def=0" json:"deprecated,omitempty"`
// The parser stores options it doesn't recognize here. See above. // The parser stores options it doesn't recognize here. See above.
UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"`
proto.XXX_InternalExtensions `json:"-"` XXX_extensions map[int32]proto.Extension `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *EnumValueOptions) Reset() { *m = EnumValueOptions{} } func (m *EnumValueOptions) Reset() { *m = EnumValueOptions{} }
@ -1394,6 +1416,12 @@ var extRange_EnumValueOptions = []proto.ExtensionRange{
func (*EnumValueOptions) ExtensionRangeArray() []proto.ExtensionRange { func (*EnumValueOptions) ExtensionRangeArray() []proto.ExtensionRange {
return extRange_EnumValueOptions return extRange_EnumValueOptions
} }
func (m *EnumValueOptions) ExtensionMap() map[int32]proto.Extension {
if m.XXX_extensions == nil {
m.XXX_extensions = make(map[int32]proto.Extension)
}
return m.XXX_extensions
}
const Default_EnumValueOptions_Deprecated bool = false const Default_EnumValueOptions_Deprecated bool = false
@ -1418,9 +1446,9 @@ type ServiceOptions struct {
// this is a formalization for deprecating services. // this is a formalization for deprecating services.
Deprecated *bool `protobuf:"varint,33,opt,name=deprecated,def=0" json:"deprecated,omitempty"` Deprecated *bool `protobuf:"varint,33,opt,name=deprecated,def=0" json:"deprecated,omitempty"`
// The parser stores options it doesn't recognize here. See above. // The parser stores options it doesn't recognize here. See above.
UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"`
proto.XXX_InternalExtensions `json:"-"` XXX_extensions map[int32]proto.Extension `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *ServiceOptions) Reset() { *m = ServiceOptions{} } func (m *ServiceOptions) Reset() { *m = ServiceOptions{} }
@ -1435,6 +1463,12 @@ var extRange_ServiceOptions = []proto.ExtensionRange{
func (*ServiceOptions) ExtensionRangeArray() []proto.ExtensionRange { func (*ServiceOptions) ExtensionRangeArray() []proto.ExtensionRange {
return extRange_ServiceOptions return extRange_ServiceOptions
} }
func (m *ServiceOptions) ExtensionMap() map[int32]proto.Extension {
if m.XXX_extensions == nil {
m.XXX_extensions = make(map[int32]proto.Extension)
}
return m.XXX_extensions
}
const Default_ServiceOptions_Deprecated bool = false const Default_ServiceOptions_Deprecated bool = false
@ -1459,9 +1493,9 @@ type MethodOptions struct {
// this is a formalization for deprecating methods. // this is a formalization for deprecating methods.
Deprecated *bool `protobuf:"varint,33,opt,name=deprecated,def=0" json:"deprecated,omitempty"` Deprecated *bool `protobuf:"varint,33,opt,name=deprecated,def=0" json:"deprecated,omitempty"`
// The parser stores options it doesn't recognize here. See above. // The parser stores options it doesn't recognize here. See above.
UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"`
proto.XXX_InternalExtensions `json:"-"` XXX_extensions map[int32]proto.Extension `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *MethodOptions) Reset() { *m = MethodOptions{} } func (m *MethodOptions) Reset() { *m = MethodOptions{} }
@ -1476,6 +1510,12 @@ var extRange_MethodOptions = []proto.ExtensionRange{
func (*MethodOptions) ExtensionRangeArray() []proto.ExtensionRange { func (*MethodOptions) ExtensionRangeArray() []proto.ExtensionRange {
return extRange_MethodOptions return extRange_MethodOptions
} }
func (m *MethodOptions) ExtensionMap() map[int32]proto.Extension {
if m.XXX_extensions == nil {
m.XXX_extensions = make(map[int32]proto.Extension)
}
return m.XXX_extensions
}
const Default_MethodOptions_Deprecated bool = false const Default_MethodOptions_Deprecated bool = false
@ -1889,8 +1929,6 @@ func init() {
proto.RegisterEnum("google.protobuf.FieldOptions_JSType", FieldOptions_JSType_name, FieldOptions_JSType_value) proto.RegisterEnum("google.protobuf.FieldOptions_JSType", FieldOptions_JSType_name, FieldOptions_JSType_value)
} }
func init() { proto.RegisterFile("google/protobuf/descriptor.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 2247 bytes of a gzipped FileDescriptorProto // 2247 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xcc, 0x59, 0x4f, 0x8f, 0xdb, 0xc6, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xcc, 0x59, 0x4f, 0x8f, 0xdb, 0xc6,

View file

@ -26,9 +26,7 @@ var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file // This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against. // is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the const _ = proto.ProtoPackageIsVersion1
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// An encoded CodeGeneratorRequest is written to the plugin's stdin. // An encoded CodeGeneratorRequest is written to the plugin's stdin.
type CodeGeneratorRequest struct { type CodeGeneratorRequest struct {
@ -202,8 +200,6 @@ func init() {
proto.RegisterType((*CodeGeneratorResponse_File)(nil), "google.protobuf.compiler.CodeGeneratorResponse.File") proto.RegisterType((*CodeGeneratorResponse_File)(nil), "google.protobuf.compiler.CodeGeneratorResponse.File")
} }
func init() { proto.RegisterFile("google/protobuf/compiler/plugin.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 311 bytes of a gzipped FileDescriptorProto // 311 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x74, 0x91, 0xd1, 0x4a, 0xfb, 0x30, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x74, 0x91, 0xd1, 0x4a, 0xfb, 0x30,

View file

@ -81,9 +81,20 @@ FOO
* Boolean values: `true`, `false` * Boolean values: `true`, `false`
* Arrays can be made by wrapping it in `[]`. Example: * Arrays can be made by wrapping it in `[]`. Example:
`["foo", "bar", 42]`. Arrays can contain primitives `["foo", "bar", 42]`. Arrays can contain primitives,
and other arrays, but cannot contain objects. Objects must other arrays, and objects. As an alternative, lists
use the block syntax shown below. of objects can be created with repeated blocks, using
this structure:
```hcl
service {
key = "value"
}
service {
key = "value"
}
```
Objects and nested objects are created using the structure shown below: Objects and nested objects are created using the structure shown below:

View file

@ -517,6 +517,12 @@ func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value)
structType := structVal.Type() structType := structVal.Type()
for i := 0; i < structType.NumField(); i++ { for i := 0; i < structType.NumField(); i++ {
fieldType := structType.Field(i) fieldType := structType.Field(i)
tagParts := strings.Split(fieldType.Tag.Get(tagName), ",")
// Ignore fields with tag name "-"
if tagParts[0] == "-" {
continue
}
if fieldType.Anonymous { if fieldType.Anonymous {
fieldKind := fieldType.Type.Kind() fieldKind := fieldType.Type.Kind()
@ -531,7 +537,6 @@ func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value)
// We have an embedded field. We "squash" the fields down // We have an embedded field. We "squash" the fields down
// if specified in the tag. // if specified in the tag.
squash := false squash := false
tagParts := strings.Split(fieldType.Tag.Get(tagName), ",")
for _, tag := range tagParts[1:] { for _, tag := range tagParts[1:] {
if tag == "squash" { if tag == "squash" {
squash = true squash = true

View file

@ -194,6 +194,27 @@ func TestDecode_interface(t *testing.T) {
}, },
}, },
}, },
{
"list_of_maps.hcl",
false,
map[string]interface{}{
"foo": []interface{}{
map[string]interface{}{"somekey1": "someval1"},
map[string]interface{}{"somekey2": "someval2", "someextrakey": "someextraval"},
},
},
},
{
"assign_deep.hcl",
false,
map[string]interface{}{
"resource": []interface{}{
map[string]interface{}{
"foo": []interface{}{
map[string]interface{}{
"bar": []map[string]interface{}{
map[string]interface{}{}}}}}}},
},
{ {
"structure_list.hcl", "structure_list.hcl",
false, false,
@ -269,9 +290,7 @@ func TestDecode_interface(t *testing.T) {
{ {
"nested_provider_bad.hcl", "nested_provider_bad.hcl",
true, true,
// This is not ideal but without significant rework of the decoder nil,
// we get a partial result back as well as an error.
map[string]interface{}{},
}, },
{ {
@ -334,6 +353,42 @@ func TestDecode_interface(t *testing.T) {
} }
} }
func TestDecode_interfaceInline(t *testing.T) {
cases := []struct {
Value string
Err bool
Out interface{}
}{
{"t t e{{}}", true, nil},
{"t=0t d {}", true, map[string]interface{}{"t": 0}},
{"v=0E0v d{}", true, map[string]interface{}{"v": float64(0)}},
}
for _, tc := range cases {
t.Logf("Testing: %q", tc.Value)
var out interface{}
err := Decode(&out, tc.Value)
if (err != nil) != tc.Err {
t.Fatalf("Input: %q\n\nError: %s", tc.Value, err)
}
if !reflect.DeepEqual(out, tc.Out) {
t.Fatalf("Input: %q. Actual, Expected.\n\n%#v\n\n%#v", tc.Value, out, tc.Out)
}
var v interface{}
err = Unmarshal([]byte(tc.Value), &v)
if (err != nil) != tc.Err {
t.Fatalf("Input: %q\n\nError: %s", tc.Value, err)
}
if !reflect.DeepEqual(v, tc.Out) {
t.Fatalf("Input: %q. Actual, Expected.\n\n%#v\n\n%#v", tc.Value, out, tc.Out)
}
}
}
func TestDecode_equal(t *testing.T) { func TestDecode_equal(t *testing.T) {
cases := []struct { cases := []struct {
One, Two string One, Two string
@ -417,9 +472,12 @@ func TestDecode_flatMap(t *testing.T) {
} }
func TestDecode_structure(t *testing.T) { func TestDecode_structure(t *testing.T) {
type Embedded interface{}
type V struct { type V struct {
Key int Embedded `hcl:"-"`
Foo string Key int
Foo string
} }
var actual V var actual V

View file

@ -133,6 +133,12 @@ type ObjectItem struct {
} }
func (o *ObjectItem) Pos() token.Pos { func (o *ObjectItem) Pos() token.Pos {
// I'm not entirely sure what causes this, but removing this causes
// a test failure. We should investigate at some point.
if len(o.Keys) == 0 {
return token.Pos{}
}
return o.Keys[0].Pos() return o.Keys[0].Pos()
} }

View file

@ -25,7 +25,7 @@ var (
type Options struct { type Options struct {
List bool // list files whose formatting differs List bool // list files whose formatting differs
Write bool // write result to (source) file instead of stdout Write bool // write result to (source) file instead of stdout
Diff bool // display diffs instead of rewriting files Diff bool // display diffs of formatting changes
} }
func isValidFile(f os.FileInfo, extensions []string) bool { func isValidFile(f os.FileInfo, extensions []string) bool {

View file

@ -79,6 +79,13 @@ func (p *Parser) objectList() (*ast.ObjectList, error) {
} }
node.Add(n) node.Add(n)
// object lists can be optionally comma-delimited e.g. when a list of maps
// is being expressed, so a comma is allowed here - it's simply consumed
tok := p.scan()
if tok.Type != token.COMMA {
p.unscan()
}
} }
return node, nil return node, nil
} }
@ -220,8 +227,19 @@ func (p *Parser) objectKey() ([]*ast.ObjectKey, error) {
return keys, nil return keys, nil
case token.LBRACE: case token.LBRACE:
var err error
// If we have no keys, then it is a syntax error. i.e. {{}} is not
// allowed.
if len(keys) == 0 {
err = &PosError{
Pos: p.tok.Pos,
Err: fmt.Errorf("expected: IDENT | STRING got: %s", p.tok.Type),
}
}
// object // object
return keys, nil return keys, err
case token.IDENT, token.STRING: case token.IDENT, token.STRING:
keyCount++ keyCount++
keys = append(keys, &ast.ObjectKey{Token: p.tok}) keys = append(keys, &ast.ObjectKey{Token: p.tok})
@ -300,15 +318,20 @@ func (p *Parser) listType() (*ast.ListType, error) {
needComma := false needComma := false
for { for {
tok := p.scan() tok := p.scan()
switch tok.Type { if needComma {
case token.NUMBER, token.FLOAT, token.STRING, token.HEREDOC: switch tok.Type {
if needComma { case token.COMMA, token.RBRACK:
default:
return nil, &PosError{ return nil, &PosError{
Pos: tok.Pos, Pos: tok.Pos,
Err: fmt.Errorf("unexpected token: %s. Expecting %s", tok.Type, token.COMMA), Err: fmt.Errorf(
"error parsing list, expected comma or list end, got: %s",
tok.Type),
} }
} }
}
switch tok.Type {
case token.NUMBER, token.FLOAT, token.STRING, token.HEREDOC:
node, err := p.literalType() node, err := p.literalType()
if err != nil { if err != nil {
return nil, err return nil, err
@ -320,7 +343,7 @@ func (p *Parser) listType() (*ast.ListType, error) {
// get next list item or we are at the end // get next list item or we are at the end
// do a look-ahead for line comment // do a look-ahead for line comment
p.scan() p.scan()
if p.lineComment != nil { if p.lineComment != nil && len(l.List) > 0 {
lit, ok := l.List[len(l.List)-1].(*ast.LiteralType) lit, ok := l.List[len(l.List)-1].(*ast.LiteralType)
if ok { if ok {
lit.LineComment = p.lineComment lit.LineComment = p.lineComment
@ -332,6 +355,18 @@ func (p *Parser) listType() (*ast.ListType, error) {
needComma = false needComma = false
continue continue
case token.LBRACE:
// Looks like a nested object, so parse it out
node, err := p.objectType()
if err != nil {
return nil, &PosError{
Pos: tok.Pos,
Err: fmt.Errorf(
"error while trying to parse object within list: %s", err),
}
}
l.Add(node)
needComma = true
case token.BOOL: case token.BOOL:
// TODO(arslan) should we support? not supported by HCL yet // TODO(arslan) should we support? not supported by HCL yet
case token.LBRACK: case token.LBRACK:

View file

@ -6,6 +6,7 @@ import (
"path/filepath" "path/filepath"
"reflect" "reflect"
"runtime" "runtime"
"strings"
"testing" "testing"
"github.com/hashicorp/hcl/hcl/ast" "github.com/hashicorp/hcl/hcl/ast"
@ -99,6 +100,58 @@ EOF
} }
} }
func TestListOfMaps(t *testing.T) {
src := `foo = [
{key = "bar"},
{key = "baz", key2 = "qux"},
]`
p := newParser([]byte(src))
file, err := p.Parse()
if err != nil {
t.Fatalf("err: %s", err)
}
// Here we make all sorts of assumptions about the input structure w/ type
// assertions. The intent is only for this to be a "smoke test" ensuring
// parsing actually performed its duty - giving this test something a bit
// more robust than _just_ "no error occurred".
expected := []string{`"bar"`, `"baz"`, `"qux"`}
actual := make([]string, 0, 3)
ol := file.Node.(*ast.ObjectList)
objItem := ol.Items[0]
list := objItem.Val.(*ast.ListType)
for _, node := range list.List {
obj := node.(*ast.ObjectType)
for _, item := range obj.List.Items {
val := item.Val.(*ast.LiteralType)
actual = append(actual, val.Token.Text)
}
}
if !reflect.DeepEqual(expected, actual) {
t.Fatalf("Expected: %#v, got %#v", expected, actual)
}
}
func TestListOfMaps_requiresComma(t *testing.T) {
src := `foo = [
{key = "bar"}
{key = "baz"}
]`
p := newParser([]byte(src))
_, err := p.Parse()
if err == nil {
t.Fatalf("Expected error, got none!")
}
expected := "error parsing list, expected comma or list end"
if !strings.Contains(err.Error(), expected) {
t.Fatalf("Expected err:\n %s\nTo contain:\n %s\n", err, expected)
}
}
func TestObjectType(t *testing.T) { func TestObjectType(t *testing.T) {
var literals = []struct { var literals = []struct {
src string src string
@ -263,6 +316,10 @@ func TestParse(t *testing.T) {
"multiple.hcl", "multiple.hcl",
false, false,
}, },
{
"object_list_comma.hcl",
false,
},
{ {
"structure.hcl", "structure.hcl",
false, false,
@ -279,10 +336,6 @@ func TestParse(t *testing.T) {
"complex.hcl", "complex.hcl",
false, false,
}, },
{
"assign_deep.hcl",
true,
},
{ {
"types.hcl", "types.hcl",
false, false,
@ -332,6 +385,29 @@ func TestParse(t *testing.T) {
} }
} }
func TestParse_inline(t *testing.T) {
cases := []struct {
Value string
Err bool
}{
{"t t e{{}}", true},
{"o{{}}", true},
{"t t e d N{{}}", true},
{"t t e d{{}}", true},
{"N{}N{{}}", true},
{"v\nN{{}}", true},
{"v=/\n[,", true},
}
for _, tc := range cases {
t.Logf("Testing: %q", tc.Value)
_, err := Parse([]byte(tc.Value))
if (err != nil) != tc.Err {
t.Fatalf("Input: %q\n\nError: %s\n\nAST: %s", tc.Value, err)
}
}
}
// equals fails the test if exp is not equal to act. // equals fails the test if exp is not equal to act.
func equals(tb testing.TB, exp, act interface{}) { func equals(tb testing.TB, exp, act interface{}) {
if !reflect.DeepEqual(exp, act) { if !reflect.DeepEqual(exp, act) {

View file

@ -0,0 +1 @@
foo = {one = 1, two = 2}

View file

@ -35,6 +35,7 @@ var data = []entry{
{"comment_aligned.input", "comment_aligned.golden"}, {"comment_aligned.input", "comment_aligned.golden"},
{"comment_standalone.input", "comment_standalone.golden"}, {"comment_standalone.input", "comment_standalone.golden"},
{"empty_block.input", "empty_block.golden"}, {"empty_block.input", "empty_block.golden"},
{"list_of_objects.input", "list_of_objects.golden"},
} }
func TestFiles(t *testing.T) { func TestFiles(t *testing.T) {

View file

@ -0,0 +1,10 @@
list_of_objects = [
{
key1 = "value1"
key2 = "value2"
},
{
key3 = "value3"
key4 = "value4"
},
]

View file

@ -0,0 +1,10 @@
list_of_objects = [
{
key1 = "value1"
key2 = "value2"
},
{
key3 = "value3"
key4 = "value4"
}
]

View file

@ -525,16 +525,27 @@ func (s *Scanner) scanEscape() rune {
// scanDigits scans a rune with the given base for n times. For example an // scanDigits scans a rune with the given base for n times. For example an
// octal notation \184 would yield in scanDigits(ch, 8, 3) // octal notation \184 would yield in scanDigits(ch, 8, 3)
func (s *Scanner) scanDigits(ch rune, base, n int) rune { func (s *Scanner) scanDigits(ch rune, base, n int) rune {
start := n
for n > 0 && digitVal(ch) < base { for n > 0 && digitVal(ch) < base {
ch = s.next() ch = s.next()
if ch == eof {
// If we see an EOF, we halt any more scanning of digits
// immediately.
break
}
n-- n--
} }
if n > 0 { if n > 0 {
s.err("illegal char escape") s.err("illegal char escape")
} }
// we scanned all digits, put the last non digit char back if n != start {
s.unread() // we scanned all digits, put the last non digit char back,
// only if we read anything at all
s.unread()
}
return ch return ch
} }

View file

@ -283,6 +283,11 @@ func TestPosition(t *testing.T) {
} }
} }
func TestNullChar(t *testing.T) {
s := New([]byte("\"\\0"))
s.Scan() // Used to panic
}
func TestComment(t *testing.T) { func TestComment(t *testing.T) {
testTokenList(t, tokenLists["comment"]) testTokenList(t, tokenLists["comment"])
} }
@ -378,7 +383,7 @@ func TestRealExample(t *testing.T) {
Main interface Main interface
EOF EOF
} }
network_interface { network_interface {
device_index = 1 device_index = 1
description = <<-EOF description = <<-EOF

View file

@ -128,6 +128,12 @@ func (p *Parser) objectKey() ([]*ast.ObjectKey, error) {
Token: p.tok.HCLToken(), Token: p.tok.HCLToken(),
}) })
case token.COLON: case token.COLON:
// If we have a zero keycount it means that we never got
// an object key, i.e. `{ :`. This is a syntax error.
if keyCount == 0 {
return nil, fmt.Errorf("expected: STRING got: %s", p.tok.Type)
}
// Done // Done
return keys, nil return keys, nil
case token.ILLEGAL: case token.ILLEGAL:

View file

@ -311,6 +311,10 @@ func TestParse(t *testing.T) {
"types.json", "types.json",
false, false,
}, },
{
"bad_input_128.json",
true,
},
} }
const fixtureDir = "./test-fixtures" const fixtureDir = "./test-fixtures"
@ -328,6 +332,22 @@ func TestParse(t *testing.T) {
} }
} }
func TestParse_inline(t *testing.T) {
cases := []struct {
Value string
Err bool
}{
{"{:{", true},
}
for _, tc := range cases {
_, err := Parse([]byte(tc.Value))
if (err != nil) != tc.Err {
t.Fatalf("Input: %q\n\nError: %s", tc.Value, err)
}
}
}
// equals fails the test if exp is not equal to act. // equals fails the test if exp is not equal to act.
func equals(tb testing.TB, exp, act interface{}) { func equals(tb testing.TB, exp, act interface{}) {
if !reflect.DeepEqual(exp, act) { if !reflect.DeepEqual(exp, act) {

View file

@ -0,0 +1 @@
{:{

View file

@ -0,0 +1,4 @@
foo = [
{somekey1 = "someval1"},
{somekey2 = "someval2", someextrakey = "someextraval"},
]

View file

@ -47,10 +47,17 @@ var _ = proto.Marshal
var _ = fmt.Errorf var _ = fmt.Errorf
var _ = math.Inf var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type Reject_RejectType int32 type Reject_RejectType int32
const ( const (
// TODO ?? // The rejection reason is unknown (details should be available
// in Reject.reason).
Reject_None Reject_RejectType = 0 Reject_None Reject_RejectType = 0
// The client attempted to connect with an incompatible version. // The client attempted to connect with an incompatible version.
Reject_WrongVersion Reject_RejectType = 1 Reject_WrongVersion Reject_RejectType = 1
@ -110,6 +117,7 @@ func (x *Reject_RejectType) UnmarshalJSON(data []byte) error {
*x = Reject_RejectType(value) *x = Reject_RejectType(value)
return nil return nil
} }
func (Reject_RejectType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{4, 0} }
type PermissionDenied_DenyType int32 type PermissionDenied_DenyType int32
@ -180,6 +188,9 @@ func (x *PermissionDenied_DenyType) UnmarshalJSON(data []byte) error {
*x = PermissionDenied_DenyType(value) *x = PermissionDenied_DenyType(value)
return nil return nil
} }
func (PermissionDenied_DenyType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor0, []int{12, 0}
}
type ContextActionModify_Context int32 type ContextActionModify_Context int32
@ -219,6 +230,9 @@ func (x *ContextActionModify_Context) UnmarshalJSON(data []byte) error {
*x = ContextActionModify_Context(value) *x = ContextActionModify_Context(value)
return nil return nil
} }
func (ContextActionModify_Context) EnumDescriptor() ([]byte, []int) {
return fileDescriptor0, []int{16, 0}
}
type ContextActionModify_Operation int32 type ContextActionModify_Operation int32
@ -252,6 +266,9 @@ func (x *ContextActionModify_Operation) UnmarshalJSON(data []byte) error {
*x = ContextActionModify_Operation(value) *x = ContextActionModify_Operation(value)
return nil return nil
} }
func (ContextActionModify_Operation) EnumDescriptor() ([]byte, []int) {
return fileDescriptor0, []int{16, 1}
}
type Version struct { type Version struct {
// 2-byte Major, 1-byte Minor and 1-byte Patch version number. // 2-byte Major, 1-byte Minor and 1-byte Patch version number.
@ -261,13 +278,14 @@ type Version struct {
// Client OS name. // Client OS name.
Os *string `protobuf:"bytes,3,opt,name=os" json:"os,omitempty"` Os *string `protobuf:"bytes,3,opt,name=os" json:"os,omitempty"`
// Client OS version. // Client OS version.
OsVersion *string `protobuf:"bytes,4,opt,name=os_version" json:"os_version,omitempty"` OsVersion *string `protobuf:"bytes,4,opt,name=os_version,json=osVersion" json:"os_version,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *Version) Reset() { *m = Version{} } func (m *Version) Reset() { *m = Version{} }
func (m *Version) String() string { return proto.CompactTextString(m) } func (m *Version) String() string { return proto.CompactTextString(m) }
func (*Version) ProtoMessage() {} func (*Version) ProtoMessage() {}
func (*Version) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *Version) GetVersion() uint32 { func (m *Version) GetVersion() uint32 {
if m != nil && m.Version != nil { if m != nil && m.Version != nil {
@ -304,9 +322,10 @@ type UDPTunnel struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *UDPTunnel) Reset() { *m = UDPTunnel{} } func (m *UDPTunnel) Reset() { *m = UDPTunnel{} }
func (m *UDPTunnel) String() string { return proto.CompactTextString(m) } func (m *UDPTunnel) String() string { return proto.CompactTextString(m) }
func (*UDPTunnel) ProtoMessage() {} func (*UDPTunnel) ProtoMessage() {}
func (*UDPTunnel) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *UDPTunnel) GetPacket() []byte { func (m *UDPTunnel) GetPacket() []byte {
if m != nil { if m != nil {
@ -324,14 +343,15 @@ type Authenticate struct {
// Additional access tokens for server ACL groups. // Additional access tokens for server ACL groups.
Tokens []string `protobuf:"bytes,3,rep,name=tokens" json:"tokens,omitempty"` Tokens []string `protobuf:"bytes,3,rep,name=tokens" json:"tokens,omitempty"`
// A list of CELT bitstream version constants supported by the client. // A list of CELT bitstream version constants supported by the client.
CeltVersions []int32 `protobuf:"varint,4,rep,name=celt_versions" json:"celt_versions,omitempty"` CeltVersions []int32 `protobuf:"varint,4,rep,name=celt_versions,json=celtVersions" json:"celt_versions,omitempty"`
Opus *bool `protobuf:"varint,5,opt,name=opus,def=0" json:"opus,omitempty"` Opus *bool `protobuf:"varint,5,opt,name=opus,def=0" json:"opus,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *Authenticate) Reset() { *m = Authenticate{} } func (m *Authenticate) Reset() { *m = Authenticate{} }
func (m *Authenticate) String() string { return proto.CompactTextString(m) } func (m *Authenticate) String() string { return proto.CompactTextString(m) }
func (*Authenticate) ProtoMessage() {} func (*Authenticate) ProtoMessage() {}
func (*Authenticate) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
const Default_Authenticate_Opus bool = false const Default_Authenticate_Opus bool = false
@ -385,23 +405,24 @@ type Ping struct {
// The amount of nonce resyncs. // The amount of nonce resyncs.
Resync *uint32 `protobuf:"varint,5,opt,name=resync" json:"resync,omitempty"` Resync *uint32 `protobuf:"varint,5,opt,name=resync" json:"resync,omitempty"`
// The total amount of UDP packets received. // The total amount of UDP packets received.
UdpPackets *uint32 `protobuf:"varint,6,opt,name=udp_packets" json:"udp_packets,omitempty"` UdpPackets *uint32 `protobuf:"varint,6,opt,name=udp_packets,json=udpPackets" json:"udp_packets,omitempty"`
// The total amount of TCP packets received. // The total amount of TCP packets received.
TcpPackets *uint32 `protobuf:"varint,7,opt,name=tcp_packets" json:"tcp_packets,omitempty"` TcpPackets *uint32 `protobuf:"varint,7,opt,name=tcp_packets,json=tcpPackets" json:"tcp_packets,omitempty"`
// UDP ping average. // UDP ping average.
UdpPingAvg *float32 `protobuf:"fixed32,8,opt,name=udp_ping_avg" json:"udp_ping_avg,omitempty"` UdpPingAvg *float32 `protobuf:"fixed32,8,opt,name=udp_ping_avg,json=udpPingAvg" json:"udp_ping_avg,omitempty"`
// UDP ping variance. // UDP ping variance.
UdpPingVar *float32 `protobuf:"fixed32,9,opt,name=udp_ping_var" json:"udp_ping_var,omitempty"` UdpPingVar *float32 `protobuf:"fixed32,9,opt,name=udp_ping_var,json=udpPingVar" json:"udp_ping_var,omitempty"`
// TCP ping average. // TCP ping average.
TcpPingAvg *float32 `protobuf:"fixed32,10,opt,name=tcp_ping_avg" json:"tcp_ping_avg,omitempty"` TcpPingAvg *float32 `protobuf:"fixed32,10,opt,name=tcp_ping_avg,json=tcpPingAvg" json:"tcp_ping_avg,omitempty"`
// TCP ping variance. // TCP ping variance.
TcpPingVar *float32 `protobuf:"fixed32,11,opt,name=tcp_ping_var" json:"tcp_ping_var,omitempty"` TcpPingVar *float32 `protobuf:"fixed32,11,opt,name=tcp_ping_var,json=tcpPingVar" json:"tcp_ping_var,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *Ping) Reset() { *m = Ping{} } func (m *Ping) Reset() { *m = Ping{} }
func (m *Ping) String() string { return proto.CompactTextString(m) } func (m *Ping) String() string { return proto.CompactTextString(m) }
func (*Ping) ProtoMessage() {} func (*Ping) ProtoMessage() {}
func (*Ping) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
func (m *Ping) GetTimestamp() uint64 { func (m *Ping) GetTimestamp() uint64 {
if m != nil && m.Timestamp != nil { if m != nil && m.Timestamp != nil {
@ -489,9 +510,10 @@ type Reject struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *Reject) Reset() { *m = Reject{} } func (m *Reject) Reset() { *m = Reject{} }
func (m *Reject) String() string { return proto.CompactTextString(m) } func (m *Reject) String() string { return proto.CompactTextString(m) }
func (*Reject) ProtoMessage() {} func (*Reject) ProtoMessage() {}
func (*Reject) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
func (m *Reject) GetType() Reject_RejectType { func (m *Reject) GetType() Reject_RejectType {
if m != nil && m.Type != nil { if m != nil && m.Type != nil {
@ -513,17 +535,18 @@ type ServerSync struct {
// The session of the current user. // The session of the current user.
Session *uint32 `protobuf:"varint,1,opt,name=session" json:"session,omitempty"` Session *uint32 `protobuf:"varint,1,opt,name=session" json:"session,omitempty"`
// Maximum bandwidth that the user should use. // Maximum bandwidth that the user should use.
MaxBandwidth *uint32 `protobuf:"varint,2,opt,name=max_bandwidth" json:"max_bandwidth,omitempty"` MaxBandwidth *uint32 `protobuf:"varint,2,opt,name=max_bandwidth,json=maxBandwidth" json:"max_bandwidth,omitempty"`
// Server welcome text. // Server welcome text.
WelcomeText *string `protobuf:"bytes,3,opt,name=welcome_text" json:"welcome_text,omitempty"` WelcomeText *string `protobuf:"bytes,3,opt,name=welcome_text,json=welcomeText" json:"welcome_text,omitempty"`
// Current user permissions TODO: Confirm?? // Current user permissions in the root channel.
Permissions *uint64 `protobuf:"varint,4,opt,name=permissions" json:"permissions,omitempty"` Permissions *uint64 `protobuf:"varint,4,opt,name=permissions" json:"permissions,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *ServerSync) Reset() { *m = ServerSync{} } func (m *ServerSync) Reset() { *m = ServerSync{} }
func (m *ServerSync) String() string { return proto.CompactTextString(m) } func (m *ServerSync) String() string { return proto.CompactTextString(m) }
func (*ServerSync) ProtoMessage() {} func (*ServerSync) ProtoMessage() {}
func (*ServerSync) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
func (m *ServerSync) GetSession() uint32 { func (m *ServerSync) GetSession() uint32 {
if m != nil && m.Session != nil { if m != nil && m.Session != nil {
@ -556,13 +579,14 @@ func (m *ServerSync) GetPermissions() uint64 {
// Sent by the client when it wants a channel removed. Sent by the server when // Sent by the client when it wants a channel removed. Sent by the server when
// a channel has been removed and clients should be notified. // a channel has been removed and clients should be notified.
type ChannelRemove struct { type ChannelRemove struct {
ChannelId *uint32 `protobuf:"varint,1,req,name=channel_id" json:"channel_id,omitempty"` ChannelId *uint32 `protobuf:"varint,1,req,name=channel_id,json=channelId" json:"channel_id,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *ChannelRemove) Reset() { *m = ChannelRemove{} } func (m *ChannelRemove) Reset() { *m = ChannelRemove{} }
func (m *ChannelRemove) String() string { return proto.CompactTextString(m) } func (m *ChannelRemove) String() string { return proto.CompactTextString(m) }
func (*ChannelRemove) ProtoMessage() {} func (*ChannelRemove) ProtoMessage() {}
func (*ChannelRemove) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
func (m *ChannelRemove) GetChannelId() uint32 { func (m *ChannelRemove) GetChannelId() uint32 {
if m != nil && m.ChannelId != nil { if m != nil && m.ChannelId != nil {
@ -576,7 +600,7 @@ func (m *ChannelRemove) GetChannelId() uint32 {
// updated. Client may use this message to update said channel properties. // updated. Client may use this message to update said channel properties.
type ChannelState struct { type ChannelState struct {
// Unique ID for the channel within the server. // Unique ID for the channel within the server.
ChannelId *uint32 `protobuf:"varint,1,opt,name=channel_id" json:"channel_id,omitempty"` ChannelId *uint32 `protobuf:"varint,1,opt,name=channel_id,json=channelId" json:"channel_id,omitempty"`
// channel_id of the parent channel. // channel_id of the parent channel.
Parent *uint32 `protobuf:"varint,2,opt,name=parent" json:"parent,omitempty"` Parent *uint32 `protobuf:"varint,2,opt,name=parent" json:"parent,omitempty"`
// UTF-8 encoded channel name. // UTF-8 encoded channel name.
@ -588,25 +612,26 @@ type ChannelState struct {
// 128 bytes // 128 bytes
Description *string `protobuf:"bytes,5,opt,name=description" json:"description,omitempty"` Description *string `protobuf:"bytes,5,opt,name=description" json:"description,omitempty"`
// A collection of channel_id values that should be added to links. // A collection of channel_id values that should be added to links.
LinksAdd []uint32 `protobuf:"varint,6,rep,name=links_add" json:"links_add,omitempty"` LinksAdd []uint32 `protobuf:"varint,6,rep,name=links_add,json=linksAdd" json:"links_add,omitempty"`
// A collection of channel_id values that should be removed from links. // A collection of channel_id values that should be removed from links.
LinksRemove []uint32 `protobuf:"varint,7,rep,name=links_remove" json:"links_remove,omitempty"` LinksRemove []uint32 `protobuf:"varint,7,rep,name=links_remove,json=linksRemove" json:"links_remove,omitempty"`
// True if the channel is temporary. // True if the channel is temporary.
Temporary *bool `protobuf:"varint,8,opt,name=temporary,def=0" json:"temporary,omitempty"` Temporary *bool `protobuf:"varint,8,opt,name=temporary,def=0" json:"temporary,omitempty"`
// Position weight to tweak the channel position in the channel list. // Position weight to tweak the channel position in the channel list.
Position *int32 `protobuf:"varint,9,opt,name=position,def=0" json:"position,omitempty"` Position *int32 `protobuf:"varint,9,opt,name=position,def=0" json:"position,omitempty"`
// SHA1 hash of the description if the description is 128 bytes or more. // SHA1 hash of the description if the description is 128 bytes or more.
DescriptionHash []byte `protobuf:"bytes,10,opt,name=description_hash" json:"description_hash,omitempty"` DescriptionHash []byte `protobuf:"bytes,10,opt,name=description_hash,json=descriptionHash" json:"description_hash,omitempty"`
// Maximum number of users allowed in the channel. If this value is zero, // Maximum number of users allowed in the channel. If this value is zero,
// the maximum number of users allowed in the channel is given by the // the maximum number of users allowed in the channel is given by the
// server's "usersperchannel" setting. // server's "usersperchannel" setting.
MaxUsers *uint32 `protobuf:"varint,11,opt,name=max_users" json:"max_users,omitempty"` MaxUsers *uint32 `protobuf:"varint,11,opt,name=max_users,json=maxUsers" json:"max_users,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *ChannelState) Reset() { *m = ChannelState{} } func (m *ChannelState) Reset() { *m = ChannelState{} }
func (m *ChannelState) String() string { return proto.CompactTextString(m) } func (m *ChannelState) String() string { return proto.CompactTextString(m) }
func (*ChannelState) ProtoMessage() {} func (*ChannelState) ProtoMessage() {}
func (*ChannelState) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
const Default_ChannelState_Temporary bool = false const Default_ChannelState_Temporary bool = false
const Default_ChannelState_Position int32 = 0 const Default_ChannelState_Position int32 = 0
@ -705,9 +730,10 @@ type UserRemove struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *UserRemove) Reset() { *m = UserRemove{} } func (m *UserRemove) Reset() { *m = UserRemove{} }
func (m *UserRemove) String() string { return proto.CompactTextString(m) } func (m *UserRemove) String() string { return proto.CompactTextString(m) }
func (*UserRemove) ProtoMessage() {} func (*UserRemove) ProtoMessage() {}
func (*UserRemove) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
func (m *UserRemove) GetSession() uint32 { func (m *UserRemove) GetSession() uint32 {
if m != nil && m.Session != nil { if m != nil && m.Session != nil {
@ -749,9 +775,9 @@ type UserState struct {
// User name, UTF-8 encoded. // User name, UTF-8 encoded.
Name *string `protobuf:"bytes,3,opt,name=name" json:"name,omitempty"` Name *string `protobuf:"bytes,3,opt,name=name" json:"name,omitempty"`
// Registered user ID if the user is registered. // Registered user ID if the user is registered.
UserId *uint32 `protobuf:"varint,4,opt,name=user_id" json:"user_id,omitempty"` UserId *uint32 `protobuf:"varint,4,opt,name=user_id,json=userId" json:"user_id,omitempty"`
// Channel on which the user is. // Channel on which the user is.
ChannelId *uint32 `protobuf:"varint,5,opt,name=channel_id" json:"channel_id,omitempty"` ChannelId *uint32 `protobuf:"varint,5,opt,name=channel_id,json=channelId" json:"channel_id,omitempty"`
// True if the user is muted by admin. // True if the user is muted by admin.
Mute *bool `protobuf:"varint,6,opt,name=mute" json:"mute,omitempty"` Mute *bool `protobuf:"varint,6,opt,name=mute" json:"mute,omitempty"`
// True if the user is deafened by admin. // True if the user is deafened by admin.
@ -760,33 +786,39 @@ type UserState struct {
// being muted. // being muted.
Suppress *bool `protobuf:"varint,8,opt,name=suppress" json:"suppress,omitempty"` Suppress *bool `protobuf:"varint,8,opt,name=suppress" json:"suppress,omitempty"`
// True if the user has muted self. // True if the user has muted self.
SelfMute *bool `protobuf:"varint,9,opt,name=self_mute" json:"self_mute,omitempty"` SelfMute *bool `protobuf:"varint,9,opt,name=self_mute,json=selfMute" json:"self_mute,omitempty"`
// True if the user has deafened self. // True if the user has deafened self.
SelfDeaf *bool `protobuf:"varint,10,opt,name=self_deaf" json:"self_deaf,omitempty"` SelfDeaf *bool `protobuf:"varint,10,opt,name=self_deaf,json=selfDeaf" json:"self_deaf,omitempty"`
// User image if it is less than 128 bytes. // User image if it is less than 128 bytes.
Texture []byte `protobuf:"bytes,11,opt,name=texture" json:"texture,omitempty"` Texture []byte `protobuf:"bytes,11,opt,name=texture" json:"texture,omitempty"`
// TODO ?? // The positional audio plugin identifier.
PluginContext []byte `protobuf:"bytes,12,opt,name=plugin_context" json:"plugin_context,omitempty"` // Positional audio information is only sent to users who share
// TODO ?? // identical plugin contexts.
PluginIdentity *string `protobuf:"bytes,13,opt,name=plugin_identity" json:"plugin_identity,omitempty"` //
// This value is not trasmitted to clients.
PluginContext []byte `protobuf:"bytes,12,opt,name=plugin_context,json=pluginContext" json:"plugin_context,omitempty"`
// The user's plugin-specific identity.
// This value is not transmitted to clients.
PluginIdentity *string `protobuf:"bytes,13,opt,name=plugin_identity,json=pluginIdentity" json:"plugin_identity,omitempty"`
// User comment if it is less than 128 bytes. // User comment if it is less than 128 bytes.
Comment *string `protobuf:"bytes,14,opt,name=comment" json:"comment,omitempty"` Comment *string `protobuf:"bytes,14,opt,name=comment" json:"comment,omitempty"`
// The hash of the user certificate. // The hash of the user certificate.
Hash *string `protobuf:"bytes,15,opt,name=hash" json:"hash,omitempty"` Hash *string `protobuf:"bytes,15,opt,name=hash" json:"hash,omitempty"`
// SHA1 hash of the user comment if it 128 bytes or more. // SHA1 hash of the user comment if it 128 bytes or more.
CommentHash []byte `protobuf:"bytes,16,opt,name=comment_hash" json:"comment_hash,omitempty"` CommentHash []byte `protobuf:"bytes,16,opt,name=comment_hash,json=commentHash" json:"comment_hash,omitempty"`
// SHA1 hash of the user picture if it 128 bytes or more. // SHA1 hash of the user picture if it 128 bytes or more.
TextureHash []byte `protobuf:"bytes,17,opt,name=texture_hash" json:"texture_hash,omitempty"` TextureHash []byte `protobuf:"bytes,17,opt,name=texture_hash,json=textureHash" json:"texture_hash,omitempty"`
// True if the user is a priority speaker. // True if the user is a priority speaker.
PrioritySpeaker *bool `protobuf:"varint,18,opt,name=priority_speaker" json:"priority_speaker,omitempty"` PrioritySpeaker *bool `protobuf:"varint,18,opt,name=priority_speaker,json=prioritySpeaker" json:"priority_speaker,omitempty"`
// True if the user is currently recording. // True if the user is currently recording.
Recording *bool `protobuf:"varint,19,opt,name=recording" json:"recording,omitempty"` Recording *bool `protobuf:"varint,19,opt,name=recording" json:"recording,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *UserState) Reset() { *m = UserState{} } func (m *UserState) Reset() { *m = UserState{} }
func (m *UserState) String() string { return proto.CompactTextString(m) } func (m *UserState) String() string { return proto.CompactTextString(m) }
func (*UserState) ProtoMessage() {} func (*UserState) ProtoMessage() {}
func (*UserState) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} }
func (m *UserState) GetSession() uint32 { func (m *UserState) GetSession() uint32 {
if m != nil && m.Session != nil { if m != nil && m.Session != nil {
@ -933,9 +965,10 @@ type BanList struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *BanList) Reset() { *m = BanList{} } func (m *BanList) Reset() { *m = BanList{} }
func (m *BanList) String() string { return proto.CompactTextString(m) } func (m *BanList) String() string { return proto.CompactTextString(m) }
func (*BanList) ProtoMessage() {} func (*BanList) ProtoMessage() {}
func (*BanList) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} }
const Default_BanList_Query bool = false const Default_BanList_Query bool = false
@ -960,7 +993,7 @@ type BanList_BanEntry struct {
Mask *uint32 `protobuf:"varint,2,req,name=mask" json:"mask,omitempty"` Mask *uint32 `protobuf:"varint,2,req,name=mask" json:"mask,omitempty"`
// User name for identification purposes (does not affect the ban). // User name for identification purposes (does not affect the ban).
Name *string `protobuf:"bytes,3,opt,name=name" json:"name,omitempty"` Name *string `protobuf:"bytes,3,opt,name=name" json:"name,omitempty"`
// TODO ?? // The certificate hash of the banned user.
Hash *string `protobuf:"bytes,4,opt,name=hash" json:"hash,omitempty"` Hash *string `protobuf:"bytes,4,opt,name=hash" json:"hash,omitempty"`
// Reason for the ban (does not affect the ban). // Reason for the ban (does not affect the ban).
Reason *string `protobuf:"bytes,5,opt,name=reason" json:"reason,omitempty"` Reason *string `protobuf:"bytes,5,opt,name=reason" json:"reason,omitempty"`
@ -971,9 +1004,10 @@ type BanList_BanEntry struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *BanList_BanEntry) Reset() { *m = BanList_BanEntry{} } func (m *BanList_BanEntry) Reset() { *m = BanList_BanEntry{} }
func (m *BanList_BanEntry) String() string { return proto.CompactTextString(m) } func (m *BanList_BanEntry) String() string { return proto.CompactTextString(m) }
func (*BanList_BanEntry) ProtoMessage() {} func (*BanList_BanEntry) ProtoMessage() {}
func (*BanList_BanEntry) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10, 0} }
func (m *BanList_BanEntry) GetAddress() []byte { func (m *BanList_BanEntry) GetAddress() []byte {
if m != nil { if m != nil {
@ -1032,18 +1066,19 @@ type TextMessage struct {
Session []uint32 `protobuf:"varint,2,rep,name=session" json:"session,omitempty"` Session []uint32 `protobuf:"varint,2,rep,name=session" json:"session,omitempty"`
// The channels to which the message is sent, identified by their // The channels to which the message is sent, identified by their
// channel_ids. // channel_ids.
ChannelId []uint32 `protobuf:"varint,3,rep,name=channel_id" json:"channel_id,omitempty"` ChannelId []uint32 `protobuf:"varint,3,rep,name=channel_id,json=channelId" json:"channel_id,omitempty"`
// The root channels when sending message recursively to several channels, // The root channels when sending message recursively to several channels,
// identified by their channel_ids. // identified by their channel_ids.
TreeId []uint32 `protobuf:"varint,4,rep,name=tree_id" json:"tree_id,omitempty"` TreeId []uint32 `protobuf:"varint,4,rep,name=tree_id,json=treeId" json:"tree_id,omitempty"`
// The UTF-8 encoded message. May be HTML if the server allows. // The UTF-8 encoded message. May be HTML if the server allows.
Message *string `protobuf:"bytes,5,req,name=message" json:"message,omitempty"` Message *string `protobuf:"bytes,5,req,name=message" json:"message,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *TextMessage) Reset() { *m = TextMessage{} } func (m *TextMessage) Reset() { *m = TextMessage{} }
func (m *TextMessage) String() string { return proto.CompactTextString(m) } func (m *TextMessage) String() string { return proto.CompactTextString(m) }
func (*TextMessage) ProtoMessage() {} func (*TextMessage) ProtoMessage() {}
func (*TextMessage) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} }
func (m *TextMessage) GetActor() uint32 { func (m *TextMessage) GetActor() uint32 {
if m != nil && m.Actor != nil { if m != nil && m.Actor != nil {
@ -1085,7 +1120,7 @@ type PermissionDenied struct {
Permission *uint32 `protobuf:"varint,1,opt,name=permission" json:"permission,omitempty"` Permission *uint32 `protobuf:"varint,1,opt,name=permission" json:"permission,omitempty"`
// channel_id for the channel where the permission was denied when type is // channel_id for the channel where the permission was denied when type is
// Permission. // Permission.
ChannelId *uint32 `protobuf:"varint,2,opt,name=channel_id" json:"channel_id,omitempty"` ChannelId *uint32 `protobuf:"varint,2,opt,name=channel_id,json=channelId" json:"channel_id,omitempty"`
// The user who was denied permissions, identified by session. // The user who was denied permissions, identified by session.
Session *uint32 `protobuf:"varint,3,opt,name=session" json:"session,omitempty"` Session *uint32 `protobuf:"varint,3,opt,name=session" json:"session,omitempty"`
// Textual reason for the denial. // Textual reason for the denial.
@ -1097,9 +1132,10 @@ type PermissionDenied struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *PermissionDenied) Reset() { *m = PermissionDenied{} } func (m *PermissionDenied) Reset() { *m = PermissionDenied{} }
func (m *PermissionDenied) String() string { return proto.CompactTextString(m) } func (m *PermissionDenied) String() string { return proto.CompactTextString(m) }
func (*PermissionDenied) ProtoMessage() {} func (*PermissionDenied) ProtoMessage() {}
func (*PermissionDenied) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} }
func (m *PermissionDenied) GetPermission() uint32 { func (m *PermissionDenied) GetPermission() uint32 {
if m != nil && m.Permission != nil { if m != nil && m.Permission != nil {
@ -1145,9 +1181,9 @@ func (m *PermissionDenied) GetName() string {
type ACL struct { type ACL struct {
// Channel ID of the channel this message affects. // Channel ID of the channel this message affects.
ChannelId *uint32 `protobuf:"varint,1,req,name=channel_id" json:"channel_id,omitempty"` ChannelId *uint32 `protobuf:"varint,1,req,name=channel_id,json=channelId" json:"channel_id,omitempty"`
// True if the channel inherits its parent's ACLs. // True if the channel inherits its parent's ACLs.
InheritAcls *bool `protobuf:"varint,2,opt,name=inherit_acls,def=1" json:"inherit_acls,omitempty"` InheritAcls *bool `protobuf:"varint,2,opt,name=inherit_acls,json=inheritAcls,def=1" json:"inherit_acls,omitempty"`
// User group specifications. // User group specifications.
Groups []*ACL_ChanGroup `protobuf:"bytes,3,rep,name=groups" json:"groups,omitempty"` Groups []*ACL_ChanGroup `protobuf:"bytes,3,rep,name=groups" json:"groups,omitempty"`
// ACL specifications. // ACL specifications.
@ -1157,9 +1193,10 @@ type ACL struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *ACL) Reset() { *m = ACL{} } func (m *ACL) Reset() { *m = ACL{} }
func (m *ACL) String() string { return proto.CompactTextString(m) } func (m *ACL) String() string { return proto.CompactTextString(m) }
func (*ACL) ProtoMessage() {} func (*ACL) ProtoMessage() {}
func (*ACL) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} }
const Default_ACL_InheritAcls bool = true const Default_ACL_InheritAcls bool = true
const Default_ACL_Query bool = false const Default_ACL_Query bool = false
@ -1214,13 +1251,14 @@ type ACL_ChanGroup struct {
// has been inherited, identified by user_id. // has been inherited, identified by user_id.
Remove []uint32 `protobuf:"varint,6,rep,name=remove" json:"remove,omitempty"` Remove []uint32 `protobuf:"varint,6,rep,name=remove" json:"remove,omitempty"`
// Users inherited, identified by user_id. // Users inherited, identified by user_id.
InheritedMembers []uint32 `protobuf:"varint,7,rep,name=inherited_members" json:"inherited_members,omitempty"` InheritedMembers []uint32 `protobuf:"varint,7,rep,name=inherited_members,json=inheritedMembers" json:"inherited_members,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *ACL_ChanGroup) Reset() { *m = ACL_ChanGroup{} } func (m *ACL_ChanGroup) Reset() { *m = ACL_ChanGroup{} }
func (m *ACL_ChanGroup) String() string { return proto.CompactTextString(m) } func (m *ACL_ChanGroup) String() string { return proto.CompactTextString(m) }
func (*ACL_ChanGroup) ProtoMessage() {} func (*ACL_ChanGroup) ProtoMessage() {}
func (*ACL_ChanGroup) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13, 0} }
const Default_ACL_ChanGroup_Inherited bool = true const Default_ACL_ChanGroup_Inherited bool = true
const Default_ACL_ChanGroup_Inherit bool = true const Default_ACL_ChanGroup_Inherit bool = true
@ -1277,13 +1315,13 @@ func (m *ACL_ChanGroup) GetInheritedMembers() []uint32 {
type ACL_ChanACL struct { type ACL_ChanACL struct {
// True if this ACL applies to the current channel. // True if this ACL applies to the current channel.
ApplyHere *bool `protobuf:"varint,1,opt,name=apply_here,def=1" json:"apply_here,omitempty"` ApplyHere *bool `protobuf:"varint,1,opt,name=apply_here,json=applyHere,def=1" json:"apply_here,omitempty"`
// True if this ACL applies to the sub channels. // True if this ACL applies to the sub channels.
ApplySubs *bool `protobuf:"varint,2,opt,name=apply_subs,def=1" json:"apply_subs,omitempty"` ApplySubs *bool `protobuf:"varint,2,opt,name=apply_subs,json=applySubs,def=1" json:"apply_subs,omitempty"`
// True if the ACL has been inherited from the parent. // True if the ACL has been inherited from the parent.
Inherited *bool `protobuf:"varint,3,opt,name=inherited,def=1" json:"inherited,omitempty"` Inherited *bool `protobuf:"varint,3,opt,name=inherited,def=1" json:"inherited,omitempty"`
// ID of the user that is affected by this ACL. // ID of the user that is affected by this ACL.
UserId *uint32 `protobuf:"varint,4,opt,name=user_id" json:"user_id,omitempty"` UserId *uint32 `protobuf:"varint,4,opt,name=user_id,json=userId" json:"user_id,omitempty"`
// ID of the group that is affected by this ACL. // ID of the group that is affected by this ACL.
Group *string `protobuf:"bytes,5,opt,name=group" json:"group,omitempty"` Group *string `protobuf:"bytes,5,opt,name=group" json:"group,omitempty"`
// Bit flag field of the permissions granted by this ACL. // Bit flag field of the permissions granted by this ACL.
@ -1293,9 +1331,10 @@ type ACL_ChanACL struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *ACL_ChanACL) Reset() { *m = ACL_ChanACL{} } func (m *ACL_ChanACL) Reset() { *m = ACL_ChanACL{} }
func (m *ACL_ChanACL) String() string { return proto.CompactTextString(m) } func (m *ACL_ChanACL) String() string { return proto.CompactTextString(m) }
func (*ACL_ChanACL) ProtoMessage() {} func (*ACL_ChanACL) ProtoMessage() {}
func (*ACL_ChanACL) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13, 1} }
const Default_ACL_ChanACL_ApplyHere bool = true const Default_ACL_ChanACL_ApplyHere bool = true
const Default_ACL_ChanACL_ApplySubs bool = true const Default_ACL_ChanACL_ApplySubs bool = true
@ -1361,9 +1400,10 @@ type QueryUsers struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *QueryUsers) Reset() { *m = QueryUsers{} } func (m *QueryUsers) Reset() { *m = QueryUsers{} }
func (m *QueryUsers) String() string { return proto.CompactTextString(m) } func (m *QueryUsers) String() string { return proto.CompactTextString(m) }
func (*QueryUsers) ProtoMessage() {} func (*QueryUsers) ProtoMessage() {}
func (*QueryUsers) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} }
func (m *QueryUsers) GetIds() []uint32 { func (m *QueryUsers) GetIds() []uint32 {
if m != nil { if m != nil {
@ -1387,15 +1427,16 @@ type CryptSetup struct {
// Encryption key. // Encryption key.
Key []byte `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"` Key []byte `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"`
// Client nonce. // Client nonce.
ClientNonce []byte `protobuf:"bytes,2,opt,name=client_nonce" json:"client_nonce,omitempty"` ClientNonce []byte `protobuf:"bytes,2,opt,name=client_nonce,json=clientNonce" json:"client_nonce,omitempty"`
// Server nonce. // Server nonce.
ServerNonce []byte `protobuf:"bytes,3,opt,name=server_nonce" json:"server_nonce,omitempty"` ServerNonce []byte `protobuf:"bytes,3,opt,name=server_nonce,json=serverNonce" json:"server_nonce,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *CryptSetup) Reset() { *m = CryptSetup{} } func (m *CryptSetup) Reset() { *m = CryptSetup{} }
func (m *CryptSetup) String() string { return proto.CompactTextString(m) } func (m *CryptSetup) String() string { return proto.CompactTextString(m) }
func (*CryptSetup) ProtoMessage() {} func (*CryptSetup) ProtoMessage() {}
func (*CryptSetup) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} }
func (m *CryptSetup) GetKey() []byte { func (m *CryptSetup) GetKey() []byte {
if m != nil { if m != nil {
@ -1429,9 +1470,10 @@ type ContextActionModify struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *ContextActionModify) Reset() { *m = ContextActionModify{} } func (m *ContextActionModify) Reset() { *m = ContextActionModify{} }
func (m *ContextActionModify) String() string { return proto.CompactTextString(m) } func (m *ContextActionModify) String() string { return proto.CompactTextString(m) }
func (*ContextActionModify) ProtoMessage() {} func (*ContextActionModify) ProtoMessage() {}
func (*ContextActionModify) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} }
func (m *ContextActionModify) GetAction() string { func (m *ContextActionModify) GetAction() string {
if m != nil && m.Action != nil { if m != nil && m.Action != nil {
@ -1466,15 +1508,16 @@ type ContextAction struct {
// The target User for the action, identified by session. // The target User for the action, identified by session.
Session *uint32 `protobuf:"varint,1,opt,name=session" json:"session,omitempty"` Session *uint32 `protobuf:"varint,1,opt,name=session" json:"session,omitempty"`
// The target Channel for the action, identified by channel_id. // The target Channel for the action, identified by channel_id.
ChannelId *uint32 `protobuf:"varint,2,opt,name=channel_id" json:"channel_id,omitempty"` ChannelId *uint32 `protobuf:"varint,2,opt,name=channel_id,json=channelId" json:"channel_id,omitempty"`
// The action that should be executed. // The action that should be executed.
Action *string `protobuf:"bytes,3,req,name=action" json:"action,omitempty"` Action *string `protobuf:"bytes,3,req,name=action" json:"action,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *ContextAction) Reset() { *m = ContextAction{} } func (m *ContextAction) Reset() { *m = ContextAction{} }
func (m *ContextAction) String() string { return proto.CompactTextString(m) } func (m *ContextAction) String() string { return proto.CompactTextString(m) }
func (*ContextAction) ProtoMessage() {} func (*ContextAction) ProtoMessage() {}
func (*ContextAction) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} }
func (m *ContextAction) GetSession() uint32 { func (m *ContextAction) GetSession() uint32 {
if m != nil && m.Session != nil { if m != nil && m.Session != nil {
@ -1504,9 +1547,10 @@ type UserList struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *UserList) Reset() { *m = UserList{} } func (m *UserList) Reset() { *m = UserList{} }
func (m *UserList) String() string { return proto.CompactTextString(m) } func (m *UserList) String() string { return proto.CompactTextString(m) }
func (*UserList) ProtoMessage() {} func (*UserList) ProtoMessage() {}
func (*UserList) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} }
func (m *UserList) GetUsers() []*UserList_User { func (m *UserList) GetUsers() []*UserList_User {
if m != nil { if m != nil {
@ -1517,17 +1561,18 @@ func (m *UserList) GetUsers() []*UserList_User {
type UserList_User struct { type UserList_User struct {
// Registered user ID. // Registered user ID.
UserId *uint32 `protobuf:"varint,1,req,name=user_id" json:"user_id,omitempty"` UserId *uint32 `protobuf:"varint,1,req,name=user_id,json=userId" json:"user_id,omitempty"`
// Registered user name. // Registered user name.
Name *string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"` Name *string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"`
LastSeen *string `protobuf:"bytes,3,opt,name=last_seen" json:"last_seen,omitempty"` LastSeen *string `protobuf:"bytes,3,opt,name=last_seen,json=lastSeen" json:"last_seen,omitempty"`
LastChannel *uint32 `protobuf:"varint,4,opt,name=last_channel" json:"last_channel,omitempty"` LastChannel *uint32 `protobuf:"varint,4,opt,name=last_channel,json=lastChannel" json:"last_channel,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *UserList_User) Reset() { *m = UserList_User{} } func (m *UserList_User) Reset() { *m = UserList_User{} }
func (m *UserList_User) String() string { return proto.CompactTextString(m) } func (m *UserList_User) String() string { return proto.CompactTextString(m) }
func (*UserList_User) ProtoMessage() {} func (*UserList_User) ProtoMessage() {}
func (*UserList_User) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18, 0} }
func (m *UserList_User) GetUserId() uint32 { func (m *UserList_User) GetUserId() uint32 {
if m != nil && m.UserId != nil { if m != nil && m.UserId != nil {
@ -1569,9 +1614,10 @@ type VoiceTarget struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *VoiceTarget) Reset() { *m = VoiceTarget{} } func (m *VoiceTarget) Reset() { *m = VoiceTarget{} }
func (m *VoiceTarget) String() string { return proto.CompactTextString(m) } func (m *VoiceTarget) String() string { return proto.CompactTextString(m) }
func (*VoiceTarget) ProtoMessage() {} func (*VoiceTarget) ProtoMessage() {}
func (*VoiceTarget) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} }
func (m *VoiceTarget) GetId() uint32 { func (m *VoiceTarget) GetId() uint32 {
if m != nil && m.Id != nil { if m != nil && m.Id != nil {
@ -1590,9 +1636,9 @@ func (m *VoiceTarget) GetTargets() []*VoiceTarget_Target {
type VoiceTarget_Target struct { type VoiceTarget_Target struct {
// Users that are included as targets. // Users that are included as targets.
Session []uint32 `protobuf:"varint,1,rep,name=session" json:"session,omitempty"` Session []uint32 `protobuf:"varint,1,rep,name=session" json:"session,omitempty"`
// Channels that are included as targets. // Channel that is included as a target.
ChannelId *uint32 `protobuf:"varint,2,opt,name=channel_id" json:"channel_id,omitempty"` ChannelId *uint32 `protobuf:"varint,2,opt,name=channel_id,json=channelId" json:"channel_id,omitempty"`
// TODO ?? // ACL group that is included as a target.
Group *string `protobuf:"bytes,3,opt,name=group" json:"group,omitempty"` Group *string `protobuf:"bytes,3,opt,name=group" json:"group,omitempty"`
// True if the voice should follow links from the specified channel. // True if the voice should follow links from the specified channel.
Links *bool `protobuf:"varint,4,opt,name=links,def=0" json:"links,omitempty"` Links *bool `protobuf:"varint,4,opt,name=links,def=0" json:"links,omitempty"`
@ -1602,9 +1648,10 @@ type VoiceTarget_Target struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *VoiceTarget_Target) Reset() { *m = VoiceTarget_Target{} } func (m *VoiceTarget_Target) Reset() { *m = VoiceTarget_Target{} }
func (m *VoiceTarget_Target) String() string { return proto.CompactTextString(m) } func (m *VoiceTarget_Target) String() string { return proto.CompactTextString(m) }
func (*VoiceTarget_Target) ProtoMessage() {} func (*VoiceTarget_Target) ProtoMessage() {}
func (*VoiceTarget_Target) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19, 0} }
const Default_VoiceTarget_Target_Links bool = false const Default_VoiceTarget_Target_Links bool = false
const Default_VoiceTarget_Target_Children bool = false const Default_VoiceTarget_Target_Children bool = false
@ -1649,7 +1696,7 @@ func (m *VoiceTarget_Target) GetChildren() bool {
// channel permissions. // channel permissions.
type PermissionQuery struct { type PermissionQuery struct {
// channel_id of the channel for which the permissions are queried. // channel_id of the channel for which the permissions are queried.
ChannelId *uint32 `protobuf:"varint,1,opt,name=channel_id" json:"channel_id,omitempty"` ChannelId *uint32 `protobuf:"varint,1,opt,name=channel_id,json=channelId" json:"channel_id,omitempty"`
// Channel permissions. // Channel permissions.
Permissions *uint32 `protobuf:"varint,2,opt,name=permissions" json:"permissions,omitempty"` Permissions *uint32 `protobuf:"varint,2,opt,name=permissions" json:"permissions,omitempty"`
// True if the client should drop its current permission information for all // True if the client should drop its current permission information for all
@ -1658,9 +1705,10 @@ type PermissionQuery struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *PermissionQuery) Reset() { *m = PermissionQuery{} } func (m *PermissionQuery) Reset() { *m = PermissionQuery{} }
func (m *PermissionQuery) String() string { return proto.CompactTextString(m) } func (m *PermissionQuery) String() string { return proto.CompactTextString(m) }
func (*PermissionQuery) ProtoMessage() {} func (*PermissionQuery) ProtoMessage() {}
func (*PermissionQuery) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} }
const Default_PermissionQuery_Flush bool = false const Default_PermissionQuery_Flush bool = false
@ -1693,14 +1741,15 @@ type CodecVersion struct {
// The version of the CELT Beta codec. // The version of the CELT Beta codec.
Beta *int32 `protobuf:"varint,2,req,name=beta" json:"beta,omitempty"` Beta *int32 `protobuf:"varint,2,req,name=beta" json:"beta,omitempty"`
// True if the user should prefer Alpha over Beta. // True if the user should prefer Alpha over Beta.
PreferAlpha *bool `protobuf:"varint,3,req,name=prefer_alpha,def=1" json:"prefer_alpha,omitempty"` PreferAlpha *bool `protobuf:"varint,3,req,name=prefer_alpha,json=preferAlpha,def=1" json:"prefer_alpha,omitempty"`
Opus *bool `protobuf:"varint,4,opt,name=opus,def=0" json:"opus,omitempty"` Opus *bool `protobuf:"varint,4,opt,name=opus,def=0" json:"opus,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *CodecVersion) Reset() { *m = CodecVersion{} } func (m *CodecVersion) Reset() { *m = CodecVersion{} }
func (m *CodecVersion) String() string { return proto.CompactTextString(m) } func (m *CodecVersion) String() string { return proto.CompactTextString(m) }
func (*CodecVersion) ProtoMessage() {} func (*CodecVersion) ProtoMessage() {}
func (*CodecVersion) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} }
const Default_CodecVersion_PreferAlpha bool = true const Default_CodecVersion_PreferAlpha bool = true
const Default_CodecVersion_Opus bool = false const Default_CodecVersion_Opus bool = false
@ -1738,30 +1787,30 @@ type UserStats struct {
// User whose stats these are. // User whose stats these are.
Session *uint32 `protobuf:"varint,1,opt,name=session" json:"session,omitempty"` Session *uint32 `protobuf:"varint,1,opt,name=session" json:"session,omitempty"`
// True if the message contains only mutable stats (packets, ping). // True if the message contains only mutable stats (packets, ping).
StatsOnly *bool `protobuf:"varint,2,opt,name=stats_only,def=0" json:"stats_only,omitempty"` StatsOnly *bool `protobuf:"varint,2,opt,name=stats_only,json=statsOnly,def=0" json:"stats_only,omitempty"`
// Full user certificate chain of the user certificate in DER format. // Full user certificate chain of the user certificate in DER format.
Certificates [][]byte `protobuf:"bytes,3,rep,name=certificates" json:"certificates,omitempty"` Certificates [][]byte `protobuf:"bytes,3,rep,name=certificates" json:"certificates,omitempty"`
// Packet statistics for packets received from the client. // Packet statistics for packets received from the client.
FromClient *UserStats_Stats `protobuf:"bytes,4,opt,name=from_client" json:"from_client,omitempty"` FromClient *UserStats_Stats `protobuf:"bytes,4,opt,name=from_client,json=fromClient" json:"from_client,omitempty"`
// Packet statistics for packets sent by the server. // Packet statistics for packets sent by the server.
FromServer *UserStats_Stats `protobuf:"bytes,5,opt,name=from_server" json:"from_server,omitempty"` FromServer *UserStats_Stats `protobuf:"bytes,5,opt,name=from_server,json=fromServer" json:"from_server,omitempty"`
// Amount of UDP packets sent. // Amount of UDP packets sent.
UdpPackets *uint32 `protobuf:"varint,6,opt,name=udp_packets" json:"udp_packets,omitempty"` UdpPackets *uint32 `protobuf:"varint,6,opt,name=udp_packets,json=udpPackets" json:"udp_packets,omitempty"`
// Amount of TCP packets sent. // Amount of TCP packets sent.
TcpPackets *uint32 `protobuf:"varint,7,opt,name=tcp_packets" json:"tcp_packets,omitempty"` TcpPackets *uint32 `protobuf:"varint,7,opt,name=tcp_packets,json=tcpPackets" json:"tcp_packets,omitempty"`
// UDP ping average. // UDP ping average.
UdpPingAvg *float32 `protobuf:"fixed32,8,opt,name=udp_ping_avg" json:"udp_ping_avg,omitempty"` UdpPingAvg *float32 `protobuf:"fixed32,8,opt,name=udp_ping_avg,json=udpPingAvg" json:"udp_ping_avg,omitempty"`
// UDP ping variance. // UDP ping variance.
UdpPingVar *float32 `protobuf:"fixed32,9,opt,name=udp_ping_var" json:"udp_ping_var,omitempty"` UdpPingVar *float32 `protobuf:"fixed32,9,opt,name=udp_ping_var,json=udpPingVar" json:"udp_ping_var,omitempty"`
// TCP ping average. // TCP ping average.
TcpPingAvg *float32 `protobuf:"fixed32,10,opt,name=tcp_ping_avg" json:"tcp_ping_avg,omitempty"` TcpPingAvg *float32 `protobuf:"fixed32,10,opt,name=tcp_ping_avg,json=tcpPingAvg" json:"tcp_ping_avg,omitempty"`
// TCP ping variance. // TCP ping variance.
TcpPingVar *float32 `protobuf:"fixed32,11,opt,name=tcp_ping_var" json:"tcp_ping_var,omitempty"` TcpPingVar *float32 `protobuf:"fixed32,11,opt,name=tcp_ping_var,json=tcpPingVar" json:"tcp_ping_var,omitempty"`
// Client version. // Client version.
Version *Version `protobuf:"bytes,12,opt,name=version" json:"version,omitempty"` Version *Version `protobuf:"bytes,12,opt,name=version" json:"version,omitempty"`
// A list of CELT bitstream version constants supported by the client of this // A list of CELT bitstream version constants supported by the client of this
// user. // user.
CeltVersions []int32 `protobuf:"varint,13,rep,name=celt_versions" json:"celt_versions,omitempty"` CeltVersions []int32 `protobuf:"varint,13,rep,name=celt_versions,json=celtVersions" json:"celt_versions,omitempty"`
// Client IP address. // Client IP address.
Address []byte `protobuf:"bytes,14,opt,name=address" json:"address,omitempty"` Address []byte `protobuf:"bytes,14,opt,name=address" json:"address,omitempty"`
// Bandwith used by this client. // Bandwith used by this client.
@ -1771,14 +1820,15 @@ type UserStats struct {
// Duration since last activity. // Duration since last activity.
Idlesecs *uint32 `protobuf:"varint,17,opt,name=idlesecs" json:"idlesecs,omitempty"` Idlesecs *uint32 `protobuf:"varint,17,opt,name=idlesecs" json:"idlesecs,omitempty"`
// True if the user has a strong certificate. // True if the user has a strong certificate.
StrongCertificate *bool `protobuf:"varint,18,opt,name=strong_certificate,def=0" json:"strong_certificate,omitempty"` StrongCertificate *bool `protobuf:"varint,18,opt,name=strong_certificate,json=strongCertificate,def=0" json:"strong_certificate,omitempty"`
Opus *bool `protobuf:"varint,19,opt,name=opus,def=0" json:"opus,omitempty"` Opus *bool `protobuf:"varint,19,opt,name=opus,def=0" json:"opus,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *UserStats) Reset() { *m = UserStats{} } func (m *UserStats) Reset() { *m = UserStats{} }
func (m *UserStats) String() string { return proto.CompactTextString(m) } func (m *UserStats) String() string { return proto.CompactTextString(m) }
func (*UserStats) ProtoMessage() {} func (*UserStats) ProtoMessage() {}
func (*UserStats) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{22} }
const Default_UserStats_StatsOnly bool = false const Default_UserStats_StatsOnly bool = false
const Default_UserStats_StrongCertificate bool = false const Default_UserStats_StrongCertificate bool = false
@ -1929,9 +1979,10 @@ type UserStats_Stats struct {
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *UserStats_Stats) Reset() { *m = UserStats_Stats{} } func (m *UserStats_Stats) Reset() { *m = UserStats_Stats{} }
func (m *UserStats_Stats) String() string { return proto.CompactTextString(m) } func (m *UserStats_Stats) String() string { return proto.CompactTextString(m) }
func (*UserStats_Stats) ProtoMessage() {} func (*UserStats_Stats) ProtoMessage() {}
func (*UserStats_Stats) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{22, 0} }
func (m *UserStats_Stats) GetGood() uint32 { func (m *UserStats_Stats) GetGood() uint32 {
if m != nil && m.Good != nil { if m != nil && m.Good != nil {
@ -1971,17 +2022,18 @@ func (m *UserStats_Stats) GetResync() uint32 {
// normally be transmitted as hashes. // normally be transmitted as hashes.
type RequestBlob struct { type RequestBlob struct {
// sessions of the requested UserState textures. // sessions of the requested UserState textures.
SessionTexture []uint32 `protobuf:"varint,1,rep,name=session_texture" json:"session_texture,omitempty"` SessionTexture []uint32 `protobuf:"varint,1,rep,name=session_texture,json=sessionTexture" json:"session_texture,omitempty"`
// sessions of the requested UserState comments. // sessions of the requested UserState comments.
SessionComment []uint32 `protobuf:"varint,2,rep,name=session_comment" json:"session_comment,omitempty"` SessionComment []uint32 `protobuf:"varint,2,rep,name=session_comment,json=sessionComment" json:"session_comment,omitempty"`
// channel_ids of the requested ChannelState descriptions. // channel_ids of the requested ChannelState descriptions.
ChannelDescription []uint32 `protobuf:"varint,3,rep,name=channel_description" json:"channel_description,omitempty"` ChannelDescription []uint32 `protobuf:"varint,3,rep,name=channel_description,json=channelDescription" json:"channel_description,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *RequestBlob) Reset() { *m = RequestBlob{} } func (m *RequestBlob) Reset() { *m = RequestBlob{} }
func (m *RequestBlob) String() string { return proto.CompactTextString(m) } func (m *RequestBlob) String() string { return proto.CompactTextString(m) }
func (*RequestBlob) ProtoMessage() {} func (*RequestBlob) ProtoMessage() {}
func (*RequestBlob) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{23} }
func (m *RequestBlob) GetSessionTexture() []uint32 { func (m *RequestBlob) GetSessionTexture() []uint32 {
if m != nil { if m != nil {
@ -2008,23 +2060,24 @@ func (m *RequestBlob) GetChannelDescription() []uint32 {
// details. // details.
type ServerConfig struct { type ServerConfig struct {
// The maximum bandwidth the clients should use. // The maximum bandwidth the clients should use.
MaxBandwidth *uint32 `protobuf:"varint,1,opt,name=max_bandwidth" json:"max_bandwidth,omitempty"` MaxBandwidth *uint32 `protobuf:"varint,1,opt,name=max_bandwidth,json=maxBandwidth" json:"max_bandwidth,omitempty"`
// Server welcome text. // Server welcome text.
WelcomeText *string `protobuf:"bytes,2,opt,name=welcome_text" json:"welcome_text,omitempty"` WelcomeText *string `protobuf:"bytes,2,opt,name=welcome_text,json=welcomeText" json:"welcome_text,omitempty"`
// True if the server allows HTML. // True if the server allows HTML.
AllowHtml *bool `protobuf:"varint,3,opt,name=allow_html" json:"allow_html,omitempty"` AllowHtml *bool `protobuf:"varint,3,opt,name=allow_html,json=allowHtml" json:"allow_html,omitempty"`
// Maximum text message length. // Maximum text message length.
MessageLength *uint32 `protobuf:"varint,4,opt,name=message_length" json:"message_length,omitempty"` MessageLength *uint32 `protobuf:"varint,4,opt,name=message_length,json=messageLength" json:"message_length,omitempty"`
// Maximum image message length. // Maximum image message length.
ImageMessageLength *uint32 `protobuf:"varint,5,opt,name=image_message_length" json:"image_message_length,omitempty"` ImageMessageLength *uint32 `protobuf:"varint,5,opt,name=image_message_length,json=imageMessageLength" json:"image_message_length,omitempty"`
// The maximum number of users allowed on the server. // The maximum number of users allowed on the server.
MaxUsers *uint32 `protobuf:"varint,6,opt,name=max_users" json:"max_users,omitempty"` MaxUsers *uint32 `protobuf:"varint,6,opt,name=max_users,json=maxUsers" json:"max_users,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *ServerConfig) Reset() { *m = ServerConfig{} } func (m *ServerConfig) Reset() { *m = ServerConfig{} }
func (m *ServerConfig) String() string { return proto.CompactTextString(m) } func (m *ServerConfig) String() string { return proto.CompactTextString(m) }
func (*ServerConfig) ProtoMessage() {} func (*ServerConfig) ProtoMessage() {}
func (*ServerConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{24} }
func (m *ServerConfig) GetMaxBandwidth() uint32 { func (m *ServerConfig) GetMaxBandwidth() uint32 {
if m != nil && m.MaxBandwidth != nil { if m != nil && m.MaxBandwidth != nil {
@ -2077,13 +2130,14 @@ type SuggestConfig struct {
// server. // server.
Positional *bool `protobuf:"varint,2,opt,name=positional" json:"positional,omitempty"` Positional *bool `protobuf:"varint,2,opt,name=positional" json:"positional,omitempty"`
// True if the administrator suggests push to talk to be used on this server. // True if the administrator suggests push to talk to be used on this server.
PushToTalk *bool `protobuf:"varint,3,opt,name=push_to_talk" json:"push_to_talk,omitempty"` PushToTalk *bool `protobuf:"varint,3,opt,name=push_to_talk,json=pushToTalk" json:"push_to_talk,omitempty"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
} }
func (m *SuggestConfig) Reset() { *m = SuggestConfig{} } func (m *SuggestConfig) Reset() { *m = SuggestConfig{} }
func (m *SuggestConfig) String() string { return proto.CompactTextString(m) } func (m *SuggestConfig) String() string { return proto.CompactTextString(m) }
func (*SuggestConfig) ProtoMessage() {} func (*SuggestConfig) ProtoMessage() {}
func (*SuggestConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{25} }
func (m *SuggestConfig) GetVersion() uint32 { func (m *SuggestConfig) GetVersion() uint32 {
if m != nil && m.Version != nil { if m != nil && m.Version != nil {
@ -2107,8 +2161,198 @@ func (m *SuggestConfig) GetPushToTalk() bool {
} }
func init() { func init() {
proto.RegisterType((*Version)(nil), "MumbleProto.Version")
proto.RegisterType((*UDPTunnel)(nil), "MumbleProto.UDPTunnel")
proto.RegisterType((*Authenticate)(nil), "MumbleProto.Authenticate")
proto.RegisterType((*Ping)(nil), "MumbleProto.Ping")
proto.RegisterType((*Reject)(nil), "MumbleProto.Reject")
proto.RegisterType((*ServerSync)(nil), "MumbleProto.ServerSync")
proto.RegisterType((*ChannelRemove)(nil), "MumbleProto.ChannelRemove")
proto.RegisterType((*ChannelState)(nil), "MumbleProto.ChannelState")
proto.RegisterType((*UserRemove)(nil), "MumbleProto.UserRemove")
proto.RegisterType((*UserState)(nil), "MumbleProto.UserState")
proto.RegisterType((*BanList)(nil), "MumbleProto.BanList")
proto.RegisterType((*BanList_BanEntry)(nil), "MumbleProto.BanList.BanEntry")
proto.RegisterType((*TextMessage)(nil), "MumbleProto.TextMessage")
proto.RegisterType((*PermissionDenied)(nil), "MumbleProto.PermissionDenied")
proto.RegisterType((*ACL)(nil), "MumbleProto.ACL")
proto.RegisterType((*ACL_ChanGroup)(nil), "MumbleProto.ACL.ChanGroup")
proto.RegisterType((*ACL_ChanACL)(nil), "MumbleProto.ACL.ChanACL")
proto.RegisterType((*QueryUsers)(nil), "MumbleProto.QueryUsers")
proto.RegisterType((*CryptSetup)(nil), "MumbleProto.CryptSetup")
proto.RegisterType((*ContextActionModify)(nil), "MumbleProto.ContextActionModify")
proto.RegisterType((*ContextAction)(nil), "MumbleProto.ContextAction")
proto.RegisterType((*UserList)(nil), "MumbleProto.UserList")
proto.RegisterType((*UserList_User)(nil), "MumbleProto.UserList.User")
proto.RegisterType((*VoiceTarget)(nil), "MumbleProto.VoiceTarget")
proto.RegisterType((*VoiceTarget_Target)(nil), "MumbleProto.VoiceTarget.Target")
proto.RegisterType((*PermissionQuery)(nil), "MumbleProto.PermissionQuery")
proto.RegisterType((*CodecVersion)(nil), "MumbleProto.CodecVersion")
proto.RegisterType((*UserStats)(nil), "MumbleProto.UserStats")
proto.RegisterType((*UserStats_Stats)(nil), "MumbleProto.UserStats.Stats")
proto.RegisterType((*RequestBlob)(nil), "MumbleProto.RequestBlob")
proto.RegisterType((*ServerConfig)(nil), "MumbleProto.ServerConfig")
proto.RegisterType((*SuggestConfig)(nil), "MumbleProto.SuggestConfig")
proto.RegisterEnum("MumbleProto.Reject_RejectType", Reject_RejectType_name, Reject_RejectType_value) proto.RegisterEnum("MumbleProto.Reject_RejectType", Reject_RejectType_name, Reject_RejectType_value)
proto.RegisterEnum("MumbleProto.PermissionDenied_DenyType", PermissionDenied_DenyType_name, PermissionDenied_DenyType_value) proto.RegisterEnum("MumbleProto.PermissionDenied_DenyType", PermissionDenied_DenyType_name, PermissionDenied_DenyType_value)
proto.RegisterEnum("MumbleProto.ContextActionModify_Context", ContextActionModify_Context_name, ContextActionModify_Context_value) proto.RegisterEnum("MumbleProto.ContextActionModify_Context", ContextActionModify_Context_name, ContextActionModify_Context_value)
proto.RegisterEnum("MumbleProto.ContextActionModify_Operation", ContextActionModify_Operation_name, ContextActionModify_Operation_value) proto.RegisterEnum("MumbleProto.ContextActionModify_Operation", ContextActionModify_Operation_name, ContextActionModify_Operation_value)
} }
func init() { proto.RegisterFile("Mumble.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 2418 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xdc, 0x58, 0x4b, 0x73, 0x23, 0x49,
0x11, 0xa6, 0xf5, 0xb2, 0x94, 0x92, 0x6c, 0xb9, 0x67, 0x58, 0x84, 0xf7, 0xdd, 0x0b, 0xcb, 0x00,
0x1b, 0x66, 0x71, 0xec, 0x65, 0x27, 0x82, 0x83, 0xc7, 0xc3, 0xe2, 0x09, 0xc6, 0x5e, 0xd3, 0xf6,
0xce, 0x1e, 0x38, 0x34, 0x6d, 0x75, 0x59, 0x6a, 0xdc, 0xea, 0x6e, 0xba, 0x5a, 0x9e, 0x55, 0x04,
0x47, 0xe0, 0x0a, 0x11, 0x1c, 0xb8, 0xf1, 0x03, 0x08, 0x62, 0x23, 0xf8, 0x01, 0x5c, 0xf8, 0x05,
0xfc, 0x01, 0x2e, 0x5c, 0xb9, 0x11, 0xc1, 0x9d, 0x7c, 0x54, 0xbf, 0x6c, 0xcd, 0xce, 0x72, 0xe5,
0x22, 0x55, 0x7e, 0x99, 0x5d, 0x95, 0x95, 0x95, 0xaf, 0x2a, 0x18, 0x9d, 0xac, 0x96, 0x97, 0x91,
0xda, 0x4f, 0xb3, 0x24, 0x4f, 0xec, 0xa1, 0x50, 0x67, 0x44, 0x38, 0x11, 0x6c, 0x3d, 0x53, 0x99,
0x0e, 0x93, 0xd8, 0x9e, 0xc2, 0xd6, 0x8d, 0x0c, 0xa7, 0xd6, 0x5b, 0xd6, 0x83, 0xb1, 0x5b, 0x90,
0xc4, 0xc9, 0x54, 0xa4, 0x7c, 0xad, 0xa6, 0x2d, 0xe4, 0x0c, 0xdc, 0x82, 0xb4, 0xb7, 0xa1, 0x95,
0xe8, 0x69, 0x9b, 0x41, 0x1c, 0xd9, 0xaf, 0x03, 0x24, 0xda, 0x2b, 0xa6, 0xe9, 0x30, 0x3e, 0x48,
0xb4, 0x59, 0xc2, 0x79, 0x07, 0x06, 0x9f, 0x3c, 0x3e, 0xbb, 0x58, 0xc5, 0xb1, 0x8a, 0xec, 0x57,
0xa0, 0x97, 0xfa, 0xb3, 0x6b, 0x95, 0xe3, 0x72, 0xad, 0x07, 0x23, 0xd7, 0x50, 0xce, 0x1f, 0x2d,
0x18, 0x1d, 0xae, 0xf2, 0x85, 0x8a, 0xf3, 0x70, 0xe6, 0xe7, 0xca, 0xde, 0x83, 0xfe, 0x4a, 0xab,
0x2c, 0xf6, 0x97, 0x8a, 0x35, 0x1b, 0xb8, 0x25, 0x4d, 0xbc, 0xd4, 0xd7, 0xfa, 0x79, 0x92, 0x05,
0x46, 0xb7, 0x92, 0xa6, 0x05, 0xf2, 0xe4, 0x5a, 0xc5, 0xa4, 0x60, 0x1b, 0x39, 0x86, 0xb2, 0xdf,
0x81, 0xf1, 0x4c, 0x45, 0x79, 0xa1, 0xa6, 0x46, 0x3d, 0xdb, 0x0f, 0xba, 0xee, 0x88, 0x40, 0xa3,
0xa9, 0xb6, 0xbf, 0x0e, 0x9d, 0x24, 0x5d, 0xe9, 0x69, 0x17, 0x27, 0xed, 0x3f, 0xec, 0x5e, 0xf9,
0x91, 0x56, 0x2e, 0x43, 0xce, 0xdf, 0x5a, 0xd0, 0x39, 0x0b, 0xe3, 0xb9, 0xfd, 0x1a, 0x0c, 0xf2,
0x70, 0xa9, 0x74, 0xee, 0x2f, 0x53, 0xd6, 0xac, 0xe3, 0x56, 0x80, 0x6d, 0x43, 0x67, 0x9e, 0x24,
0xa2, 0xd6, 0xd8, 0xe5, 0x31, 0x61, 0x11, 0x6e, 0x89, 0x2d, 0x86, 0x18, 0x8d, 0x19, 0x4b, 0x74,
0xce, 0xd6, 0x22, 0x0c, 0xc7, 0xa4, 0x7a, 0xa6, 0xf4, 0x3a, 0x9e, 0xf1, 0xfa, 0x63, 0xd7, 0x50,
0xf6, 0x9b, 0x30, 0x5c, 0x05, 0xa9, 0x27, 0x96, 0xd2, 0xd3, 0x1e, 0x33, 0x01, 0xa1, 0x33, 0x41,
0x48, 0x20, 0x9f, 0x55, 0x02, 0x5b, 0x22, 0x80, 0x50, 0x21, 0xf0, 0x16, 0x8c, 0x78, 0x06, 0xd4,
0xdf, 0xf3, 0x6f, 0xe6, 0xd3, 0x3e, 0x4a, 0xb4, 0x64, 0x0a, 0x84, 0x0e, 0x6f, 0xe6, 0x0d, 0x89,
0x1b, 0x3f, 0x9b, 0x0e, 0x1a, 0x12, 0xcf, 0xfc, 0x8c, 0x24, 0x78, 0x91, 0x62, 0x0e, 0x10, 0x09,
0x5a, 0xa5, 0x9a, 0xa3, 0x94, 0xa0, 0x39, 0x86, 0x0d, 0x09, 0x9c, 0xc3, 0xf9, 0x75, 0x0b, 0x7a,
0xae, 0xfa, 0xb9, 0x9a, 0xe5, 0xf6, 0x01, 0x74, 0xf2, 0x75, 0x2a, 0x67, 0xbb, 0x7d, 0xf0, 0xc6,
0x7e, 0xcd, 0x3f, 0xf7, 0x45, 0xc4, 0xfc, 0x5d, 0xa0, 0x94, 0xcb, 0xb2, 0x62, 0x20, 0x5f, 0xa3,
0x93, 0xc9, 0xa9, 0x1b, 0xca, 0xf9, 0xdc, 0x02, 0xa8, 0x84, 0xed, 0x3e, 0x74, 0x4e, 0x93, 0x58,
0x4d, 0xbe, 0x62, 0x4f, 0x60, 0xf4, 0x69, 0x96, 0xe0, 0xda, 0x72, 0xc0, 0x13, 0xcb, 0xbe, 0x07,
0x3b, 0x4f, 0xe2, 0x1b, 0x3f, 0x0a, 0x83, 0x4f, 0x8c, 0x37, 0x4d, 0x5a, 0xf6, 0x0e, 0x0c, 0x59,
0x8c, 0xa0, 0xb3, 0x4f, 0x27, 0x6d, 0x7b, 0x17, 0xc6, 0x0c, 0x9c, 0xab, 0xec, 0x86, 0xa1, 0x0e,
0x41, 0xc5, 0x17, 0x4f, 0x62, 0x1c, 0x4d, 0xba, 0x18, 0x07, 0x20, 0x02, 0x1f, 0xad, 0xa2, 0x68,
0xd2, 0x23, 0x91, 0xd3, 0xe4, 0x48, 0x65, 0x79, 0x78, 0xc5, 0x3e, 0x3c, 0xd9, 0xb2, 0xbf, 0x0a,
0xbb, 0x35, 0xaf, 0x4e, 0xb2, 0x8f, 0xfc, 0x30, 0x9a, 0xf4, 0x9d, 0xdf, 0x59, 0xc5, 0xa7, 0xe7,
0x74, 0xc0, 0x18, 0x6a, 0x5a, 0xe9, 0x7a, 0x10, 0x1a, 0x92, 0xbc, 0x76, 0xe9, 0x7f, 0xe6, 0x5d,
0xfa, 0x71, 0xf0, 0x3c, 0x0c, 0xf2, 0x85, 0xf1, 0xab, 0x11, 0x82, 0x8f, 0x0a, 0xcc, 0x7e, 0x1b,
0x46, 0xcf, 0x55, 0x34, 0x4b, 0x96, 0xca, 0xcb, 0xd5, 0x67, 0xb9, 0x89, 0xcc, 0xa1, 0xc1, 0x2e,
0x10, 0xc2, 0xa3, 0x19, 0xa6, 0x2a, 0x5b, 0x86, 0xba, 0xf0, 0x7d, 0x72, 0xdb, 0x3a, 0xe4, 0xec,
0xc3, 0xf8, 0x68, 0xe1, 0x53, 0x8c, 0xba, 0x6a, 0x99, 0xdc, 0x28, 0x8a, 0xea, 0x99, 0x00, 0x5e,
0x18, 0x70, 0xb4, 0x8e, 0xdd, 0x81, 0x41, 0x9e, 0x04, 0xce, 0x3f, 0x5a, 0x30, 0x32, 0x1f, 0x9c,
0xe7, 0xe4, 0xd1, 0xb7, 0xe5, 0xad, 0x86, 0xbc, 0x04, 0x7e, 0x86, 0x86, 0x30, 0x5b, 0x30, 0x14,
0x05, 0x02, 0xc7, 0xb8, 0x28, 0xcd, 0x63, 0xfb, 0x3e, 0x74, 0xa3, 0x30, 0xbe, 0x96, 0x18, 0x1d,
0xbb, 0x42, 0xd0, 0x1e, 0x02, 0xa5, 0x67, 0x59, 0x98, 0xe6, 0x64, 0xa9, 0xae, 0xec, 0xb2, 0x06,
0xd9, 0xaf, 0xc2, 0x80, 0x45, 0x3d, 0x3f, 0x08, 0x30, 0x4c, 0xe8, 0xdb, 0x3e, 0x03, 0x87, 0x41,
0x40, 0x56, 0x12, 0x66, 0xc6, 0xfb, 0xc3, 0x28, 0x21, 0xfe, 0x90, 0x31, 0xb3, 0x65, 0xcc, 0x54,
0xb9, 0x5a, 0xa6, 0x49, 0xe6, 0x67, 0x6b, 0x8e, 0x91, 0x32, 0x07, 0x54, 0x38, 0xee, 0xb3, 0x9f,
0x26, 0x3a, 0x64, 0x1d, 0x28, 0x4a, 0xba, 0x0f, 0xad, 0xf7, 0xdd, 0x12, 0xb2, 0xbf, 0x0d, 0x93,
0x9a, 0x4a, 0xde, 0xc2, 0xd7, 0x0b, 0x0e, 0x95, 0x91, 0xbb, 0x53, 0xc3, 0x8f, 0x11, 0x26, 0x75,
0xe9, 0x70, 0x29, 0xad, 0x69, 0x0e, 0x16, 0x54, 0x17, 0x01, 0x72, 0x33, 0xed, 0x5c, 0x01, 0xd0,
0xc0, 0x68, 0xd6, 0xf0, 0x90, 0x56, 0xdd, 0x43, 0xd0, 0x56, 0xfe, 0x0c, 0x3d, 0xcb, 0x98, 0x55,
0x88, 0x5a, 0xa4, 0xb4, 0xeb, 0x91, 0x82, 0x01, 0xd1, 0x46, 0x5f, 0xe2, 0xf3, 0xef, 0xbb, 0x34,
0x74, 0xfe, 0xdc, 0xc1, 0xf4, 0x8c, 0x0b, 0xc9, 0x21, 0xbe, 0xd8, 0x13, 0x37, 0xaf, 0xb3, 0xe9,
0xf4, 0xbe, 0x06, 0x5b, 0xb4, 0x25, 0xf2, 0x02, 0xc9, 0x6e, 0x3d, 0x22, 0xd1, 0x05, 0x9a, 0x1e,
0xd2, 0xbd, 0xed, 0x21, 0x38, 0xd7, 0x72, 0x85, 0x69, 0xb2, 0xc7, 0xca, 0xf1, 0x98, 0xb0, 0x40,
0xf9, 0x57, 0x9c, 0xd2, 0x10, 0xa3, 0x31, 0x65, 0x7f, 0xbd, 0x4a, 0x53, 0x4c, 0x8e, 0x5a, 0x0e,
0xc9, 0x2d, 0x69, 0x32, 0xa9, 0x56, 0xd1, 0x95, 0xc7, 0x13, 0x0d, 0x0c, 0x13, 0x81, 0x13, 0x9a,
0xac, 0x60, 0xf2, 0x8c, 0x50, 0x31, 0x1f, 0xd3, 0xac, 0xb8, 0x73, 0x0a, 0x9e, 0x55, 0xa6, 0xf8,
0x28, 0x46, 0x6e, 0x41, 0xda, 0xdf, 0x84, 0xed, 0x34, 0x5a, 0xcd, 0xc3, 0xd8, 0x9b, 0x25, 0x31,
0x07, 0xd8, 0x88, 0x05, 0xc6, 0x82, 0x1e, 0x09, 0x68, 0x7f, 0x0b, 0x76, 0x8c, 0x58, 0x18, 0x50,
0xbc, 0xe7, 0xeb, 0xe9, 0x98, 0xad, 0x62, 0xbe, 0x7e, 0x62, 0x50, 0x5a, 0x09, 0xe3, 0x72, 0x49,
0xa1, 0xb0, 0x2d, 0x85, 0xd5, 0x90, 0xb4, 0x5b, 0xf6, 0x97, 0x1d, 0xb1, 0x26, 0x8d, 0xc9, 0x6d,
0x0d, 0x5b, 0x7c, 0x69, 0xc2, 0x6b, 0x0f, 0x0d, 0x76, 0x6c, 0x44, 0x8c, 0xae, 0x22, 0xb2, 0x2b,
0x22, 0x06, 0x63, 0x11, 0xf4, 0xca, 0x34, 0x0b, 0x93, 0x0c, 0xd7, 0xf7, 0x74, 0xaa, 0xfc, 0x6b,
0x95, 0x4d, 0x6d, 0xb6, 0xc0, 0x4e, 0x81, 0x9f, 0x0b, 0x4c, 0xf5, 0x2d, 0x53, 0x33, 0x2c, 0xa5,
0x98, 0xb3, 0xa7, 0xf7, 0x58, 0xa6, 0x02, 0x9c, 0xdf, 0xb4, 0x60, 0x0b, 0x33, 0xcf, 0xd3, 0x10,
0xeb, 0xd5, 0xf7, 0xa1, 0x83, 0x1e, 0xa4, 0xd1, 0x53, 0xda, 0x0f, 0x86, 0x07, 0xaf, 0x37, 0x52,
0xb8, 0x91, 0xa1, 0xff, 0x1f, 0xc6, 0x79, 0xb6, 0x76, 0x59, 0x14, 0x8f, 0xa0, 0xfb, 0x8b, 0x95,
0xc2, 0xe8, 0x6a, 0xd5, 0xa3, 0x4b, 0xb0, 0xbd, 0x3f, 0x59, 0xd0, 0x2f, 0xe4, 0xc9, 0x4a, 0x18,
0xc5, 0x7c, 0xc8, 0xd2, 0x29, 0x14, 0x24, 0xfb, 0x89, 0xaf, 0xaf, 0x71, 0x0a, 0x0a, 0x04, 0x1e,
0x6f, 0xf4, 0xc3, 0xc2, 0x9a, 0x9d, 0x9a, 0x35, 0xab, 0xb8, 0xe8, 0x36, 0xe2, 0x02, 0xbd, 0x1b,
0xeb, 0x77, 0x96, 0xb3, 0xf3, 0x0d, 0x5c, 0x21, 0xc8, 0xd3, 0x82, 0x55, 0xe6, 0x73, 0xa8, 0x4b,
0x51, 0x2d, 0x69, 0xe7, 0xb7, 0x16, 0x0c, 0x29, 0xb5, 0x9e, 0xa0, 0x4a, 0xfe, 0x5c, 0x55, 0xf1,
0x61, 0xd5, 0xe3, 0xa3, 0x16, 0x4f, 0x2d, 0xce, 0x37, 0x65, 0x3c, 0x35, 0x83, 0xa1, 0xcd, 0xcc,
0x5a, 0x30, 0x60, 0x10, 0xe5, 0x99, 0x52, 0x12, 0x44, 0xc4, 0xeb, 0x11, 0x89, 0x0c, 0x9c, 0x71,
0x29, 0x4b, 0xe2, 0x16, 0x5a, 0xe4, 0x3d, 0x86, 0x74, 0x7e, 0xdf, 0x86, 0xc9, 0x59, 0x99, 0xd1,
0x1f, 0xab, 0x38, 0x54, 0x81, 0xfd, 0x06, 0x40, 0x95, 0xe5, 0x8d, 0x6e, 0x35, 0xe4, 0x96, 0x1a,
0xad, 0xdb, 0x31, 0x59, 0xd3, 0xbf, 0xdd, 0xcc, 0x07, 0x95, 0x25, 0x3b, 0x0d, 0x4b, 0x3e, 0x34,
0x75, 0xbd, 0xcb, 0x75, 0xfd, 0xdd, 0x86, 0x53, 0xdc, 0xd6, 0x6e, 0x1f, 0xff, 0xd6, 0xb5, 0xfa,
0x5e, 0x9c, 0x62, 0xaf, 0x3a, 0x45, 0xe7, 0xaf, 0xe8, 0x14, 0x85, 0x18, 0x55, 0x76, 0xb2, 0x39,
0x56, 0x76, 0xac, 0xbd, 0xd5, 0x6c, 0x58, 0xd7, 0xc7, 0x30, 0x38, 0x5f, 0xe1, 0xbe, 0x28, 0x95,
0x49, 0x45, 0x37, 0xc5, 0xe9, 0x94, 0x4a, 0x7c, 0x9b, 0x00, 0xfa, 0xf2, 0x22, 0x49, 0x9e, 0x62,
0x5d, 0xc7, 0x7a, 0xbe, 0x05, 0xed, 0xe3, 0x0f, 0x7f, 0x8c, 0x55, 0xfc, 0x3e, 0x4c, 0x2e, 0x8a,
0xe4, 0x6e, 0xbe, 0xc1, 0x5a, 0xfe, 0x0a, 0xd8, 0x27, 0x34, 0x79, 0x3c, 0x6f, 0x16, 0xf4, 0x11,
0xf4, 0x69, 0x09, 0x9e, 0xb5, 0x5f, 0x5b, 0x86, 0x5b, 0x80, 0x01, 0x35, 0x1c, 0xa7, 0xd8, 0x09,
0xe2, 0x67, 0x4f, 0xc3, 0x65, 0x98, 0x4f, 0xc0, 0xf9, 0x55, 0x17, 0xda, 0x87, 0x47, 0x4f, 0x5f,
0x52, 0x4e, 0x31, 0x7b, 0x8c, 0xc2, 0x78, 0xa1, 0x30, 0x10, 0x3d, 0x7f, 0x16, 0x69, 0x13, 0x1f,
0x9d, 0x3c, 0x5b, 0x29, 0x77, 0x68, 0x38, 0x87, 0xc8, 0xc0, 0xbe, 0xa9, 0x37, 0xcf, 0x92, 0x55,
0x2a, 0xfd, 0xed, 0xf0, 0x60, 0xaf, 0x61, 0x61, 0x5c, 0x69, 0x9f, 0x34, 0xfa, 0x11, 0x89, 0xb8,
0x46, 0xd2, 0x7e, 0x0f, 0x3a, 0x3c, 0x69, 0x87, 0xbf, 0x98, 0x6e, 0xfc, 0x02, 0xff, 0x5d, 0x96,
0xaa, 0x62, 0xb4, 0xbb, 0x21, 0x46, 0xff, 0x69, 0xc1, 0xa0, 0x5c, 0xa0, 0x3c, 0x30, 0x8b, 0x3d,
0x51, 0xc2, 0xce, 0x81, 0x81, 0xd1, 0x57, 0x05, 0x8d, 0x6d, 0x54, 0x30, 0x7a, 0xe5, 0x96, 0x21,
0xd8, 0xad, 0x0a, 0x89, 0x02, 0xb4, 0xdf, 0x85, 0x62, 0xcf, 0x3e, 0x2a, 0x2a, 0xe5, 0xea, 0x96,
0x31, 0x88, 0x41, 0xe5, 0x8c, 0x4a, 0x7d, 0x97, 0x23, 0x84, 0x86, 0xe2, 0x96, 0x5c, 0xdf, 0xa5,
0xfe, 0x1b, 0xca, 0xfe, 0x2e, 0xec, 0x96, 0xcb, 0x7b, 0x4b, 0xb5, 0xbc, 0xa4, 0x9a, 0x2b, 0x2d,
0xc0, 0xa4, 0x64, 0x9c, 0x08, 0xbe, 0xf7, 0x77, 0x0b, 0xb6, 0x8c, 0x4d, 0xb0, 0x27, 0x00, 0x3f,
0x4d, 0xa3, 0xb5, 0x87, 0x32, 0xd2, 0xad, 0x96, 0xfb, 0x61, 0xfc, 0x18, 0xe1, 0x4a, 0x48, 0xaf,
0x2e, 0x9b, 0x67, 0x27, 0x42, 0xe7, 0x08, 0x37, 0x0d, 0xd3, 0xde, 0x6c, 0x98, 0x17, 0xd6, 0x4e,
0x4c, 0x2f, 0x7c, 0x98, 0x26, 0x6f, 0x09, 0x21, 0xa8, 0x1f, 0xe7, 0xe6, 0x4e, 0x20, 0x84, 0x14,
0xcd, 0x78, 0x6d, 0x52, 0x16, 0x8f, 0x9d, 0x0f, 0x00, 0x7e, 0x42, 0x07, 0xc8, 0xcd, 0x05, 0xd9,
0x2d, 0x0c, 0x24, 0x71, 0xa3, 0xdd, 0x70, 0x48, 0x33, 0xd1, 0xe9, 0x69, 0x4e, 0x53, 0x38, 0x3f,
0x13, 0x4e, 0x00, 0x70, 0x94, 0xad, 0xd3, 0xfc, 0x5c, 0xe5, 0xb8, 0x1a, 0x7e, 0x75, 0xad, 0xd6,
0x6c, 0x83, 0x91, 0x4b, 0x43, 0x2e, 0x4e, 0x51, 0x48, 0xb5, 0x29, 0x4e, 0xe2, 0x99, 0x5c, 0x14,
0xa9, 0x38, 0x31, 0x76, 0x4a, 0x10, 0x89, 0x68, 0xee, 0x74, 0x8d, 0x48, 0x5b, 0x44, 0x04, 0x63,
0x11, 0xe7, 0x3f, 0x16, 0xdc, 0x33, 0x55, 0xf4, 0x70, 0x46, 0xc9, 0xf5, 0x24, 0x09, 0xc2, 0xab,
0x35, 0x9d, 0xa5, 0xcf, 0xb4, 0xf1, 0x2f, 0x43, 0xd1, 0xfe, 0xb8, 0x0c, 0xcb, 0x25, 0x80, 0xc7,
0x52, 0x54, 0xe3, 0xb2, 0xfd, 0x1d, 0xbb, 0x05, 0x69, 0x1f, 0xc3, 0x20, 0xc1, 0xc4, 0x20, 0x59,
0xbc, 0xc3, 0x59, 0xe9, 0x3b, 0x8d, 0x08, 0xd8, 0xb0, 0xf4, 0xfe, 0xc7, 0xc5, 0x17, 0x6e, 0xf5,
0xb1, 0xf3, 0x1e, 0x7a, 0x85, 0x99, 0x14, 0xa0, 0x27, 0xfd, 0x3b, 0xa6, 0x9e, 0xa1, 0x38, 0x0b,
0xe5, 0x8d, 0x16, 0x65, 0x28, 0x4e, 0x41, 0x1d, 0xe7, 0x2d, 0x18, 0x94, 0xb3, 0x50, 0xb6, 0xc1,
0x1e, 0x14, 0xf3, 0x16, 0xd0, 0x05, 0x88, 0x3c, 0x72, 0x62, 0x39, 0x3f, 0xc3, 0x96, 0xbb, 0xbe,
0xf6, 0x17, 0x74, 0x5f, 0x2f, 0x49, 0xd3, 0x95, 0xa5, 0xda, 0x75, 0x4b, 0x39, 0x7f, 0xb1, 0x24,
0x5d, 0x71, 0xb9, 0x7e, 0x1f, 0xba, 0xd2, 0x6a, 0x5a, 0x1b, 0x12, 0x47, 0x21, 0xc5, 0x03, 0x57,
0x04, 0xf7, 0xb4, 0x6c, 0xa6, 0xee, 0x95, 0x92, 0xb8, 0x0a, 0xaf, 0x2c, 0xe2, 0xbf, 0x55, 0x2b,
0xbb, 0xd4, 0x84, 0xfb, 0x3a, 0xf7, 0xb4, 0x52, 0x45, 0xf7, 0xd9, 0x27, 0xe0, 0x1c, 0x69, 0x6e,
0xc2, 0x89, 0x69, 0x54, 0x37, 0x4e, 0x3e, 0x24, 0xcc, 0xd8, 0xd0, 0xf9, 0x37, 0x16, 0xd6, 0x67,
0x49, 0x38, 0x53, 0x17, 0x7e, 0x36, 0x57, 0x39, 0xbd, 0x36, 0x94, 0xf7, 0x09, 0x1c, 0xd9, 0x1f,
0x62, 0x65, 0x64, 0x8e, 0xf8, 0xea, 0xf0, 0xe0, 0xcd, 0xc6, 0x46, 0x6a, 0x9f, 0xee, 0xcb, 0x9f,
0x5b, 0xc8, 0xef, 0xfd, 0xc1, 0x82, 0x9e, 0x99, 0xb5, 0x61, 0xea, 0xf6, 0xff, 0x60, 0xea, 0x32,
0x10, 0xdb, 0xf5, 0x40, 0x7c, 0xb5, 0xba, 0xb1, 0xd4, 0x73, 0xa6, 0x5c, 0x5c, 0xde, 0x86, 0xfe,
0x6c, 0x11, 0x46, 0xd8, 0xbd, 0xc4, 0xcd, 0x9c, 0x5a, 0xc2, 0x4e, 0x02, 0x3b, 0x55, 0x39, 0xe3,
0x40, 0x7d, 0xd9, 0x7d, 0xea, 0xd6, 0x8d, 0x4e, 0xf4, 0xac, 0x43, 0xa4, 0xd3, 0x55, 0xb4, 0xc2,
0x06, 0xa8, 0xdd, 0xd0, 0x89, 0x31, 0xe7, 0x97, 0x78, 0x7b, 0x4b, 0x02, 0x35, 0x2b, 0xde, 0x81,
0xa8, 0x7d, 0x89, 0xd2, 0x85, 0xcf, 0x07, 0xdc, 0x75, 0x85, 0xa0, 0xf3, 0xbd, 0x54, 0xb9, 0xcf,
0xad, 0x56, 0xd7, 0xe5, 0x31, 0x55, 0x2a, 0xec, 0xb5, 0xaf, 0xd0, 0x1d, 0xe4, 0x03, 0xf2, 0xb8,
0x32, 0x39, 0x0b, 0xe7, 0x90, 0x3f, 0x2e, 0x1e, 0x53, 0x3a, 0x77, 0x1f, 0x53, 0x3e, 0xef, 0x55,
0x97, 0x0e, 0xfd, 0x05, 0x6e, 0xff, 0x0d, 0x00, 0x4d, 0x22, 0x5e, 0x12, 0x47, 0xb7, 0x7a, 0xc6,
0x01, 0x33, 0x3e, 0x46, 0x1c, 0x13, 0xeb, 0x68, 0x56, 0x15, 0x69, 0x29, 0x8c, 0x23, 0xb7, 0x81,
0xd9, 0x3f, 0x80, 0xe1, 0x55, 0x96, 0x2c, 0x3d, 0x49, 0x4d, 0xac, 0xd3, 0xf0, 0xe0, 0xb5, 0x3b,
0x21, 0xc0, 0x0a, 0xed, 0xf3, 0xaf, 0x0b, 0xf4, 0xc1, 0x11, 0xcb, 0x97, 0x9f, 0x4b, 0xda, 0xe2,
0x53, 0xfc, 0x52, 0x9f, 0x4b, 0x92, 0xf8, 0xff, 0x79, 0xc1, 0xb1, 0xf7, 0xab, 0xf7, 0xc2, 0x11,
0x1b, 0xe1, 0x7e, 0x33, 0xfa, 0x84, 0x57, 0xbd, 0x22, 0xde, 0x79, 0x76, 0x1b, 0x6f, 0x78, 0x76,
0xab, 0xf5, 0xfa, 0xdb, 0x72, 0xf7, 0x2a, 0x7a, 0x7d, 0xbc, 0x8c, 0x54, 0x6f, 0x1f, 0x3b, 0x12,
0x03, 0x25, 0x40, 0xcd, 0x2d, 0x3a, 0x46, 0x18, 0x2b, 0xad, 0x66, 0x9a, 0x6f, 0x46, 0x68, 0xb4,
0x0a, 0xa1, 0xfe, 0x3d, 0x0c, 0x22, 0xe1, 0xee, 0x4a, 0xff, 0x5e, 0xd0, 0xf6, 0x07, 0x60, 0xeb,
0x9c, 0xde, 0x78, 0xbc, 0x9a, 0x9f, 0xc8, 0x9d, 0xa8, 0x70, 0xb1, 0x5d, 0x11, 0xa8, 0x35, 0x80,
0xa5, 0x4f, 0xdf, 0xbb, 0xe3, 0xd3, 0x7b, 0x3f, 0x85, 0xae, 0xb8, 0x73, 0xf1, 0x04, 0x68, 0x6d,
0x78, 0x02, 0x6c, 0x6d, 0x78, 0x02, 0x6c, 0x6f, 0x7c, 0x02, 0xec, 0xd4, 0x9f, 0x00, 0xe9, 0xc1,
0x68, 0xe8, 0x2a, 0x6c, 0xc1, 0x74, 0xfe, 0x28, 0x4a, 0x2e, 0xe9, 0xb2, 0x69, 0x62, 0xc4, 0x2b,
0x6e, 0xad, 0x92, 0xc6, 0xb6, 0x0d, 0x7c, 0x61, 0x2e, 0xaf, 0x35, 0xc1, 0xe2, 0xd2, 0xd9, 0x6a,
0x08, 0x1e, 0x99, 0xbb, 0xe7, 0xf7, 0xe0, 0x5e, 0x91, 0x6e, 0xea, 0xaf, 0x2c, 0x72, 0x31, 0xb1,
0x0d, 0xeb, 0x71, 0xc5, 0x71, 0xfe, 0x65, 0xc1, 0x48, 0xdc, 0x1b, 0x8b, 0xd8, 0x55, 0x38, 0xbf,
0xfb, 0x56, 0x65, 0x7d, 0x89, 0xb7, 0xaa, 0xd6, 0xdd, 0xb7, 0x2a, 0x4c, 0x7c, 0x7e, 0x14, 0x25,
0xcf, 0xbd, 0x45, 0xbe, 0x8c, 0x24, 0x79, 0x61, 0x1b, 0x45, 0xc8, 0x31, 0x02, 0x74, 0x1d, 0x37,
0x37, 0x1e, 0x2f, 0x52, 0xf1, 0x3c, 0x5f, 0x18, 0x53, 0x8d, 0x0d, 0xfa, 0x94, 0x41, 0xac, 0x76,
0xf7, 0xc3, 0x25, 0x09, 0xdd, 0x12, 0x96, 0x67, 0x07, 0x9b, 0x79, 0x27, 0x8d, 0x2f, 0x1a, 0xcf,
0x31, 0xbd, 0x5b, 0xcf, 0x31, 0xd7, 0x30, 0x3e, 0x5f, 0xcd, 0xe7, 0x68, 0x7f, 0xb3, 0xdb, 0x17,
0x3f, 0x9c, 0xd3, 0x95, 0xcb, 0xbc, 0x06, 0xf9, 0x91, 0x24, 0x2d, 0xb7, 0x86, 0x50, 0x90, 0xa1,
0xbf, 0x2c, 0xbc, 0x3c, 0xf1, 0x72, 0x3f, 0xba, 0x36, 0x3b, 0x04, 0xc2, 0x2e, 0x92, 0x0b, 0x44,
0x1e, 0xb5, 0x8e, 0xad, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x85, 0xe0, 0xf0, 0x3c, 0xbf, 0x17,
0x00, 0x00,
}

View file

@ -94,11 +94,12 @@ func (c *Client) handleUDPTunnel(buffer []byte) error {
} }
// Session // Session
buffer = buffer[1:]
session, n := varint.Decode(buffer) session, n := varint.Decode(buffer)
if n <= 0 { if n <= 0 {
return errInvalidProtobuf return errInvalidProtobuf
} }
buff := buffer[n:] buffer = buffer[n:]
user := c.Users[uint32(session)] user := c.Users[uint32(session)]
if user == nil { if user == nil {
return errInvalidProtobuf return errInvalidProtobuf
@ -117,25 +118,25 @@ func (c *Client) handleUDPTunnel(buffer []byte) error {
// Sequence // Sequence
// TODO: use in jitter buffer // TODO: use in jitter buffer
_, n = varint.Decode(buff) _, n = varint.Decode(buffer)
if n <= 0 { if n <= 0 {
return errInvalidProtobuf return errInvalidProtobuf
} }
buff = buff[n:] buffer = buffer[n:]
// Length // Length
length, n := varint.Decode(buff) length, n := varint.Decode(buffer)
if n <= 0 { if n <= 0 {
return errInvalidProtobuf return errInvalidProtobuf
} }
buff = buff[n:] buffer = buffer[n:]
// Opus audio packets set the 13th bit in the size field as the terminator. // Opus audio packets set the 13th bit in the size field as the terminator.
audioLength := int(length) &^ 0x2000 audioLength := int(length) &^ 0x2000
if audioLength > len(buff) { if audioLength > len(buffer) {
return errInvalidProtobuf return errInvalidProtobuf
} }
pcm, err := decoder.Decode(buff[:audioLength], AudioMaximumFrameSize) pcm, err := decoder.Decode(buffer[:audioLength], AudioMaximumFrameSize)
if err != nil { if err != nil {
return err return err
} }
@ -149,13 +150,13 @@ func (c *Client) handleUDPTunnel(buffer []byte) error {
AudioBuffer: AudioBuffer(pcm), AudioBuffer: AudioBuffer(pcm),
} }
if len(buff)-audioLength == 3*4 { if len(buffer)-audioLength == 3*4 {
// the packet has positional audio data; 3x float32 // the packet has positional audio data; 3x float32
buff = buff[audioLength:] buffer = buffer[audioLength:]
event.X = math.Float32frombits(binary.LittleEndian.Uint32(buff)) event.X = math.Float32frombits(binary.LittleEndian.Uint32(buffer))
event.Y = math.Float32frombits(binary.LittleEndian.Uint32(buff[4:])) event.Y = math.Float32frombits(binary.LittleEndian.Uint32(buffer[4:]))
event.Z = math.Float32frombits(binary.LittleEndian.Uint32(buff[8:])) event.Z = math.Float32frombits(binary.LittleEndian.Uint32(buffer[8:]))
event.HasPosition = true event.HasPosition = true
} }

View file

@ -37,15 +37,13 @@ func Ping(address string, interval, timeout time.Duration) (*PingResponse, error
if timeout < 0 { if timeout < 0 {
return nil, errors.New("gumble: timeout must be positive") return nil, errors.New("gumble: timeout must be positive")
} }
addr, err := net.ResolveUDPAddr("udp", address) deadline := time.Now().Add(timeout)
if err != nil { conn, err := net.DialTimeout("udp", address, timeout)
return nil, err
}
conn, err := net.DialUDP("udp", nil, addr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer conn.Close() defer conn.Close()
conn.SetReadDeadline(deadline)
var ( var (
idsLock sync.Mutex idsLock sync.Mutex
@ -83,7 +81,6 @@ func Ping(address string, interval, timeout time.Duration) (*PingResponse, error
buildSendPacket() buildSendPacket()
conn.SetReadDeadline(time.Now().Add(timeout))
for { for {
var incoming [24]byte var incoming [24]byte
if _, err := io.ReadFull(conn, incoming[:]); err != nil { if _, err := io.ReadFull(conn, incoming[:]); err != nil {
@ -98,7 +95,7 @@ func Ping(address string, interval, timeout time.Duration) (*PingResponse, error
} }
return &PingResponse{ return &PingResponse{
Address: addr, Address: conn.RemoteAddr().(*net.UDPAddr),
Ping: time.Since(sendTime), Ping: time.Since(sendTime),
Version: Version{ Version: Version{
Version: binary.BigEndian.Uint32(incoming[0:]), Version: binary.BigEndian.Uint32(incoming[0:]),

View file

@ -36,7 +36,17 @@ func Decode(b []byte) (int64, int) {
if (b[0]&0xFC) == 0xF4 && len(b) >= 9 { if (b[0]&0xFC) == 0xF4 && len(b) >= 9 {
return int64(binary.BigEndian.Uint64(b[1:])), 9 return int64(binary.BigEndian.Uint64(b[1:])), 9
} }
// TODO: 111110__ + varint Negative recursive varint // 111110__ + varint Negative recursive varint
// TODO: 111111xx Byte-inverted negative two bit number (~xx) if b[0]&0xFC == 0xF8 {
if v, n := Decode(b[1:]); n > 0 {
return -v, n + 1
}
return 0, 0
}
// 111111xx Byte-inverted negative two bit number (~xx)
if b[0]&0xFC == 0xFC {
return ^int64(b[0] & 0x03), 1
}
return 0, 0 return 0, 0
} }

View file

@ -0,0 +1,32 @@
package varint
import "testing"
func TestRange(t *testing.T) {
fn := func(i int64) {
var b [MaxVarintLen]byte
size := Encode(b[:], i)
if size == 0 {
t.Errorf("Encode returned size 0\n")
}
s := b[:size]
val, size := Decode(s)
if size == 0 {
t.Errorf("Decode return size 0\n")
}
if i != val {
t.Errorf("Encoded %d (%v) equals decoded %d\n", i, s, val)
}
}
for i := int64(-10000); i <= 10000; i++ {
fn(i)
}
fn(134342525)
fn(10282934828342)
fn(1028293482834200000)
}

View file

@ -11,7 +11,11 @@ const MaxVarintLen = 10
// Encode encodes the given value to varint format. // Encode encodes the given value to varint format.
func Encode(b []byte, value int64) int { func Encode(b []byte, value int64) int {
// TODO: 111111xx Byte-inverted negative two bit number (~xx) // 111111xx Byte-inverted negative two bit number (~xx)
if value <= -1 && value >= -4 {
b[0] = 0xFC | byte(^value&0xFF)
return 1
}
// 111110__ + varint Negative recursive varint // 111110__ + varint Negative recursive varint
if value < 0 { if value < 0 {
b[0] = 0xF8 b[0] = 0xF8

View file

@ -2,3 +2,5 @@
*.sublime-workspace *.sublime-workspace
*.un~ *.un~
*.swp *.swp
.idea/
*.iml

View file

@ -5,7 +5,6 @@
package properties package properties
import ( import (
"bytes"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
@ -36,14 +35,14 @@ func LoadString(s string) (*Properties, error) {
// LoadFile reads a file into a Properties struct. // LoadFile reads a file into a Properties struct.
func LoadFile(filename string, enc Encoding) (*Properties, error) { func LoadFile(filename string, enc Encoding) (*Properties, error) {
return loadFiles([]string{filename}, enc, false) return loadAll([]string{filename}, enc, false)
} }
// LoadFiles reads multiple files in the given order into // LoadFiles reads multiple files in the given order into
// a Properties struct. If 'ignoreMissing' is true then // a Properties struct. If 'ignoreMissing' is true then
// non-existent files will not be reported as error. // non-existent files will not be reported as error.
func LoadFiles(filenames []string, enc Encoding, ignoreMissing bool) (*Properties, error) { func LoadFiles(filenames []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
return loadFiles(filenames, enc, ignoreMissing) return loadAll(filenames, enc, ignoreMissing)
} }
// LoadURL reads the content of the URL into a Properties struct. // LoadURL reads the content of the URL into a Properties struct.
@ -55,7 +54,7 @@ func LoadFiles(filenames []string, enc Encoding, ignoreMissing bool) (*Propertie
// encoding is set to UTF-8. A missing content type header is // encoding is set to UTF-8. A missing content type header is
// interpreted as 'text/plain; charset=utf-8'. // interpreted as 'text/plain; charset=utf-8'.
func LoadURL(url string) (*Properties, error) { func LoadURL(url string) (*Properties, error) {
return loadURLs([]string{url}, false) return loadAll([]string{url}, UTF8, false)
} }
// LoadURLs reads the content of multiple URLs in the given order into a // LoadURLs reads the content of multiple URLs in the given order into a
@ -63,7 +62,15 @@ func LoadURL(url string) (*Properties, error) {
// not be reported as error. See LoadURL for the Content-Type header // not be reported as error. See LoadURL for the Content-Type header
// and the encoding. // and the encoding.
func LoadURLs(urls []string, ignoreMissing bool) (*Properties, error) { func LoadURLs(urls []string, ignoreMissing bool) (*Properties, error) {
return loadURLs(urls, ignoreMissing) return loadAll(urls, UTF8, ignoreMissing)
}
// LoadAll reads the content of multiple URLs or files in the given order into a
// Properties struct. If 'ignoreMissing' is true then a 404 status code or missing file will
// not be reported as error. Encoding sets the encoding for files. For the URLs please see
// LoadURL for the Content-Type header and the encoding.
func LoadAll(names []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
return loadAll(names, enc, ignoreMissing)
} }
// MustLoadString reads an UTF8 string into a Properties struct and // MustLoadString reads an UTF8 string into a Properties struct and
@ -98,6 +105,14 @@ func MustLoadURLs(urls []string, ignoreMissing bool) *Properties {
return must(LoadURLs(urls, ignoreMissing)) return must(LoadURLs(urls, ignoreMissing))
} }
// MustLoadAll reads the content of multiple URLs or files in the given order into a
// Properties struct. If 'ignoreMissing' is true then a 404 status code or missing file will
// not be reported as error. Encoding sets the encoding for files. For the URLs please see
// LoadURL for the Content-Type header and the encoding. It panics on error.
func MustLoadAll(names []string, enc Encoding, ignoreMissing bool) *Properties {
return must(LoadAll(names, enc, ignoreMissing))
}
func loadBuf(buf []byte, enc Encoding) (*Properties, error) { func loadBuf(buf []byte, enc Encoding) (*Properties, error) {
p, err := parse(convert(buf, enc)) p, err := parse(convert(buf, enc))
if err != nil { if err != nil {
@ -106,66 +121,78 @@ func loadBuf(buf []byte, enc Encoding) (*Properties, error) {
return p, p.check() return p, p.check()
} }
func loadFiles(filenames []string, enc Encoding, ignoreMissing bool) (*Properties, error) { func loadAll(names []string, enc Encoding, ignoreMissing bool) (*Properties, error) {
var buf bytes.Buffer result := NewProperties()
for _, filename := range filenames { for _, name := range names {
f, err := expandFilename(filename) n, err := expandName(name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var p *Properties
data, err := ioutil.ReadFile(f) if strings.HasPrefix(n, "http://") || strings.HasPrefix(n, "https://") {
p, err = loadURL(n, ignoreMissing)
} else {
p, err = loadFile(n, enc, ignoreMissing)
}
if err != nil { if err != nil {
if ignoreMissing && os.IsNotExist(err) {
LogPrintf("properties: %s not found. skipping", filename)
continue
}
return nil, err return nil, err
} }
result.Merge(p)
// concatenate the buffers and add a new line in case
// the previous file didn't end with a new line
buf.Write(data)
buf.WriteRune('\n')
} }
return loadBuf(buf.Bytes(), enc) return result, result.check()
} }
func loadURLs(urls []string, ignoreMissing bool) (*Properties, error) { func loadFile(filename string, enc Encoding, ignoreMissing bool) (*Properties, error) {
var buf bytes.Buffer data, err := ioutil.ReadFile(filename)
for _, u := range urls { if err != nil {
resp, err := http.Get(u) if ignoreMissing && os.IsNotExist(err) {
if err != nil { LogPrintf("properties: %s not found. skipping", filename)
return nil, fmt.Errorf("properties: error fetching %q. %s", u, err) return NewProperties(), nil
} }
if resp.StatusCode == 404 && ignoreMissing { return nil, err
LogPrintf("properties: %s returned %d. skipping", u, resp.StatusCode)
continue
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("properties: %s returned %d", u, resp.StatusCode)
}
body, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, fmt.Errorf("properties: %s error reading response. %s", u, err)
}
ct := resp.Header.Get("Content-Type")
var enc Encoding
switch strings.ToLower(ct) {
case "text/plain", "text/plain; charset=iso-8859-1", "text/plain; charset=latin1":
enc = ISO_8859_1
case "", "text/plain; charset=utf-8":
enc = UTF8
default:
return nil, fmt.Errorf("properties: invalid content type %s", ct)
}
buf.WriteString(convert(body, enc))
buf.WriteRune('\n')
} }
return loadBuf(buf.Bytes(), UTF8) p, err := parse(convert(data, enc))
if err != nil {
return nil, err
}
return p, nil
}
func loadURL(url string, ignoreMissing bool) (*Properties, error) {
resp, err := http.Get(url)
if err != nil {
return nil, fmt.Errorf("properties: error fetching %q. %s", url, err)
}
if resp.StatusCode == 404 && ignoreMissing {
LogPrintf("properties: %s returned %d. skipping", url, resp.StatusCode)
return NewProperties(), nil
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("properties: %s returned %d", url, resp.StatusCode)
}
body, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, fmt.Errorf("properties: %s error reading response. %s", url, err)
}
ct := resp.Header.Get("Content-Type")
var enc Encoding
switch strings.ToLower(ct) {
case "text/plain", "text/plain; charset=iso-8859-1", "text/plain; charset=latin1":
enc = ISO_8859_1
case "", "text/plain; charset=utf-8":
enc = UTF8
default:
return nil, fmt.Errorf("properties: invalid content type %s", ct)
}
p, err := parse(convert(body, enc))
if err != nil {
return nil, err
}
return p, nil
} }
func must(p *Properties, err error) *Properties { func must(p *Properties, err error) *Properties {
@ -175,12 +202,12 @@ func must(p *Properties, err error) *Properties {
return p return p
} }
// expandFilename expands ${ENV_VAR} expressions in a filename. // expandName expands ${ENV_VAR} expressions in a name.
// If the environment variable does not exist then it will be replaced // If the environment variable does not exist then it will be replaced
// with an empty string. Malformed expressions like "${ENV_VAR" will // with an empty string. Malformed expressions like "${ENV_VAR" will
// be reported as error. // be reported as error.
func expandFilename(filename string) (string, error) { func expandName(name string) (string, error) {
return expand(filename, make(map[string]bool), "${", "}", make(map[string]string)) return expand(name, make(map[string]bool), "${", "}", make(map[string]string))
} }
// Interprets a byte buffer either as an ISO-8859-1 or UTF-8 encoded string. // Interprets a byte buffer either as an ISO-8859-1 or UTF-8 encoded string.

View file

@ -124,6 +124,16 @@ func (s *LoadSuite) TestLoadURLFailInvalidEncoding(c *C) {
c.Assert(err, ErrorMatches, ".*invalid content type.*") c.Assert(err, ErrorMatches, ".*invalid content type.*")
} }
func (s *LoadSuite) TestLoadAll(c *C) {
filename := s.makeFile(c, "key=value")
filename2 := s.makeFile(c, "key2=value3")
filename3 := s.makeFile(c, "key=value4")
srv := testServer()
defer srv.Close()
p := MustLoadAll([]string{filename, filename2, srv.URL + "/a", srv.URL + "/b", filename3}, UTF8, false)
assertKeyValues(c, "", p, "key", "value4", "key2", "value2")
}
func (s *LoadSuite) SetUpSuite(c *C) { func (s *LoadSuite) SetUpSuite(c *C) {
s.tempFiles = make([]string, 0) s.tempFiles = make([]string, 0)
} }

View file

@ -630,6 +630,26 @@ func (p *Properties) Delete(key string) {
p.k = newKeys p.k = newKeys
} }
// Merge merges properties, comments and keys from other *Properties into p
func (p *Properties) Merge(other *Properties) {
for k,v := range other.m {
p.m[k] = v
}
for k,v := range other.c {
p.c[k] = v
}
outer:
for _, otherKey := range other.k {
for _, key := range p.k {
if otherKey == key {
continue outer
}
}
p.k = append(p.k, otherKey)
}
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// check expands all values and returns an error if a circular reference or // check expands all values and returns an error if a circular reference or

View file

@ -842,6 +842,20 @@ func (s *TestSuite) TestDeleteUnknownKey(c *C) {
c.Check(len(p.k), Equals, 1) c.Check(len(p.k), Equals, 1)
} }
func (s *TestSuite) TestMerge(c *C) {
input1 := "#comment\nkey=value\nkey2=value2"
input2 := "#another comment\nkey=another value\nkey3=value3"
p1, err := parse(input1)
c.Assert(err, IsNil)
p2, err := parse(input2)
p1.Merge(p2)
c.Check(len(p1.m), Equals, 3)
c.Check(len(p1.c), Equals, 1)
c.Check(len(p1.k), Equals, 3)
c.Check(p1.MustGet("key"), Equals, "another value")
c.Check(p1.GetComment("key"), Equals, "another comment")
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// tests all combinations of delimiters, leading and/or trailing whitespace and newlines. // tests all combinations of delimiters, leading and/or trailing whitespace and newlines.

View file

@ -144,6 +144,7 @@ func getAttrList(path string, attrList attrList, attrBuf []byte, options uint) (
uintptr(options), uintptr(options),
0, 0,
) )
use(unsafe.Pointer(_p0))
if e1 != 0 { if e1 != 0 {
return nil, e1 return nil, e1
} }
@ -196,6 +197,7 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf)) bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf))
} }
r0, _, e1 := Syscall(SYS_GETFSSTAT64, uintptr(_p0), bufsize, uintptr(flags)) r0, _, e1 := Syscall(SYS_GETFSSTAT64, uintptr(_p0), bufsize, uintptr(flags))
use(unsafe.Pointer(_p0))
n = int(r0) n = int(r0)
if e1 != 0 { if e1 != 0 {
err = e1 err = e1

View file

@ -109,6 +109,7 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf)) bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf))
} }
r0, _, e1 := Syscall(SYS_GETFSSTAT, uintptr(_p0), bufsize, uintptr(flags)) r0, _, e1 := Syscall(SYS_GETFSSTAT, uintptr(_p0), bufsize, uintptr(flags))
use(unsafe.Pointer(_p0))
n = int(r0) n = int(r0)
if e1 != 0 { if e1 != 0 {
err = e1 err = e1

View file

@ -129,6 +129,7 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf)) bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf))
} }
r0, _, e1 := Syscall(SYS_GETFSSTAT, uintptr(_p0), bufsize, uintptr(flags)) r0, _, e1 := Syscall(SYS_GETFSSTAT, uintptr(_p0), bufsize, uintptr(flags))
use(unsafe.Pointer(_p0))
n = int(r0) n = int(r0)
if e1 != 0 { if e1 != 0 {
err = e1 err = e1

View file

@ -111,6 +111,7 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf)) bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf))
} }
r0, _, e1 := Syscall(SYS_GETFSSTAT, uintptr(_p0), bufsize, uintptr(flags)) r0, _, e1 := Syscall(SYS_GETFSSTAT, uintptr(_p0), bufsize, uintptr(flags))
use(unsafe.Pointer(_p0))
n = int(r0) n = int(r0)
if e1 != 0 { if e1 != 0 {
err = e1 err = e1