Compare commits
112 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52d4705a91 | ||
|
|
7288f71201 | ||
|
|
9c43eff753 | ||
|
|
c8d7fdeedc | ||
|
|
c211152e23 | ||
|
|
ab75d5097e | ||
|
|
c3644c8d3b | ||
|
|
6438a3dba3 | ||
|
|
4b226a6a63 | ||
|
|
4801850013 | ||
|
|
6a7412bf2b | ||
|
|
5a1fd7dadd | ||
|
|
ac06a26809 | ||
|
|
61d56f26f8 | ||
|
|
6aa05b3981 | ||
|
|
aad60c882e | ||
|
|
fecca57507 | ||
|
|
2bcad846c0 | ||
|
|
15ad0165fc | ||
|
|
2e8ab11978 | ||
|
|
9a8ce9b17e | ||
|
|
16ab4c6fed | ||
|
|
e3ee0df7ba | ||
|
|
8f7ab280e2 | ||
|
|
dbedc99421 | ||
|
|
6cb359cb80 | ||
|
|
ae2ad824a9 | ||
|
|
02e3d7852b | ||
|
|
3893a035be | ||
|
|
658bdd9faa | ||
|
|
e1eebcd4e0 | ||
|
|
062b831e88 | ||
|
|
b275efaeff | ||
|
|
80d3033456 | ||
|
|
bd0516f09a | ||
|
|
df4d76e466 | ||
|
|
dcbd7f8cad | ||
|
|
73ec02ab9d | ||
|
|
d1f8347071 | ||
|
|
8601eedada | ||
|
|
9afd33cdfc | ||
|
|
5e1be8e558 | ||
|
|
835dd2635a | ||
|
|
f65b18c2f6 | ||
|
|
b0e7b84f40 | ||
|
|
1635db93c7 | ||
|
|
c4fe462d11 | ||
|
|
b1f403165d | ||
|
|
46e4317b77 | ||
|
|
e3ffbcadd8 | ||
|
|
b7d73077e5 | ||
|
|
77f61ee20a | ||
|
|
8967f02fc9 | ||
|
|
831ff6d0a9 | ||
|
|
2199174def | ||
|
|
55f41ddaab | ||
|
|
21305d93bf | ||
|
|
4478d5d904 | ||
|
|
cc6253a6b8 | ||
|
|
85f66853bc | ||
|
|
7464fd149c | ||
|
|
86f1a8019c | ||
|
|
b98d56dcf6 | ||
|
|
a3a8a5769d | ||
|
|
4dd8bae5c9 | ||
|
|
7ae45c42e7 | ||
|
|
7551b4e7a3 | ||
|
|
61bab22dde | ||
|
|
6dcc23ebb6 | ||
|
|
b06a574cc5 | ||
|
|
b56f80b1b8 | ||
|
|
20f6c05ec5 | ||
|
|
57fce93af7 | ||
|
|
110b6a1431 | ||
|
|
53cafa9f3d | ||
|
|
d4195deb3a | ||
|
|
400ecfb79c | ||
|
|
86151da271 | ||
|
|
44f3e2557d | ||
|
|
1f365c716e | ||
|
|
9efcc41ab2 | ||
|
|
13bbeeaceb | ||
|
|
da4dcec14d | ||
|
|
761c0b79c5 | ||
|
|
d93ab0496f | ||
|
|
66b6f9749d | ||
|
|
17c2d1f26a | ||
|
|
a79e632cdc | ||
|
|
f36498421b | ||
|
|
e45bbe4571 | ||
|
|
fb5a84212c | ||
|
|
dedc1c45a1 | ||
|
|
6a12f9ff84 | ||
|
|
641ed1873b | ||
|
|
1d50da4b1c | ||
|
|
c7897cca5d | ||
|
|
4091b6f6b4 | ||
|
|
766f35554e | ||
|
|
c86137449e | ||
|
|
efec01a92f | ||
|
|
4fcad8e04b | ||
|
|
4b4b2d790e | ||
|
|
ec6ae343dd | ||
|
|
b9fb361959 | ||
|
|
a189298ab0 | ||
|
|
714a2ad730 | ||
|
|
fa8b96dfa1 | ||
|
|
01955a0df8 | ||
|
|
ac4aee39e3 | ||
|
|
a0bca42a7a | ||
|
|
af543dcd05 | ||
|
|
af77109a47 |
22
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
# Docs: <https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/customizing-dependency-updates>
|
||||
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
- package-ecosystem: gomod
|
||||
directory: /
|
||||
schedule: {interval: weekly}
|
||||
reviewers: [42wim]
|
||||
assignees: [42wim]
|
||||
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule: {interval: weekly}
|
||||
reviewers: [42wim]
|
||||
assignees: [42wim]
|
||||
|
||||
- package-ecosystem: docker
|
||||
directory: /
|
||||
schedule: {interval: weekly}
|
||||
reviewers: [42wim]
|
||||
assignees: [42wim]
|
||||
16
.github/workflows/development.yml
vendored
@@ -11,12 +11,12 @@ jobs:
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
version: v1.29
|
||||
version: latest
|
||||
args: "-v --new-from-rev HEAD~5"
|
||||
test-build-upload:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.15.x, 1.16.x]
|
||||
go-version: [1.17.x]
|
||||
platform: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
@@ -35,23 +35,23 @@ jobs:
|
||||
run: |
|
||||
mkdir -p output/{win,lin,arm,mac}
|
||||
VERSION=$(git describe --tags)
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags "-s -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o output/lin/matterbridge-$VERSION-linux-amd64
|
||||
GOOS=windows GOARCH=amd64 go build -ldflags "-s -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o output/win/matterbridge-$VERSION-windows-amd64.exe
|
||||
GOOS=darwin GOARCH=amd64 go build -ldflags "-s -X main.githash=$(git log --pretty=format:'%h' -n 1)" -o output/mac/matterbridge-$VERSION-darwin-amd64
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -X github.com/42wim/matterbridge/version.GitHash=$(git log --pretty=format:'%h' -n 1)" -o output/lin/matterbridge-$VERSION-linux-amd64
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -X github.com/42wim/matterbridge/version.GitHash=$(git log --pretty=format:'%h' -n 1)" -o output/win/matterbridge-$VERSION-windows-amd64.exe
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -X github.com/42wim/matterbridge/version.GitHash=$(git log --pretty=format:'%h' -n 1)" -o output/mac/matterbridge-$VERSION-darwin-amd64
|
||||
- name: Upload linux 64-bit
|
||||
if: startsWith(matrix.go-version,'1.16')
|
||||
if: startsWith(matrix.go-version,'1.17')
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: matterbridge-linux-64bit
|
||||
path: output/lin
|
||||
- name: Upload windows 64-bit
|
||||
if: startsWith(matrix.go-version,'1.16')
|
||||
if: startsWith(matrix.go-version,'1.17')
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: matterbridge-windows-64bit
|
||||
path: output/win
|
||||
- name: Upload darwin 64-bit
|
||||
if: startsWith(matrix.go-version,'1.16')
|
||||
if: startsWith(matrix.go-version,'1.17')
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: matterbridge-darwin-64bit
|
||||
|
||||
68
.github/workflows/docker.yml
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
name: docker
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'master'
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
with:
|
||||
platforms: amd64,arm64
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
-
|
||||
name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
with:
|
||||
images: 42wim/matterbridge,ghcr.io/42wim/matterbridge
|
||||
flavor: |
|
||||
latest=true
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern=stable
|
||||
type=semver,pattern={{major}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
-
|
||||
name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Log into registry ghcr.io
|
||||
uses: docker/login-action@v1
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
-
|
||||
name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
3
.gitignore
vendored
@@ -4,3 +4,6 @@
|
||||
|
||||
# Exclude configuration file
|
||||
matterbridge.toml
|
||||
|
||||
# Exclude IDE Files
|
||||
.vscode
|
||||
|
||||
@@ -7,7 +7,7 @@ run:
|
||||
# concurrency: 4
|
||||
|
||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||
deadline: 2m
|
||||
deadline: 5m
|
||||
|
||||
# exit code when at least one issue was found, default is 1
|
||||
issues-exit-code: 1
|
||||
@@ -182,7 +182,28 @@ linters:
|
||||
- interfacer
|
||||
- goheader
|
||||
- noctx
|
||||
|
||||
- gci
|
||||
- errorlint
|
||||
- nlreturn
|
||||
- exhaustivestruct
|
||||
- forbidigo
|
||||
- wrapcheck
|
||||
- varnamelen
|
||||
- ireturn
|
||||
- errorlint
|
||||
- tparallel
|
||||
- wrapcheck
|
||||
- paralleltest
|
||||
- makezero
|
||||
- thelper
|
||||
- cyclop
|
||||
- revive
|
||||
- importas
|
||||
- gomoddirectives
|
||||
- promlinter
|
||||
- tagliatelle
|
||||
- errname
|
||||
- typecheck
|
||||
# rules to deal with reported isues
|
||||
issues:
|
||||
# List of regexps of issue texts to exclude, empty list by default.
|
||||
|
||||
@@ -18,8 +18,11 @@ builds:
|
||||
- arm
|
||||
- arm64
|
||||
- 386
|
||||
goarm:
|
||||
- 6
|
||||
- 7
|
||||
ldflags:
|
||||
- -s -w -X main.githash={{.ShortCommit}}
|
||||
- -s -w -X github.com/42wim/matterbridge/version.GitHash={{.ShortCommit}}
|
||||
|
||||
archives:
|
||||
-
|
||||
|
||||
@@ -3,7 +3,7 @@ FROM alpine AS builder
|
||||
COPY . /go/src/matterbridge
|
||||
RUN apk --no-cache add go git \
|
||||
&& cd /go/src/matterbridge \
|
||||
&& go build -mod vendor -ldflags "-X main.githash=$(git log --pretty=format:'%h' -n 1)" -o /bin/matterbridge
|
||||
&& CGO_ENABLED=0 go build -mod vendor -ldflags "-X github.com/42wim/matterbridge/version.GitHash=$(git log --pretty=format:'%h' -n 1)" -o /bin/matterbridge
|
||||
|
||||
FROM alpine
|
||||
RUN apk --no-cache add ca-certificates mailcap
|
||||
|
||||
28
README.md
@@ -67,6 +67,7 @@ And more...
|
||||
- [Bridge slack (#general) - discord (general)](#bridge-slack-general---discord-general)
|
||||
- [Running](#running)
|
||||
- [Docker](#docker)
|
||||
- [Systemd](#systemd)
|
||||
- [Changelog](#changelog)
|
||||
- [FAQ](#faq)
|
||||
- [Related projects](#related-projects)
|
||||
@@ -163,7 +164,7 @@ See <https://github.com/42wim/matterbridge/wiki>
|
||||
|
||||
### Binaries
|
||||
|
||||
- Latest stable release [v1.22.1](https://github.com/42wim/matterbridge/releases/latest)
|
||||
- Latest stable release [v1.23.2](https://github.com/42wim/matterbridge/releases/latest)
|
||||
- Development releases (follows master) can be downloaded [here](https://github.com/42wim/matterbridge/actions) selecting the latest green build and then artifacts.
|
||||
|
||||
To install or upgrade just download the latest [binary](https://github.com/42wim/matterbridge/releases/latest). On \*nix platforms you may need to make the binary executable - you can do this by running `chmod a+x` on the binary (example: `chmod a+x matterbridge-1.20.0-linux-64bit`). After downloading (and making the binary executable, if necessary), follow the instructions on the [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration.
|
||||
@@ -179,10 +180,18 @@ To install or upgrade just download the latest [binary](https://github.com/42wim
|
||||
Most people just want to use binaries, you can find those [here](https://github.com/42wim/matterbridge/releases/latest)
|
||||
|
||||
If you really want to build from source, follow these instructions:
|
||||
Go 1.13+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed.
|
||||
Go 1.17+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed.
|
||||
|
||||
To install the latest stable run:
|
||||
|
||||
```bash
|
||||
go get github.com/42wim/matterbridge
|
||||
go install github.com/42wim/matterbridge
|
||||
```
|
||||
|
||||
To install the latest dev run:
|
||||
|
||||
```bash
|
||||
go install github.com/42wim/matterbridge@master
|
||||
```
|
||||
|
||||
You should now have matterbridge binary in the ~/go/bin directory:
|
||||
@@ -212,8 +221,8 @@ All possible [settings](https://github.com/42wim/matterbridge/wiki/Settings) for
|
||||
|
||||
```toml
|
||||
[irc]
|
||||
[irc.freenode]
|
||||
Server="irc.freenode.net:6667"
|
||||
[irc.libera]
|
||||
Server="irc.libera.chat:6667"
|
||||
Nick="yourbotname"
|
||||
|
||||
[mattermost]
|
||||
@@ -229,7 +238,7 @@ All possible [settings](https://github.com/42wim/matterbridge/wiki/Settings) for
|
||||
name="mygateway"
|
||||
enable=true
|
||||
[[gateway.inout]]
|
||||
account="irc.freenode"
|
||||
account="irc.libera"
|
||||
channel="#testing"
|
||||
|
||||
[[gateway.inout]]
|
||||
@@ -286,6 +295,10 @@ Usage of ./matterbridge:
|
||||
|
||||
Please take a look at the [Docker Wiki page](https://github.com/42wim/matterbridge/wiki/Deploy:-Docker) for more information.
|
||||
|
||||
### Systemd
|
||||
|
||||
Please take a look at the [Service Files page](https://github.com/42wim/matterbridge/wiki/Service-files) for more information.
|
||||
|
||||
## Changelog
|
||||
|
||||
See [changelog.md](https://github.com/42wim/matterbridge/blob/master/changelog.md)
|
||||
@@ -323,6 +336,7 @@ See [FAQ](https://github.com/42wim/matterbridge/wiki/FAQ)
|
||||
- <https://daniele.tech/2019/02/how-to-use-matterbridge-to-connect-2-different-slack-workspaces/>
|
||||
- <https://userlinux.net/mattermost-and-matterbridge.html>
|
||||
- <https://nextcloud.com/blog/bridging-chat-services-in-talk/>
|
||||
- <https://minecraftchest1.wordpress.com/2021/06/05/how-to-install-and-setup-matterbridge/>
|
||||
- Youtube: [whatsapp - telegram bridging](https://www.youtube.com/watch?v=W-VXISoKtNc)
|
||||
|
||||
## Thanks
|
||||
@@ -363,7 +377,7 @@ Matterbridge wouldn't exist without these libraries:
|
||||
|
||||
[mb-discord]: https://discord.gg/AkKPtrQ
|
||||
[mb-gitter]: https://gitter.im/42wim/matterbridge
|
||||
[mb-irc]: https://webchat.freenode.net/?channels=matterbridgechat
|
||||
[mb-irc]: https://web.libera.chat/#matterbridge
|
||||
[mb-keybase]: https://keybase.io/team/matterbridge
|
||||
[mb-matrix]: https://riot.im/app/#/room/#matterbridge:matrix.org
|
||||
[mb-mattermost]: https://framateam.org/signup_user_complete/?id=tfqm33ggop8x3qgu4boeieta6e
|
||||
|
||||
@@ -23,6 +23,7 @@ const (
|
||||
EventRejoinChannels = "rejoin_channels"
|
||||
EventUserAction = "user_action"
|
||||
EventMsgDelete = "msg_delete"
|
||||
EventFileDelete = "file_delete"
|
||||
EventAPIConnected = "api_connected"
|
||||
EventUserTyping = "user_typing"
|
||||
EventGetChannelMembers = "get_channel_members"
|
||||
@@ -56,13 +57,14 @@ func (m Message) ParentValid() bool {
|
||||
}
|
||||
|
||||
type FileInfo struct {
|
||||
Name string
|
||||
Data *[]byte
|
||||
Comment string
|
||||
URL string
|
||||
Size int64
|
||||
Avatar bool
|
||||
SHA string
|
||||
Name string
|
||||
Data *[]byte
|
||||
Comment string
|
||||
URL string
|
||||
Size int64
|
||||
Avatar bool
|
||||
SHA string
|
||||
NativeID string
|
||||
}
|
||||
|
||||
type ChannelInfo struct {
|
||||
@@ -85,27 +87,28 @@ type ChannelMember struct {
|
||||
type ChannelMembers []ChannelMember
|
||||
|
||||
type Protocol struct {
|
||||
AuthCode string // steam
|
||||
BindAddress string // mattermost, slack // DEPRECATED
|
||||
Buffer int // api
|
||||
Charset string // irc
|
||||
ClientID string // msteams
|
||||
ColorNicks bool // only irc for now
|
||||
Debug bool // general
|
||||
DebugLevel int // only for irc now
|
||||
DisableWebPagePreview bool // telegram
|
||||
EditSuffix string // mattermost, slack, discord, telegram, gitter
|
||||
EditDisable bool // mattermost, slack, discord, telegram, gitter
|
||||
HTMLDisable bool // matrix
|
||||
IconURL string // mattermost, slack
|
||||
IgnoreFailureOnStart bool // general
|
||||
IgnoreNicks string // all protocols
|
||||
IgnoreMessages string // all protocols
|
||||
Jid string // xmpp
|
||||
JoinDelay string // all protocols
|
||||
Label string // all protocols
|
||||
Login string // mattermost, matrix
|
||||
LogFile string // general
|
||||
AllowMention []string // discord
|
||||
AuthCode string // steam
|
||||
BindAddress string // mattermost, slack // DEPRECATED
|
||||
Buffer int // api
|
||||
Charset string // irc
|
||||
ClientID string // msteams
|
||||
ColorNicks bool // only irc for now
|
||||
Debug bool // general
|
||||
DebugLevel int // only for irc now
|
||||
DisableWebPagePreview bool // telegram
|
||||
EditSuffix string // mattermost, slack, discord, telegram, gitter
|
||||
EditDisable bool // mattermost, slack, discord, telegram, gitter
|
||||
HTMLDisable bool // matrix
|
||||
IconURL string // mattermost, slack
|
||||
IgnoreFailureOnStart bool // general
|
||||
IgnoreNicks string // all protocols
|
||||
IgnoreMessages string // all protocols
|
||||
Jid string // xmpp
|
||||
JoinDelay string // all protocols
|
||||
Label string // all protocols
|
||||
Login string // mattermost, matrix
|
||||
LogFile string // general
|
||||
MediaDownloadBlackList []string
|
||||
MediaDownloadPath string // Basically MediaServerUpload, but instead of uploading it, just write it to a file on the same server.
|
||||
MediaDownloadSize int // all protocols
|
||||
@@ -119,6 +122,7 @@ type Protocol struct {
|
||||
MessageQueue int // IRC, size of message queue for flood control
|
||||
MessageSplit bool // IRC, split long messages with newlines on MessageLength instead of clipping
|
||||
Muc string // xmpp
|
||||
MxID string // matrix
|
||||
Name string // all protocols
|
||||
Nick string // all protocols
|
||||
NickFormatter string // mattermost, slack
|
||||
@@ -136,12 +140,13 @@ type Protocol struct {
|
||||
QuoteDisable bool // telegram
|
||||
QuoteFormat string // telegram
|
||||
QuoteLengthLimit int // telegram
|
||||
RealName string // IRC
|
||||
RejoinDelay int // IRC
|
||||
ReplaceMessages [][]string // all protocols
|
||||
ReplaceNicks [][]string // all protocols
|
||||
RemoteNickFormat string // all protocols
|
||||
RunCommands []string // IRC
|
||||
Server string // IRC,mattermost,XMPP,discord
|
||||
Server string // IRC,mattermost,XMPP,discord,matrix
|
||||
SessionFile string // msteams,whatsapp
|
||||
ShowJoinPart bool // all protocols
|
||||
ShowTopicChange bool // slack
|
||||
@@ -156,7 +161,7 @@ type Protocol struct {
|
||||
Team string // mattermost, keybase
|
||||
TeamID string // msteams
|
||||
TenantID string // msteams
|
||||
Token string // gitter, slack, discord, api
|
||||
Token string // gitter, slack, discord, api, matrix
|
||||
Topic string // zulip
|
||||
URL string // mattermost, slack // DEPRECATED
|
||||
UseAPI bool // mattermost, slack
|
||||
@@ -165,8 +170,9 @@ type Protocol struct {
|
||||
UseTLS bool // IRC
|
||||
UseDiscriminator bool // discord
|
||||
UseFirstName bool // telegram
|
||||
UseUserName bool // discord, matrix
|
||||
UseUserName bool // discord, matrix, mattermost
|
||||
UseInsecureURL bool // telegram
|
||||
UserName string // IRC
|
||||
VerboseJoinPart bool // IRC
|
||||
WebhookBindAddress string // mattermost, slack
|
||||
WebhookURL string // mattermost, slack
|
||||
|
||||
@@ -10,10 +10,14 @@ import (
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
"github.com/42wim/matterbridge/bridge/discord/transmitter"
|
||||
"github.com/42wim/matterbridge/bridge/helper"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"github.com/matterbridge/discordgo"
|
||||
)
|
||||
|
||||
const MessageLength = 1950
|
||||
const (
|
||||
MessageLength = 1950
|
||||
cFileUpload = "file_upload"
|
||||
)
|
||||
|
||||
type Bdiscord struct {
|
||||
*bridge.Config
|
||||
@@ -35,10 +39,20 @@ type Bdiscord struct {
|
||||
// Webhook specific logic
|
||||
useAutoWebhooks bool
|
||||
transmitter *transmitter.Transmitter
|
||||
cache *lru.Cache
|
||||
}
|
||||
|
||||
func New(cfg *bridge.Config) bridge.Bridger {
|
||||
b := &Bdiscord{Config: cfg}
|
||||
newCache, err := lru.New(5000)
|
||||
if err != nil {
|
||||
cfg.Log.Fatalf("Could not create LRU cache: %v", err)
|
||||
}
|
||||
|
||||
b := &Bdiscord{
|
||||
Config: cfg,
|
||||
cache: newCache,
|
||||
}
|
||||
|
||||
b.userMemberMap = make(map[string]*discordgo.Member)
|
||||
b.nickMemberMap = make(map[string]*discordgo.Member)
|
||||
b.channelInfoMap = make(map[string]*config.ChannelInfo)
|
||||
@@ -75,6 +89,9 @@ func (b *Bdiscord) Connect() error {
|
||||
b.c.AddHandler(b.messageDeleteBulk)
|
||||
b.c.AddHandler(b.memberAdd)
|
||||
b.c.AddHandler(b.memberRemove)
|
||||
if b.GetInt("debuglevel") == 1 {
|
||||
b.c.AddHandler(b.messageEvent)
|
||||
}
|
||||
// Add privileged intent for guild member tracking. This is needed to track nicks
|
||||
// for display names and @mention translation
|
||||
b.c.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsAllWithoutPrivileged |
|
||||
@@ -153,7 +170,7 @@ func (b *Bdiscord) Connect() error {
|
||||
return fmt.Errorf("use of removed WebhookURL setting")
|
||||
}
|
||||
|
||||
if b.GetInt("debuglevel") > 0 {
|
||||
if b.GetInt("debuglevel") == 2 {
|
||||
b.Log.Debug("enabling even more discord debug")
|
||||
b.c.Debug = true
|
||||
}
|
||||
@@ -280,10 +297,25 @@ func (b *Bdiscord) handleEventBotUser(msg *config.Message, channelID string) (st
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Delete a file
|
||||
if msg.Event == config.EventFileDelete {
|
||||
if msg.ID == "" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if fi, ok := b.cache.Get(cFileUpload + msg.ID); ok {
|
||||
err := b.c.ChannelMessageDelete(channelID, fi.(string)) // nolint:forcetypeassert
|
||||
b.cache.Remove(cFileUpload + msg.ID)
|
||||
return "", err
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("file %s not found", msg.ID)
|
||||
}
|
||||
|
||||
// Upload a file if it exists
|
||||
if msg.Extra != nil {
|
||||
for _, rmsg := range helper.HandleExtra(msg, b.General) {
|
||||
rmsg.Text = helper.ClipMessage(rmsg.Text, MessageLength)
|
||||
rmsg.Text = helper.ClipMessage(rmsg.Text, MessageLength, b.GetString("MessageClipped"))
|
||||
if _, err := b.c.ChannelMessageSend(channelID, rmsg.Username+rmsg.Text); err != nil {
|
||||
b.Log.Errorf("Could not send message %#v: %s", rmsg, err)
|
||||
}
|
||||
@@ -294,7 +326,7 @@ func (b *Bdiscord) handleEventBotUser(msg *config.Message, channelID string) (st
|
||||
}
|
||||
}
|
||||
|
||||
msg.Text = helper.ClipMessage(msg.Text, MessageLength)
|
||||
msg.Text = helper.ClipMessage(msg.Text, MessageLength, b.GetString("MessageClipped"))
|
||||
msg.Text = b.replaceUserMentions(msg.Text)
|
||||
|
||||
// Edit message
|
||||
@@ -304,7 +336,8 @@ func (b *Bdiscord) handleEventBotUser(msg *config.Message, channelID string) (st
|
||||
}
|
||||
|
||||
m := discordgo.MessageSend{
|
||||
Content: msg.Username + msg.Text,
|
||||
Content: msg.Username + msg.Text,
|
||||
AllowedMentions: b.getAllowedMentions(),
|
||||
}
|
||||
|
||||
if msg.ParentValid() {
|
||||
@@ -326,7 +359,6 @@ func (b *Bdiscord) handleEventBotUser(msg *config.Message, channelID string) (st
|
||||
|
||||
// handleUploadFile handles native upload of files
|
||||
func (b *Bdiscord) handleUploadFile(msg *config.Message, channelID string) (string, error) {
|
||||
var err error
|
||||
for _, f := range msg.Extra["file"] {
|
||||
fi := f.(config.FileInfo)
|
||||
file := discordgo.File{
|
||||
@@ -335,13 +367,19 @@ func (b *Bdiscord) handleUploadFile(msg *config.Message, channelID string) (stri
|
||||
Reader: bytes.NewReader(*fi.Data),
|
||||
}
|
||||
m := discordgo.MessageSend{
|
||||
Content: msg.Username + fi.Comment,
|
||||
Files: []*discordgo.File{&file},
|
||||
Content: msg.Username + fi.Comment,
|
||||
Files: []*discordgo.File{&file},
|
||||
AllowedMentions: b.getAllowedMentions(),
|
||||
}
|
||||
_, err = b.c.ChannelMessageSendComplex(channelID, &m)
|
||||
res, err := b.c.ChannelMessageSendComplex(channelID, &m)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("file upload failed: %s", err)
|
||||
}
|
||||
|
||||
// link file_upload_nativeID (file ID from the original bridge) to our upload id
|
||||
// so that we can remove this later when it eg needs to be deleted
|
||||
b.cache.Add(cFileUpload+fi.NativeID, res.ID)
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package bdiscord
|
||||
|
||||
import (
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/matterbridge/discordgo"
|
||||
)
|
||||
|
||||
@@ -31,6 +32,10 @@ func (b *Bdiscord) messageDeleteBulk(s *discordgo.Session, m *discordgo.MessageD
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bdiscord) messageEvent(s *discordgo.Session, m *discordgo.Event) {
|
||||
b.Log.Debug(spew.Sdump(m.Struct))
|
||||
}
|
||||
|
||||
func (b *Bdiscord) messageTyping(s *discordgo.Session, m *discordgo.TypingStart) {
|
||||
if !b.GetBool("ShowUserTyping") {
|
||||
return
|
||||
@@ -82,8 +87,9 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat
|
||||
|
||||
rmsg := config.Message{Account: b.Account, Avatar: "https://cdn.discordapp.com/avatars/" + m.Author.ID + "/" + m.Author.Avatar + ".jpg", UserID: m.Author.ID, ID: m.ID}
|
||||
|
||||
b.Log.Debugf("== Receiving event %#v", m.Message)
|
||||
|
||||
if m.Content != "" {
|
||||
b.Log.Debugf("== Receiving event %#v", m.Message)
|
||||
m.Message.Content = b.replaceChannelMentions(m.Message.Content)
|
||||
rmsg.Text, err = m.ContentWithMoreMentionsReplaced(b.c)
|
||||
if err != nil {
|
||||
|
||||
@@ -9,6 +9,30 @@ import (
|
||||
"github.com/matterbridge/discordgo"
|
||||
)
|
||||
|
||||
func (b *Bdiscord) getAllowedMentions() *discordgo.MessageAllowedMentions {
|
||||
// If AllowMention is not specified, then allow all mentions (default Discord behavior)
|
||||
if !b.IsKeySet("AllowMention") {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Otherwise, allow only the mentions that are specified
|
||||
allowedMentionTypes := make([]discordgo.AllowedMentionType, 0, 3)
|
||||
for _, m := range b.GetStringSlice("AllowMention") {
|
||||
switch m {
|
||||
case "everyone":
|
||||
allowedMentionTypes = append(allowedMentionTypes, discordgo.AllowedMentionTypeEveryone)
|
||||
case "roles":
|
||||
allowedMentionTypes = append(allowedMentionTypes, discordgo.AllowedMentionTypeRoles)
|
||||
case "users":
|
||||
allowedMentionTypes = append(allowedMentionTypes, discordgo.AllowedMentionTypeUsers)
|
||||
}
|
||||
}
|
||||
|
||||
return &discordgo.MessageAllowedMentions{
|
||||
Parse: allowedMentionTypes,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bdiscord) getNick(user *discordgo.User, guildID string) string {
|
||||
b.membersMutex.RLock()
|
||||
defer b.membersMutex.RUnlock()
|
||||
|
||||
@@ -63,9 +63,10 @@ func (b *Bdiscord) webhookSend(msg *config.Message, channelID string) (*discordg
|
||||
res, err = b.transmitter.Send(
|
||||
channelID,
|
||||
&discordgo.WebhookParams{
|
||||
Content: msg.Text,
|
||||
Username: msg.Username,
|
||||
AvatarURL: msg.Avatar,
|
||||
Content: msg.Text,
|
||||
Username: msg.Username,
|
||||
AvatarURL: msg.Avatar,
|
||||
AllowedMentions: b.getAllowedMentions(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@@ -81,17 +82,16 @@ func (b *Bdiscord) webhookSend(msg *config.Message, channelID string) (*discordg
|
||||
ContentType: "",
|
||||
Reader: bytes.NewReader(*fi.Data),
|
||||
}
|
||||
content := ""
|
||||
if msg.Text == "" {
|
||||
content = fi.Comment
|
||||
}
|
||||
content := fi.Comment
|
||||
|
||||
_, e2 := b.transmitter.Send(
|
||||
channelID,
|
||||
&discordgo.WebhookParams{
|
||||
Username: msg.Username,
|
||||
AvatarURL: msg.Avatar,
|
||||
File: &file,
|
||||
Content: content,
|
||||
Username: msg.Username,
|
||||
AvatarURL: msg.Avatar,
|
||||
File: &file,
|
||||
Content: content,
|
||||
AllowedMentions: b.getAllowedMentions(),
|
||||
},
|
||||
)
|
||||
if e2 != nil {
|
||||
@@ -114,7 +114,7 @@ func (b *Bdiscord) handleEventWebhook(msg *config.Message, channelID string) (st
|
||||
return "", nil
|
||||
}
|
||||
|
||||
msg.Text = helper.ClipMessage(msg.Text, MessageLength)
|
||||
msg.Text = helper.ClipMessage(msg.Text, MessageLength, b.GetString("MessageClipped"))
|
||||
msg.Text = b.replaceUserMentions(msg.Text)
|
||||
// discord username must be [0..32] max
|
||||
if len(msg.Username) > 32 {
|
||||
@@ -124,8 +124,9 @@ func (b *Bdiscord) handleEventWebhook(msg *config.Message, channelID string) (st
|
||||
if msg.ID != "" {
|
||||
b.Log.Debugf("Editing webhook message")
|
||||
err := b.transmitter.Edit(channelID, msg.ID, &discordgo.WebhookParams{
|
||||
Content: msg.Text,
|
||||
Username: msg.Username,
|
||||
Content: msg.Text,
|
||||
Username: msg.Username,
|
||||
AllowedMentions: b.getAllowedMentions(),
|
||||
})
|
||||
if err == nil {
|
||||
return msg.ID, nil
|
||||
|
||||
252
bridge/harmony/harmony.go
Normal file
@@ -0,0 +1,252 @@
|
||||
package harmony
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/42wim/matterbridge/bridge"
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
"github.com/harmony-development/shibshib"
|
||||
chatv1 "github.com/harmony-development/shibshib/gen/chat/v1"
|
||||
typesv1 "github.com/harmony-development/shibshib/gen/harmonytypes/v1"
|
||||
profilev1 "github.com/harmony-development/shibshib/gen/profile/v1"
|
||||
)
|
||||
|
||||
type cachedProfile struct {
|
||||
data *profilev1.GetProfileResponse
|
||||
lastUpdated time.Time
|
||||
}
|
||||
|
||||
type Bharmony struct {
|
||||
*bridge.Config
|
||||
|
||||
c *shibshib.Client
|
||||
profileCache map[uint64]cachedProfile
|
||||
}
|
||||
|
||||
func uToStr(in uint64) string {
|
||||
return strconv.FormatUint(in, 10)
|
||||
}
|
||||
|
||||
func strToU(in string) (uint64, error) {
|
||||
return strconv.ParseUint(in, 10, 64)
|
||||
}
|
||||
|
||||
func New(cfg *bridge.Config) bridge.Bridger {
|
||||
b := &Bharmony{
|
||||
Config: cfg,
|
||||
profileCache: map[uint64]cachedProfile{},
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Bharmony) getProfile(u uint64) (*profilev1.GetProfileResponse, error) {
|
||||
if v, ok := b.profileCache[u]; ok && time.Since(v.lastUpdated) < time.Minute*10 {
|
||||
return v.data, nil
|
||||
}
|
||||
|
||||
resp, err := b.c.ProfileKit.GetProfile(&profilev1.GetProfileRequest{
|
||||
UserId: u,
|
||||
})
|
||||
if err != nil {
|
||||
if v, ok := b.profileCache[u]; ok {
|
||||
return v.data, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
b.profileCache[u] = cachedProfile{
|
||||
data: resp,
|
||||
lastUpdated: time.Now(),
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (b *Bharmony) avatarFor(m *chatv1.Message) string {
|
||||
if m.Overrides != nil {
|
||||
return m.Overrides.GetAvatar()
|
||||
}
|
||||
|
||||
profi, err := b.getProfile(m.AuthorId)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return b.c.TransformHMCURL(profi.Profile.GetUserAvatar())
|
||||
}
|
||||
|
||||
func (b *Bharmony) usernameFor(m *chatv1.Message) string {
|
||||
if m.Overrides != nil {
|
||||
return m.Overrides.GetUsername()
|
||||
}
|
||||
|
||||
profi, err := b.getProfile(m.AuthorId)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return profi.Profile.UserName
|
||||
}
|
||||
|
||||
func (b *Bharmony) toMessage(msg *shibshib.LocatedMessage) config.Message {
|
||||
message := config.Message{}
|
||||
message.Account = b.Account
|
||||
message.UserID = uToStr(msg.Message.AuthorId)
|
||||
message.Avatar = b.avatarFor(msg.Message)
|
||||
message.Username = b.usernameFor(msg.Message)
|
||||
message.Channel = uToStr(msg.ChannelID)
|
||||
message.ID = uToStr(msg.MessageId)
|
||||
|
||||
switch content := msg.Message.Content.Content.(type) {
|
||||
case *chatv1.Content_EmbedMessage:
|
||||
message.Text = "Embed"
|
||||
case *chatv1.Content_AttachmentMessage:
|
||||
var s strings.Builder
|
||||
for idx, attach := range content.AttachmentMessage.Files {
|
||||
s.WriteString(b.c.TransformHMCURL(attach.Id))
|
||||
if idx < len(content.AttachmentMessage.Files)-1 {
|
||||
s.WriteString(", ")
|
||||
}
|
||||
}
|
||||
message.Text = s.String()
|
||||
case *chatv1.Content_PhotoMessage:
|
||||
var s strings.Builder
|
||||
for idx, attach := range content.PhotoMessage.GetPhotos() {
|
||||
s.WriteString(attach.GetCaption().GetText())
|
||||
s.WriteString("\n")
|
||||
s.WriteString(b.c.TransformHMCURL(attach.GetHmc()))
|
||||
if idx < len(content.PhotoMessage.GetPhotos())-1 {
|
||||
s.WriteString("\n\n")
|
||||
}
|
||||
}
|
||||
message.Text = s.String()
|
||||
case *chatv1.Content_TextMessage:
|
||||
message.Text = content.TextMessage.Content.Text
|
||||
}
|
||||
|
||||
return message
|
||||
}
|
||||
|
||||
func (b *Bharmony) outputMessages() {
|
||||
for {
|
||||
msg := <-b.c.EventsStream()
|
||||
|
||||
if msg.Message.AuthorId == b.c.UserID {
|
||||
continue
|
||||
}
|
||||
|
||||
b.Remote <- b.toMessage(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bharmony) GetUint64(conf string) uint64 {
|
||||
num, err := strToU(b.GetString(conf))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return num
|
||||
}
|
||||
|
||||
func (b *Bharmony) Connect() (err error) {
|
||||
b.c, err = shibshib.NewClient(b.GetString("Homeserver"), b.GetString("Token"), b.GetUint64("UserID"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
b.c.SubscribeToGuild(b.GetUint64("Community"))
|
||||
|
||||
go b.outputMessages()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bharmony) send(msg config.Message) (id string, err error) {
|
||||
msgChan, err := strToU(msg.Channel)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
retID, err := b.c.ChatKit.SendMessage(&chatv1.SendMessageRequest{
|
||||
GuildId: b.GetUint64("Community"),
|
||||
ChannelId: msgChan,
|
||||
Content: &chatv1.Content{
|
||||
Content: &chatv1.Content_TextMessage{
|
||||
TextMessage: &chatv1.Content_TextContent{
|
||||
Content: &chatv1.FormattedText{
|
||||
Text: msg.Text,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Overrides: &chatv1.Overrides{
|
||||
Username: &msg.Username,
|
||||
Avatar: &msg.Avatar,
|
||||
Reason: &chatv1.Overrides_Bridge{Bridge: &typesv1.Empty{}},
|
||||
},
|
||||
InReplyTo: nil,
|
||||
EchoId: nil,
|
||||
Metadata: nil,
|
||||
})
|
||||
if err != nil {
|
||||
err = fmt.Errorf("send: error sending message: %w", err)
|
||||
log.Println(err.Error())
|
||||
}
|
||||
|
||||
return uToStr(retID.MessageId), err
|
||||
}
|
||||
|
||||
func (b *Bharmony) delete(msg config.Message) (id string, err error) {
|
||||
msgChan, err := strToU(msg.Channel)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
msgID, err := strToU(msg.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, err = b.c.ChatKit.DeleteMessage(&chatv1.DeleteMessageRequest{
|
||||
GuildId: b.GetUint64("Community"),
|
||||
ChannelId: msgChan,
|
||||
MessageId: msgID,
|
||||
})
|
||||
return "", err
|
||||
}
|
||||
|
||||
func (b *Bharmony) typing(msg config.Message) (id string, err error) {
|
||||
msgChan, err := strToU(msg.Channel)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, err = b.c.ChatKit.Typing(&chatv1.TypingRequest{
|
||||
GuildId: b.GetUint64("Community"),
|
||||
ChannelId: msgChan,
|
||||
})
|
||||
return "", err
|
||||
}
|
||||
|
||||
func (b *Bharmony) Send(msg config.Message) (id string, err error) {
|
||||
switch msg.Event {
|
||||
case "":
|
||||
return b.send(msg)
|
||||
case config.EventMsgDelete:
|
||||
return b.delete(msg)
|
||||
case config.EventUserTyping:
|
||||
return b.typing(msg)
|
||||
default:
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bharmony) JoinChannel(channel config.ChannelInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bharmony) Disconnect() error {
|
||||
return nil
|
||||
}
|
||||
@@ -5,10 +5,7 @@ import (
|
||||
"fmt"
|
||||
"image/png"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -82,9 +79,7 @@ func DownloadFileAuthRocket(url, token, userID string) (*[]byte, error) {
|
||||
// TODO: The current implementation has the inconvenient that it disregards
|
||||
// word boundaries when splitting but this is hard to solve without potentially
|
||||
// breaking formatting and other stylistic effects.
|
||||
func GetSubLines(message string, maxLineLength int) []string {
|
||||
const clippingMessage = " <clipped message>"
|
||||
|
||||
func GetSubLines(message string, maxLineLength int, clippingMessage string) []string {
|
||||
var lines []string
|
||||
for _, line := range strings.Split(strings.TrimSpace(message), "\n") {
|
||||
if maxLineLength == 0 || len([]byte(line)) <= maxLineLength {
|
||||
@@ -99,8 +94,8 @@ func GetSubLines(message string, maxLineLength int) []string {
|
||||
var splitStart int
|
||||
var startOfPreviousRune int
|
||||
for i := range line {
|
||||
if i-splitStart > maxLineLength-len([]byte(clippingMessage)) {
|
||||
lines = append(lines, line[splitStart:startOfPreviousRune]+clippingMessage)
|
||||
if i-splitStart > maxLineLength {
|
||||
lines = append(lines, line[splitStart:startOfPreviousRune])
|
||||
splitStart = startOfPreviousRune
|
||||
}
|
||||
startOfPreviousRune = i
|
||||
@@ -169,17 +164,23 @@ func HandleDownloadSize(logger *logrus.Entry, msg *config.Message, name string,
|
||||
|
||||
// HandleDownloadData adds the data for a remote file into a Matterbridge gateway message.
|
||||
func HandleDownloadData(logger *logrus.Entry, msg *config.Message, name, comment, url string, data *[]byte, general *config.Protocol) {
|
||||
HandleDownloadData2(logger, msg, name, "", comment, url, data, general)
|
||||
}
|
||||
|
||||
// HandleDownloadData adds the data for a remote file into a Matterbridge gateway message.
|
||||
func HandleDownloadData2(logger *logrus.Entry, msg *config.Message, name, id, comment, url string, data *[]byte, general *config.Protocol) {
|
||||
var avatar bool
|
||||
logger.Debugf("Download OK %#v %#v", name, len(*data))
|
||||
if msg.Event == config.EventAvatarDownload {
|
||||
avatar = true
|
||||
}
|
||||
msg.Extra["file"] = append(msg.Extra["file"], config.FileInfo{
|
||||
Name: name,
|
||||
Data: data,
|
||||
URL: url,
|
||||
Comment: comment,
|
||||
Avatar: avatar,
|
||||
Name: name,
|
||||
Data: data,
|
||||
URL: url,
|
||||
Comment: comment,
|
||||
Avatar: avatar,
|
||||
NativeID: id,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -193,8 +194,11 @@ func RemoveEmptyNewLines(msg string) string {
|
||||
|
||||
// ClipMessage trims a message to the specified length if it exceeds it and adds a warning
|
||||
// to the message in case it does so.
|
||||
func ClipMessage(text string, length int) string {
|
||||
const clippingMessage = " <clipped message>"
|
||||
func ClipMessage(text string, length int, clippingMessage string) string {
|
||||
if clippingMessage == "" {
|
||||
clippingMessage = " <clipped message>"
|
||||
}
|
||||
|
||||
if len(text) > length {
|
||||
text = text[:length-len(clippingMessage)]
|
||||
if r, size := utf8.DecodeLastRuneInString(text); r == utf8.RuneError {
|
||||
@@ -234,66 +238,3 @@ func ConvertWebPToPNG(data *[]byte) error {
|
||||
*data = w.Bytes()
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanConvertTgsToX Checks whether the external command necessary for ConvertTgsToX works.
|
||||
func CanConvertTgsToX() error {
|
||||
// We depend on the fact that `lottie_convert.py --help` has exit status 0.
|
||||
// Hyrum's Law predicted this, and Murphy's Law predicts that this will break eventually.
|
||||
// However, there is no alternative like `lottie_convert.py --is-properly-installed`
|
||||
cmd := exec.Command("lottie_convert.py", "--help")
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// ConvertTgsToWebP convert input data (which should be tgs format) to WebP format
|
||||
// This relies on an external command, which is ugly, but works.
|
||||
func ConvertTgsToX(data *[]byte, outputFormat string, logger *logrus.Entry) error {
|
||||
// lottie can't handle input from a pipe, so write to a temporary file:
|
||||
tmpInFile, err := ioutil.TempFile(os.TempDir(), "matterbridge-lottie-input-*.tgs")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpInFileName := tmpInFile.Name()
|
||||
defer func() {
|
||||
if removeErr := os.Remove(tmpInFileName); removeErr != nil {
|
||||
logger.Errorf("Could not delete temporary (input) file %s: %v", tmpInFileName, removeErr)
|
||||
}
|
||||
}()
|
||||
// lottie can handle writing to a pipe, but there is no way to do that platform-independently.
|
||||
// "/dev/stdout" won't work on Windows, and "-" upsets Cairo for some reason. So we need another file:
|
||||
tmpOutFile, err := ioutil.TempFile(os.TempDir(), "matterbridge-lottie-output-*.data")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpOutFileName := tmpOutFile.Name()
|
||||
defer func() {
|
||||
if removeErr := os.Remove(tmpOutFileName); removeErr != nil {
|
||||
logger.Errorf("Could not delete temporary (output) file %s: %v", tmpOutFileName, removeErr)
|
||||
}
|
||||
}()
|
||||
|
||||
if _, writeErr := tmpInFile.Write(*data); writeErr != nil {
|
||||
return writeErr
|
||||
}
|
||||
// Must close before calling lottie to avoid data races:
|
||||
if closeErr := tmpInFile.Close(); closeErr != nil {
|
||||
return closeErr
|
||||
}
|
||||
|
||||
// Call lottie to transform:
|
||||
cmd := exec.Command("lottie_convert.py", "--input-format", "lottie", "--output-format", outputFormat, tmpInFileName, tmpOutFileName)
|
||||
cmd.Stdout = nil
|
||||
cmd.Stderr = nil
|
||||
// NB: lottie writes progress into to stderr in all cases.
|
||||
_, stderr := cmd.Output()
|
||||
if stderr != nil {
|
||||
// 'stderr' already contains some parts of Stderr, because it was set to 'nil'.
|
||||
return stderr
|
||||
}
|
||||
dataContents, err := ioutil.ReadFile(tmpOutFileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*data = dataContents
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -10,98 +10,96 @@ import (
|
||||
|
||||
const testLineLength = 64
|
||||
|
||||
var (
|
||||
lineSplittingTestCases = map[string]struct {
|
||||
input string
|
||||
splitOutput []string
|
||||
nonSplitOutput []string
|
||||
}{
|
||||
"Short single-line message": {
|
||||
input: "short",
|
||||
splitOutput: []string{"short"},
|
||||
nonSplitOutput: []string{"short"},
|
||||
var lineSplittingTestCases = map[string]struct {
|
||||
input string
|
||||
splitOutput []string
|
||||
nonSplitOutput []string
|
||||
}{
|
||||
"Short single-line message": {
|
||||
input: "short",
|
||||
splitOutput: []string{"short"},
|
||||
nonSplitOutput: []string{"short"},
|
||||
},
|
||||
"Long single-line message": {
|
||||
input: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
splitOutput: []string{
|
||||
"Lorem ipsum dolor sit amet, consectetur adipis <clipped message>",
|
||||
"cing elit, sed do eiusmod tempor incididunt ut <clipped message>",
|
||||
" labore et dolore magna aliqua.",
|
||||
},
|
||||
"Long single-line message": {
|
||||
input: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
splitOutput: []string{
|
||||
"Lorem ipsum dolor sit amet, consectetur adipis <clipped message>",
|
||||
"cing elit, sed do eiusmod tempor incididunt ut <clipped message>",
|
||||
" labore et dolore magna aliqua.",
|
||||
},
|
||||
nonSplitOutput: []string{"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."},
|
||||
nonSplitOutput: []string{"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."},
|
||||
},
|
||||
"Short multi-line message": {
|
||||
input: "I\ncan't\nget\nno\nsatisfaction!",
|
||||
splitOutput: []string{
|
||||
"I",
|
||||
"can't",
|
||||
"get",
|
||||
"no",
|
||||
"satisfaction!",
|
||||
},
|
||||
"Short multi-line message": {
|
||||
input: "I\ncan't\nget\nno\nsatisfaction!",
|
||||
splitOutput: []string{
|
||||
"I",
|
||||
"can't",
|
||||
"get",
|
||||
"no",
|
||||
"satisfaction!",
|
||||
},
|
||||
nonSplitOutput: []string{
|
||||
"I",
|
||||
"can't",
|
||||
"get",
|
||||
"no",
|
||||
"satisfaction!",
|
||||
},
|
||||
nonSplitOutput: []string{
|
||||
"I",
|
||||
"can't",
|
||||
"get",
|
||||
"no",
|
||||
"satisfaction!",
|
||||
},
|
||||
"Long multi-line message": {
|
||||
input: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n" +
|
||||
"Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\n" +
|
||||
"Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\n" +
|
||||
"Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
|
||||
splitOutput: []string{
|
||||
"Lorem ipsum dolor sit amet, consectetur adipis <clipped message>",
|
||||
"cing elit, sed do eiusmod tempor incididunt ut <clipped message>",
|
||||
" labore et dolore magna aliqua.",
|
||||
"Ut enim ad minim veniam, quis nostrud exercita <clipped message>",
|
||||
"tion ullamco laboris nisi ut aliquip ex ea com <clipped message>",
|
||||
"modo consequat.",
|
||||
"Duis aute irure dolor in reprehenderit in volu <clipped message>",
|
||||
"ptate velit esse cillum dolore eu fugiat nulla <clipped message>",
|
||||
" pariatur.",
|
||||
"Excepteur sint occaecat cupidatat non proident <clipped message>",
|
||||
", sunt in culpa qui officia deserunt mollit an <clipped message>",
|
||||
"im id est laborum.",
|
||||
},
|
||||
nonSplitOutput: []string{
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
"Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
|
||||
"Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
|
||||
"Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
|
||||
},
|
||||
},
|
||||
"Long multi-line message": {
|
||||
input: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n" +
|
||||
"Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\n" +
|
||||
"Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\n" +
|
||||
"Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
|
||||
splitOutput: []string{
|
||||
"Lorem ipsum dolor sit amet, consectetur adipis <clipped message>",
|
||||
"cing elit, sed do eiusmod tempor incididunt ut <clipped message>",
|
||||
" labore et dolore magna aliqua.",
|
||||
"Ut enim ad minim veniam, quis nostrud exercita <clipped message>",
|
||||
"tion ullamco laboris nisi ut aliquip ex ea com <clipped message>",
|
||||
"modo consequat.",
|
||||
"Duis aute irure dolor in reprehenderit in volu <clipped message>",
|
||||
"ptate velit esse cillum dolore eu fugiat nulla <clipped message>",
|
||||
" pariatur.",
|
||||
"Excepteur sint occaecat cupidatat non proident <clipped message>",
|
||||
", sunt in culpa qui officia deserunt mollit an <clipped message>",
|
||||
"im id est laborum.",
|
||||
},
|
||||
"Message ending with new-line.": {
|
||||
input: "Newline ending\n",
|
||||
splitOutput: []string{"Newline ending"},
|
||||
nonSplitOutput: []string{"Newline ending"},
|
||||
nonSplitOutput: []string{
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
"Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
|
||||
"Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
|
||||
"Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
|
||||
},
|
||||
"Long message containing UTF-8 multi-byte runes": {
|
||||
input: "不布人個我此而及單石業喜資富下我河下日沒一我臺空達的常景便物沒為……子大我別名解成?生賣的全直黑,我自我結毛分洲了世當,是政福那是東;斯說",
|
||||
splitOutput: []string{
|
||||
"不布人個我此而及單石業喜資富下 <clipped message>",
|
||||
"我河下日沒一我臺空達的常景便物 <clipped message>",
|
||||
"沒為……子大我別名解成?生賣的 <clipped message>",
|
||||
"全直黑,我自我結毛分洲了世當, <clipped message>",
|
||||
"是政福那是東;斯說",
|
||||
},
|
||||
nonSplitOutput: []string{"不布人個我此而及單石業喜資富下我河下日沒一我臺空達的常景便物沒為……子大我別名解成?生賣的全直黑,我自我結毛分洲了世當,是政福那是東;斯說"},
|
||||
},
|
||||
"Message ending with new-line.": {
|
||||
input: "Newline ending\n",
|
||||
splitOutput: []string{"Newline ending"},
|
||||
nonSplitOutput: []string{"Newline ending"},
|
||||
},
|
||||
"Long message containing UTF-8 multi-byte runes": {
|
||||
input: "不布人個我此而及單石業喜資富下我河下日沒一我臺空達的常景便物沒為……子大我別名解成?生賣的全直黑,我自我結毛分洲了世當,是政福那是東;斯說",
|
||||
splitOutput: []string{
|
||||
"不布人個我此而及單石業喜資富下 <clipped message>",
|
||||
"我河下日沒一我臺空達的常景便物 <clipped message>",
|
||||
"沒為……子大我別名解成?生賣的 <clipped message>",
|
||||
"全直黑,我自我結毛分洲了世當, <clipped message>",
|
||||
"是政福那是東;斯說",
|
||||
},
|
||||
}
|
||||
)
|
||||
nonSplitOutput: []string{"不布人個我此而及單石業喜資富下我河下日沒一我臺空達的常景便物沒為……子大我別名解成?生賣的全直黑,我自我結毛分洲了世當,是政福那是東;斯說"},
|
||||
},
|
||||
}
|
||||
|
||||
func TestGetSubLines(t *testing.T) {
|
||||
for testname, testcase := range lineSplittingTestCases {
|
||||
splitLines := GetSubLines(testcase.input, testLineLength)
|
||||
splitLines := GetSubLines(testcase.input, testLineLength, "")
|
||||
assert.Equalf(t, testcase.splitOutput, splitLines, "'%s' testcase should give expected lines with splitting.", testname)
|
||||
for _, splitLine := range splitLines {
|
||||
byteLength := len([]byte(splitLine))
|
||||
assert.True(t, byteLength <= testLineLength, "Splitted line '%s' of testcase '%s' should not exceed the maximum byte-length (%d vs. %d).", splitLine, testcase, byteLength, testLineLength)
|
||||
}
|
||||
|
||||
nonSplitLines := GetSubLines(testcase.input, 0)
|
||||
nonSplitLines := GetSubLines(testcase.input, 0, "")
|
||||
assert.Equalf(t, testcase.nonSplitOutput, nonSplitLines, "'%s' testcase should give expected lines without splitting.", testname)
|
||||
}
|
||||
}
|
||||
@@ -110,16 +108,19 @@ func TestConvertWebPToPNG(t *testing.T) {
|
||||
if os.Getenv("LOCAL_TEST") == "" {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
input, err := ioutil.ReadFile("test.webp")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
d := &input
|
||||
err = ConvertWebPToPNG(d)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
err = ioutil.WriteFile("test.png", *d, 0644)
|
||||
|
||||
err = ioutil.WriteFile("test.png", *d, 0o644) // nolint:gosec
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
36
bridge/helper/libtgsconverter.go
Normal file
@@ -0,0 +1,36 @@
|
||||
//go:build cgo
|
||||
// +build cgo
|
||||
|
||||
package helper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Benau/tgsconverter/libtgsconverter"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func CanConvertTgsToX() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConvertTgsToX convert input data (which should be tgs format) to any format supported by libtgsconverter
|
||||
func ConvertTgsToX(data *[]byte, outputFormat string, logger *logrus.Entry) error {
|
||||
options := libtgsconverter.NewConverterOptions()
|
||||
options.SetExtension(outputFormat)
|
||||
blob, err := libtgsconverter.ImportFromData(*data, options)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to run libtgsconverter.ImportFromData: %s", err.Error())
|
||||
}
|
||||
|
||||
*data = blob
|
||||
return nil
|
||||
}
|
||||
|
||||
func SupportsFormat(format string) bool {
|
||||
return libtgsconverter.SupportsExtension(format)
|
||||
}
|
||||
|
||||
func LottieBackend() string {
|
||||
return "libtgsconverter"
|
||||
}
|
||||
89
bridge/helper/lottie_convert.go
Normal file
@@ -0,0 +1,89 @@
|
||||
// +build !cgo
|
||||
|
||||
package helper
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// CanConvertTgsToX Checks whether the external command necessary for ConvertTgsToX works.
|
||||
func CanConvertTgsToX() error {
|
||||
// We depend on the fact that `lottie_convert.py --help` has exit status 0.
|
||||
// Hyrum's Law predicted this, and Murphy's Law predicts that this will break eventually.
|
||||
// However, there is no alternative like `lottie_convert.py --is-properly-installed`
|
||||
cmd := exec.Command("lottie_convert.py", "--help")
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// ConvertTgsToWebP convert input data (which should be tgs format) to WebP format
|
||||
// This relies on an external command, which is ugly, but works.
|
||||
func ConvertTgsToX(data *[]byte, outputFormat string, logger *logrus.Entry) error {
|
||||
// lottie can't handle input from a pipe, so write to a temporary file:
|
||||
tmpInFile, err := ioutil.TempFile(os.TempDir(), "matterbridge-lottie-input-*.tgs")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpInFileName := tmpInFile.Name()
|
||||
defer func() {
|
||||
if removeErr := os.Remove(tmpInFileName); removeErr != nil {
|
||||
logger.Errorf("Could not delete temporary (input) file %s: %v", tmpInFileName, removeErr)
|
||||
}
|
||||
}()
|
||||
// lottie can handle writing to a pipe, but there is no way to do that platform-independently.
|
||||
// "/dev/stdout" won't work on Windows, and "-" upsets Cairo for some reason. So we need another file:
|
||||
tmpOutFile, err := ioutil.TempFile(os.TempDir(), "matterbridge-lottie-output-*.data")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpOutFileName := tmpOutFile.Name()
|
||||
defer func() {
|
||||
if removeErr := os.Remove(tmpOutFileName); removeErr != nil {
|
||||
logger.Errorf("Could not delete temporary (output) file %s: %v", tmpOutFileName, removeErr)
|
||||
}
|
||||
}()
|
||||
|
||||
if _, writeErr := tmpInFile.Write(*data); writeErr != nil {
|
||||
return writeErr
|
||||
}
|
||||
// Must close before calling lottie to avoid data races:
|
||||
if closeErr := tmpInFile.Close(); closeErr != nil {
|
||||
return closeErr
|
||||
}
|
||||
|
||||
// Call lottie to transform:
|
||||
cmd := exec.Command("lottie_convert.py", "--input-format", "lottie", "--output-format", outputFormat, tmpInFileName, tmpOutFileName)
|
||||
cmd.Stdout = nil
|
||||
cmd.Stderr = nil
|
||||
// NB: lottie writes progress into to stderr in all cases.
|
||||
_, stderr := cmd.Output()
|
||||
if stderr != nil {
|
||||
// 'stderr' already contains some parts of Stderr, because it was set to 'nil'.
|
||||
return stderr
|
||||
}
|
||||
dataContents, err := ioutil.ReadFile(tmpOutFileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*data = dataContents
|
||||
return nil
|
||||
}
|
||||
|
||||
func SupportsFormat(format string) bool {
|
||||
switch format {
|
||||
case "png":
|
||||
fallthrough
|
||||
case "webp":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func LottieBackend() string {
|
||||
return "lottie_convert.py"
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package birc
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"io/ioutil"
|
||||
@@ -24,7 +25,7 @@ import (
|
||||
|
||||
type Birc struct {
|
||||
i *girc.Client
|
||||
Nick string
|
||||
Nick, MessageClipped string
|
||||
names map[string][]string
|
||||
connected chan error
|
||||
Local chan config.Message // local queue for flood control
|
||||
@@ -72,6 +73,10 @@ func (b *Birc) Command(msg *config.Message) string {
|
||||
}
|
||||
|
||||
func (b *Birc) Connect() error {
|
||||
if b.GetBool("UseSASL") && b.GetString("TLSClientCertificate") != "" {
|
||||
return errors.New("you can't enable SASL and TLSClientCertificate at the same time")
|
||||
}
|
||||
|
||||
b.Local = make(chan config.Message, b.MessageQueue+10)
|
||||
b.Log.Infof("Connecting %s", b.GetString("Server"))
|
||||
|
||||
@@ -167,10 +172,11 @@ func (b *Birc) Send(msg config.Message) (string, error) {
|
||||
}
|
||||
|
||||
if b.GetBool("MessageSplit") {
|
||||
msgLines = helper.GetSubLines(msg.Text, b.MessageLength)
|
||||
msgLines = helper.GetSubLines(msg.Text, b.MessageLength, "")
|
||||
} else {
|
||||
msgLines = helper.GetSubLines(msg.Text, 0)
|
||||
msgLines = []string{helper.GetSubLines(msg.Text, b.MessageLength, "")[0] + b.getMessageClipped()}
|
||||
}
|
||||
|
||||
for i := range msgLines {
|
||||
if len(b.Local) >= b.MessageQueue {
|
||||
b.Log.Debugf("flooding, dropping message (queue at %d)", len(b.Local))
|
||||
@@ -271,8 +277,11 @@ func (b *Birc) getClient() (*girc.Client, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user := b.GetString("UserName")
|
||||
if user == "" {
|
||||
user = b.GetString("Nick")
|
||||
}
|
||||
// fix strict user handling of girc
|
||||
user := b.GetString("Nick")
|
||||
for !girc.IsValidUser(user) {
|
||||
if len(user) == 1 || len(user) == 0 {
|
||||
user = "matterbridge"
|
||||
@@ -280,6 +289,10 @@ func (b *Birc) getClient() (*girc.Client, error) {
|
||||
}
|
||||
user = user[1:]
|
||||
}
|
||||
realName := b.GetString("RealName")
|
||||
if realName == "" {
|
||||
realName = b.GetString("Nick")
|
||||
}
|
||||
|
||||
debug := ioutil.Discard
|
||||
if b.GetInt("DebugLevel") == 2 {
|
||||
@@ -293,15 +306,21 @@ func (b *Birc) getClient() (*girc.Client, error) {
|
||||
|
||||
b.Log.Debugf("setting pingdelay to %s", pingDelay)
|
||||
|
||||
tlsConfig, err := b.getTLSConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
i := girc.New(girc.Config{
|
||||
Server: server,
|
||||
ServerPass: b.GetString("Password"),
|
||||
Port: port,
|
||||
Nick: b.GetString("Nick"),
|
||||
User: user,
|
||||
Name: b.GetString("Nick"),
|
||||
Name: realName,
|
||||
SSL: b.GetBool("UseTLS"),
|
||||
TLSConfig: &tls.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"), ServerName: server}, //nolint:gosec
|
||||
Bind: b.GetString("Bind"),
|
||||
TLSConfig: tlsConfig,
|
||||
PingDelay: pingDelay,
|
||||
// skip gIRC internal rate limiting, since we have our own throttling
|
||||
AllowFlood: true,
|
||||
@@ -316,12 +335,16 @@ func (b *Birc) endNames(client *girc.Client, event girc.Event) {
|
||||
sort.Strings(b.names[channel])
|
||||
maxNamesPerPost := (300 / b.nicksPerRow()) * b.nicksPerRow()
|
||||
for len(b.names[channel]) > maxNamesPerPost {
|
||||
b.Remote <- config.Message{Username: b.Nick, Text: b.formatnicks(b.names[channel][0:maxNamesPerPost]),
|
||||
Channel: channel, Account: b.Account}
|
||||
b.Remote <- config.Message{
|
||||
Username: b.Nick, Text: b.formatnicks(b.names[channel][0:maxNamesPerPost]),
|
||||
Channel: channel, Account: b.Account,
|
||||
}
|
||||
b.names[channel] = b.names[channel][maxNamesPerPost:]
|
||||
}
|
||||
b.Remote <- config.Message{Username: b.Nick, Text: b.formatnicks(b.names[channel]),
|
||||
Channel: channel, Account: b.Account}
|
||||
b.Remote <- config.Message{
|
||||
Username: b.Nick, Text: b.formatnicks(b.names[channel]),
|
||||
Channel: channel, Account: b.Account,
|
||||
}
|
||||
b.names[channel] = nil
|
||||
b.i.Handlers.Clear(girc.RPL_NAMREPLY)
|
||||
b.i.Handlers.Clear(girc.RPL_ENDOFNAMES)
|
||||
@@ -369,3 +392,31 @@ func (b *Birc) storeNames(client *girc.Client, event girc.Event) {
|
||||
func (b *Birc) formatnicks(nicks []string) string {
|
||||
return strings.Join(nicks, ", ") + " currently on IRC"
|
||||
}
|
||||
|
||||
func (b *Birc) getTLSConfig() (*tls.Config, error) {
|
||||
server, _, _ := net.SplitHostPort(b.GetString("server"))
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: b.GetBool("skiptlsverify"), //nolint:gosec
|
||||
ServerName: server,
|
||||
}
|
||||
|
||||
if filename := b.GetString("TLSClientCertificate"); filename != "" {
|
||||
cert, err := tls.LoadX509KeyPair(filename, filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tlsConfig.Certificates = []tls.Certificate{cert}
|
||||
}
|
||||
|
||||
return tlsConfig, nil
|
||||
}
|
||||
|
||||
func (b *Birc) getMessageClipped() string {
|
||||
if b.GetString("MessageClipped") == "" {
|
||||
return " <clipped message>"
|
||||
}
|
||||
|
||||
return " " + b.GetString("MessageClipped")
|
||||
}
|
||||
|
||||
@@ -48,8 +48,10 @@ type matrixUsername struct {
|
||||
|
||||
// SubTextMessage represents the new content of the message in edit messages.
|
||||
type SubTextMessage struct {
|
||||
MsgType string `json:"msgtype"`
|
||||
Body string `json:"body"`
|
||||
MsgType string `json:"msgtype"`
|
||||
Body string `json:"body"`
|
||||
FormattedBody string `json:"formatted_body,omitempty"`
|
||||
Format string `json:"format,omitempty"`
|
||||
}
|
||||
|
||||
// MessageRelation explains how the current message relates to a previous message.
|
||||
@@ -65,6 +67,19 @@ type EditedMessage struct {
|
||||
matrix.TextMessage
|
||||
}
|
||||
|
||||
type InReplyToRelationContent struct {
|
||||
EventID string `json:"event_id"`
|
||||
}
|
||||
|
||||
type InReplyToRelation struct {
|
||||
InReplyTo InReplyToRelationContent `json:"m.in_reply_to"`
|
||||
}
|
||||
|
||||
type ReplyMessage struct {
|
||||
RelatedTo InReplyToRelation `json:"m.relates_to"`
|
||||
matrix.TextMessage
|
||||
}
|
||||
|
||||
func New(cfg *bridge.Config) bridge.Bridger {
|
||||
b := &Bmatrix{Config: cfg}
|
||||
b.RoomMap = make(map[string]string)
|
||||
@@ -75,22 +90,33 @@ func New(cfg *bridge.Config) bridge.Bridger {
|
||||
func (b *Bmatrix) Connect() error {
|
||||
var err error
|
||||
b.Log.Infof("Connecting %s", b.GetString("Server"))
|
||||
b.mc, err = matrix.NewClient(b.GetString("Server"), "", "")
|
||||
if err != nil {
|
||||
return err
|
||||
if b.GetString("MxID") != "" && b.GetString("Token") != "" {
|
||||
b.mc, err = matrix.NewClient(
|
||||
b.GetString("Server"), b.GetString("MxID"), b.GetString("Token"),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.UserID = b.GetString("MxID")
|
||||
b.Log.Info("Using existing Matrix credentials")
|
||||
} else {
|
||||
b.mc, err = matrix.NewClient(b.GetString("Server"), "", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := b.mc.Login(&matrix.ReqLogin{
|
||||
Type: "m.login.password",
|
||||
User: b.GetString("Login"),
|
||||
Password: b.GetString("Password"),
|
||||
Identifier: matrix.NewUserIdentifier(b.GetString("Login")),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.mc.SetCredentials(resp.UserID, resp.AccessToken)
|
||||
b.UserID = resp.UserID
|
||||
b.Log.Info("Connection succeeded")
|
||||
}
|
||||
resp, err := b.mc.Login(&matrix.ReqLogin{
|
||||
Type: "m.login.password",
|
||||
User: b.GetString("Login"),
|
||||
Password: b.GetString("Password"),
|
||||
Identifier: matrix.NewUserIdentifier(b.GetString("Login")),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.mc.SetCredentials(resp.UserID, resp.AccessToken)
|
||||
b.UserID = resp.UserID
|
||||
b.Log.Info("Connection succeeded")
|
||||
go b.handlematrix()
|
||||
return nil
|
||||
}
|
||||
@@ -127,7 +153,13 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
|
||||
m := matrix.TextMessage{
|
||||
MsgType: "m.emote",
|
||||
Body: username.plain + msg.Text,
|
||||
FormattedBody: username.formatted + msg.Text,
|
||||
FormattedBody: username.formatted + helper.ParseMarkdown(msg.Text),
|
||||
Format: "org.matrix.custom.html",
|
||||
}
|
||||
|
||||
if b.GetBool("HTMLDisable") {
|
||||
m.Format = ""
|
||||
m.FormattedBody = ""
|
||||
}
|
||||
|
||||
msgID := ""
|
||||
@@ -190,20 +222,29 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
|
||||
|
||||
// Edit message if we have an ID
|
||||
if msg.ID != "" {
|
||||
rmsg := EditedMessage{TextMessage: matrix.TextMessage{
|
||||
Body: username.plain + msg.Text,
|
||||
MsgType: "m.text",
|
||||
}}
|
||||
if b.GetBool("HTMLDisable") {
|
||||
rmsg.TextMessage.FormattedBody = username.formatted + "* " + msg.Text
|
||||
} else {
|
||||
rmsg.Format = "org.matrix.custom.html"
|
||||
rmsg.TextMessage.FormattedBody = username.formatted + "* " + helper.ParseMarkdown(msg.Text)
|
||||
rmsg := EditedMessage{
|
||||
TextMessage: matrix.TextMessage{
|
||||
Body: username.plain + msg.Text,
|
||||
MsgType: "m.text",
|
||||
Format: "org.matrix.custom.html",
|
||||
FormattedBody: username.formatted + helper.ParseMarkdown(msg.Text),
|
||||
},
|
||||
}
|
||||
|
||||
rmsg.NewContent = SubTextMessage{
|
||||
Body: rmsg.TextMessage.Body,
|
||||
MsgType: "m.text",
|
||||
Body: rmsg.TextMessage.Body,
|
||||
FormattedBody: rmsg.TextMessage.FormattedBody,
|
||||
Format: rmsg.TextMessage.Format,
|
||||
MsgType: "m.text",
|
||||
}
|
||||
|
||||
if b.GetBool("HTMLDisable") {
|
||||
rmsg.TextMessage.Format = ""
|
||||
rmsg.TextMessage.FormattedBody = ""
|
||||
rmsg.NewContent.Format = ""
|
||||
rmsg.NewContent.FormattedBody = ""
|
||||
}
|
||||
|
||||
rmsg.RelatedTo = MessageRelation{
|
||||
EventID: msg.ID,
|
||||
Type: "m.replace",
|
||||
@@ -227,6 +268,50 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
|
||||
MsgType: "m.notice",
|
||||
Body: username.plain + msg.Text,
|
||||
FormattedBody: username.formatted + msg.Text,
|
||||
Format: "org.matrix.custom.html",
|
||||
}
|
||||
|
||||
if b.GetBool("HTMLDisable") {
|
||||
m.Format = ""
|
||||
m.FormattedBody = ""
|
||||
}
|
||||
|
||||
var (
|
||||
resp *matrix.RespSendEvent
|
||||
err error
|
||||
)
|
||||
|
||||
err = b.retry(func() error {
|
||||
resp, err = b.mc.SendMessageEvent(channel, "m.room.message", m)
|
||||
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return resp.EventID, err
|
||||
}
|
||||
|
||||
if msg.ParentValid() {
|
||||
m := ReplyMessage{
|
||||
TextMessage: matrix.TextMessage{
|
||||
MsgType: "m.text",
|
||||
Body: username.plain + msg.Text,
|
||||
FormattedBody: username.formatted + helper.ParseMarkdown(msg.Text),
|
||||
Format: "org.matrix.custom.html",
|
||||
},
|
||||
}
|
||||
|
||||
if b.GetBool("HTMLDisable") {
|
||||
m.TextMessage.Format = ""
|
||||
m.TextMessage.FormattedBody = ""
|
||||
}
|
||||
|
||||
m.RelatedTo = InReplyToRelation{
|
||||
InReplyTo: InReplyToRelationContent{
|
||||
EventID: msg.ParentID,
|
||||
},
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -290,6 +375,9 @@ func (b *Bmatrix) handlematrix() {
|
||||
syncer.OnEventType("m.room.member", b.handleMemberChange)
|
||||
go func() {
|
||||
for {
|
||||
if b == nil {
|
||||
return
|
||||
}
|
||||
if err := b.mc.Sync(); err != nil {
|
||||
b.Log.Println("Sync() returned ", err)
|
||||
}
|
||||
@@ -327,6 +415,35 @@ func (b *Bmatrix) handleEdit(ev *matrix.Event, rmsg config.Message) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *Bmatrix) handleReply(ev *matrix.Event, rmsg config.Message) bool {
|
||||
relationInterface, present := ev.Content["m.relates_to"]
|
||||
if !present {
|
||||
return false
|
||||
}
|
||||
|
||||
var relation InReplyToRelation
|
||||
if err := interface2Struct(relationInterface, &relation); err != nil {
|
||||
// probably fine
|
||||
return false
|
||||
}
|
||||
|
||||
body := rmsg.Text
|
||||
for strings.HasPrefix(body, "> ") {
|
||||
lineIdx := strings.IndexRune(body, '\n')
|
||||
if lineIdx == -1 {
|
||||
body = ""
|
||||
} else {
|
||||
body = body[(lineIdx + 1):]
|
||||
}
|
||||
}
|
||||
|
||||
rmsg.Text = body
|
||||
rmsg.ParentID = relation.InReplyTo.EventID
|
||||
b.Remote <- rmsg
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *Bmatrix) handleMemberChange(ev *matrix.Event) {
|
||||
// Update the displayname on join messages, according to https://matrix.org/docs/spec/client_server/r0.6.1#events-on-change-of-profile-information
|
||||
if ev.Content["membership"] == "join" {
|
||||
@@ -357,13 +474,6 @@ func (b *Bmatrix) handleEvent(ev *matrix.Event) {
|
||||
Avatar: b.getAvatarURL(ev.Sender),
|
||||
}
|
||||
|
||||
// Text must be a string
|
||||
if rmsg.Text, ok = ev.Content["body"].(string); !ok {
|
||||
b.Log.Errorf("Content[body] is not a string: %T\n%#v",
|
||||
ev.Content["body"], ev.Content)
|
||||
return
|
||||
}
|
||||
|
||||
// Remove homeserver suffix if configured
|
||||
if b.GetBool("NoHomeServerSuffix") {
|
||||
re := regexp.MustCompile("(.*?):.*")
|
||||
@@ -379,6 +489,13 @@ func (b *Bmatrix) handleEvent(ev *matrix.Event) {
|
||||
return
|
||||
}
|
||||
|
||||
// Text must be a string
|
||||
if rmsg.Text, ok = ev.Content["body"].(string); !ok {
|
||||
b.Log.Errorf("Content[body] is not a string: %T\n%#v",
|
||||
ev.Content["body"], ev.Content)
|
||||
return
|
||||
}
|
||||
|
||||
// Do we have a /me action
|
||||
if ev.Content["msgtype"].(string) == "m.emote" {
|
||||
rmsg.Event = config.EventUserAction
|
||||
@@ -389,6 +506,11 @@ func (b *Bmatrix) handleEvent(ev *matrix.Event) {
|
||||
return
|
||||
}
|
||||
|
||||
// Is it a reply?
|
||||
if b.handleReply(ev, rmsg) {
|
||||
return
|
||||
}
|
||||
|
||||
// Do we have attachments
|
||||
if b.containsAttachment(ev.Content) {
|
||||
err := b.handleDownloadFile(&rmsg, ev.Content)
|
||||
|
||||
@@ -4,7 +4,9 @@ import (
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
"github.com/42wim/matterbridge/bridge/helper"
|
||||
"github.com/42wim/matterbridge/matterclient"
|
||||
matterclient6 "github.com/matterbridge/matterclient"
|
||||
"github.com/mattermost/mattermost-server/v5/model"
|
||||
model6 "github.com/mattermost/mattermost-server/v6/model"
|
||||
)
|
||||
|
||||
// handleDownloadAvatar downloads the avatar of userid from channel
|
||||
@@ -21,12 +23,26 @@ func (b *Bmattermost) handleDownloadAvatar(userid string, channel string) {
|
||||
Extra: make(map[string][]interface{}),
|
||||
}
|
||||
if _, ok := b.avatarMap[userid]; !ok {
|
||||
data, resp := b.mc.Client.GetProfileImage(userid, "")
|
||||
if resp.Error != nil {
|
||||
b.Log.Errorf("ProfileImage download failed for %#v %s", userid, resp.Error)
|
||||
return
|
||||
var (
|
||||
data []byte
|
||||
err error
|
||||
resp *model.Response
|
||||
)
|
||||
if b.mc6 != nil {
|
||||
data, _, err = b.mc6.Client.GetProfileImage(userid, "")
|
||||
if err != nil {
|
||||
b.Log.Errorf("ProfileImage download failed for %#v %s", userid, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
data, resp = b.mc.Client.GetProfileImage(userid, "")
|
||||
if resp.Error != nil {
|
||||
b.Log.Errorf("ProfileImage download failed for %#v %s", userid, resp.Error)
|
||||
return
|
||||
}
|
||||
}
|
||||
err := helper.HandleDownloadSize(b.Log, &rmsg, userid+".png", int64(len(data)), b.General)
|
||||
|
||||
err = helper.HandleDownloadSize(b.Log, &rmsg, userid+".png", int64(len(data)), b.General)
|
||||
if err != nil {
|
||||
b.Log.Error(err)
|
||||
return
|
||||
@@ -38,6 +54,10 @@ func (b *Bmattermost) handleDownloadAvatar(userid string, channel string) {
|
||||
|
||||
// handleDownloadFile handles file download
|
||||
func (b *Bmattermost) handleDownloadFile(rmsg *config.Message, id string) error {
|
||||
if b.mc6 != nil {
|
||||
return b.handleDownloadFile6(rmsg, id)
|
||||
}
|
||||
|
||||
url, _ := b.mc.Client.GetFileLink(id)
|
||||
finfo, resp := b.mc.Client.GetFileInfo(id)
|
||||
if resp.Error != nil {
|
||||
@@ -55,6 +75,25 @@ func (b *Bmattermost) handleDownloadFile(rmsg *config.Message, id string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// nolint:wrapcheck
|
||||
func (b *Bmattermost) handleDownloadFile6(rmsg *config.Message, id string) error {
|
||||
url, _, _ := b.mc6.Client.GetFileLink(id)
|
||||
finfo, _, err := b.mc6.Client.GetFileInfo(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = helper.HandleDownloadSize(b.Log, rmsg, finfo.Name, finfo.Size, b.General)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, _, err := b.mc6.Client.DownloadFile(id, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
helper.HandleDownloadData(b.Log, rmsg, finfo.Name, rmsg.Text, url, &data, b.General)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bmattermost) handleMatter() {
|
||||
messages := make(chan *config.Message)
|
||||
if b.GetString("WebhookBindAddress") != "" {
|
||||
@@ -87,6 +126,12 @@ func (b *Bmattermost) handleMatter() {
|
||||
}
|
||||
|
||||
func (b *Bmattermost) handleMatterClient(messages chan *config.Message) {
|
||||
if b.mc6 != nil {
|
||||
b.Log.Debug("starting matterclient6")
|
||||
b.handleMatterClient6(messages)
|
||||
return
|
||||
}
|
||||
|
||||
for message := range b.mc.MessageChan {
|
||||
b.Log.Debugf("%#v", message.Raw.Data)
|
||||
|
||||
@@ -95,9 +140,14 @@ func (b *Bmattermost) handleMatterClient(messages chan *config.Message) {
|
||||
continue
|
||||
}
|
||||
|
||||
channelName := b.getChannelName(message.Post.ChannelId)
|
||||
if channelName == "" {
|
||||
channelName = message.Channel
|
||||
}
|
||||
|
||||
// only download avatars if we have a place to upload them (configured mediaserver)
|
||||
if b.General.MediaServerUpload != "" || b.General.MediaDownloadPath != "" {
|
||||
b.handleDownloadAvatar(message.UserID, message.Channel)
|
||||
b.handleDownloadAvatar(message.UserID, channelName)
|
||||
}
|
||||
|
||||
b.Log.Debugf("== Receiving event %#v", message)
|
||||
@@ -105,7 +155,7 @@ func (b *Bmattermost) handleMatterClient(messages chan *config.Message) {
|
||||
rmsg := &config.Message{
|
||||
Username: message.Username,
|
||||
UserID: message.UserID,
|
||||
Channel: message.Channel,
|
||||
Channel: channelName,
|
||||
Text: message.Text,
|
||||
ID: message.Post.Id,
|
||||
ParentID: message.Post.RootId, // ParentID is obsolete with mattermost
|
||||
@@ -132,8 +182,72 @@ func (b *Bmattermost) handleMatterClient(messages chan *config.Message) {
|
||||
}
|
||||
|
||||
// Use nickname instead of username if defined
|
||||
if nick := b.mc.GetNickName(rmsg.UserID); nick != "" {
|
||||
rmsg.Username = nick
|
||||
if !b.GetBool("useusername") {
|
||||
if nick := b.mc.GetNickName(rmsg.UserID); nick != "" {
|
||||
rmsg.Username = nick
|
||||
}
|
||||
}
|
||||
|
||||
messages <- rmsg
|
||||
}
|
||||
}
|
||||
|
||||
// nolint:cyclop
|
||||
func (b *Bmattermost) handleMatterClient6(messages chan *config.Message) {
|
||||
for message := range b.mc6.MessageChan {
|
||||
b.Log.Debugf("%#v %#v", message.Raw.GetData(), message.Raw.EventType())
|
||||
|
||||
if b.skipMessage6(message) {
|
||||
b.Log.Debugf("Skipped message: %#v", message)
|
||||
continue
|
||||
}
|
||||
|
||||
channelName := b.getChannelName(message.Post.ChannelId)
|
||||
if channelName == "" {
|
||||
channelName = message.Channel
|
||||
}
|
||||
|
||||
// only download avatars if we have a place to upload them (configured mediaserver)
|
||||
if b.General.MediaServerUpload != "" || b.General.MediaDownloadPath != "" {
|
||||
b.handleDownloadAvatar(message.UserID, channelName)
|
||||
}
|
||||
|
||||
b.Log.Debugf("== Receiving event %#v", message)
|
||||
|
||||
rmsg := &config.Message{
|
||||
Username: message.Username,
|
||||
UserID: message.UserID,
|
||||
Channel: channelName,
|
||||
Text: message.Text,
|
||||
ID: message.Post.Id,
|
||||
ParentID: message.Post.RootId, // ParentID is obsolete with mattermost
|
||||
Extra: make(map[string][]interface{}),
|
||||
}
|
||||
|
||||
// handle mattermost post properties (override username and attachments)
|
||||
b.handleProps6(rmsg, message)
|
||||
|
||||
// create a text for bridges that don't support native editing
|
||||
if message.Raw.EventType() == model6.WebsocketEventPostEdited && !b.GetBool("EditDisable") {
|
||||
rmsg.Text = message.Text + b.GetString("EditSuffix")
|
||||
}
|
||||
|
||||
if message.Raw.EventType() == model6.WebsocketEventPostDeleted {
|
||||
rmsg.Event = config.EventMsgDelete
|
||||
}
|
||||
|
||||
for _, id := range message.Post.FileIds {
|
||||
err := b.handleDownloadFile(rmsg, id)
|
||||
if err != nil {
|
||||
b.Log.Errorf("download failed: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Use nickname instead of username if defined
|
||||
if !b.GetBool("useusername") {
|
||||
if nick := b.mc6.GetNickName(rmsg.UserID); nick != "" {
|
||||
rmsg.Username = nick
|
||||
}
|
||||
}
|
||||
|
||||
messages <- rmsg
|
||||
@@ -144,6 +258,7 @@ func (b *Bmattermost) handleMatterHook(messages chan *config.Message) {
|
||||
for {
|
||||
message := b.mh.Receive()
|
||||
b.Log.Debugf("Receiving from matterhook %#v", message)
|
||||
|
||||
messages <- &config.Message{
|
||||
UserID: message.UserID,
|
||||
Username: message.UserName,
|
||||
@@ -155,9 +270,13 @@ func (b *Bmattermost) handleMatterHook(messages chan *config.Message) {
|
||||
|
||||
// handleUploadFile handles native upload of files
|
||||
func (b *Bmattermost) handleUploadFile(msg *config.Message) (string, error) {
|
||||
if b.mc6 != nil {
|
||||
return b.handleUploadFile6(msg)
|
||||
}
|
||||
|
||||
var err error
|
||||
var res, id string
|
||||
channelID := b.mc.GetChannelId(msg.Channel, b.TeamID)
|
||||
channelID := b.getChannelID(msg.Channel)
|
||||
for _, f := range msg.Extra["file"] {
|
||||
fi := f.(config.FileInfo)
|
||||
id, err = b.mc.UploadFile(*fi.Data, channelID, fi.Name)
|
||||
@@ -173,6 +292,26 @@ func (b *Bmattermost) handleUploadFile(msg *config.Message) (string, error) {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// nolint:forcetypeassert,wrapcheck
|
||||
func (b *Bmattermost) handleUploadFile6(msg *config.Message) (string, error) {
|
||||
var err error
|
||||
var res, id string
|
||||
channelID := b.getChannelID(msg.Channel)
|
||||
for _, f := range msg.Extra["file"] {
|
||||
fi := f.(config.FileInfo)
|
||||
id, err = b.mc6.UploadFile(*fi.Data, channelID, fi.Name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
msg.Text = fi.Comment
|
||||
if b.GetBool("PrefixMessagesWithNick") {
|
||||
msg.Text = msg.Username + msg.Text
|
||||
}
|
||||
res, err = b.mc6.PostMessageWithFiles(channelID, msg.Text, msg.ParentID, []string{id})
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (b *Bmattermost) handleProps(rmsg *config.Message, message *matterclient.Message) {
|
||||
props := message.Post.Props
|
||||
if props == nil {
|
||||
@@ -197,3 +336,31 @@ func (b *Bmattermost) handleProps(rmsg *config.Message, message *matterclient.Me
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nolint:forcetypeassert
|
||||
func (b *Bmattermost) handleProps6(rmsg *config.Message, message *matterclient6.Message) {
|
||||
props := message.Post.Props
|
||||
if props == nil {
|
||||
return
|
||||
}
|
||||
if _, ok := props["override_username"].(string); ok {
|
||||
rmsg.Username = props["override_username"].(string)
|
||||
}
|
||||
if _, ok := props["attachments"].([]interface{}); ok {
|
||||
rmsg.Extra["attachments"] = props["attachments"].([]interface{})
|
||||
if rmsg.Text != "" {
|
||||
return
|
||||
}
|
||||
|
||||
for _, attachment := range rmsg.Extra["attachments"] {
|
||||
attach := attachment.(map[string]interface{})
|
||||
if attach["text"].(string) != "" {
|
||||
rmsg.Text += attach["text"].(string)
|
||||
continue
|
||||
}
|
||||
if attach["fallback"].(string) != "" {
|
||||
rmsg.Text += attach["fallback"].(string)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package bmattermost
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
"github.com/42wim/matterbridge/bridge/helper"
|
||||
"github.com/42wim/matterbridge/matterclient"
|
||||
"github.com/42wim/matterbridge/matterhook"
|
||||
matterclient6 "github.com/matterbridge/matterclient"
|
||||
"github.com/mattermost/mattermost-server/v5/model"
|
||||
model6 "github.com/mattermost/mattermost-server/v6/model"
|
||||
)
|
||||
|
||||
func (b *Bmattermost) doConnectWebhookBind() error {
|
||||
@@ -15,25 +18,47 @@ func (b *Bmattermost) doConnectWebhookBind() error {
|
||||
case b.GetString("WebhookURL") != "":
|
||||
b.Log.Info("Connecting using webhookurl (sending) and webhookbindaddress (receiving)")
|
||||
b.mh = matterhook.New(b.GetString("WebhookURL"),
|
||||
matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"),
|
||||
BindAddress: b.GetString("WebhookBindAddress")})
|
||||
matterhook.Config{
|
||||
InsecureSkipVerify: b.GetBool("SkipTLSVerify"),
|
||||
BindAddress: b.GetString("WebhookBindAddress"),
|
||||
})
|
||||
case b.GetString("Token") != "":
|
||||
b.Log.Info("Connecting using token (sending)")
|
||||
err := b.apiLogin()
|
||||
if err != nil {
|
||||
return err
|
||||
b.Log.Infof("Using mattermost v6 methods: %t", b.v6)
|
||||
|
||||
if b.v6 {
|
||||
err := b.apiLogin6()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err := b.apiLogin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case b.GetString("Login") != "":
|
||||
b.Log.Info("Connecting using login/password (sending)")
|
||||
err := b.apiLogin()
|
||||
if err != nil {
|
||||
return err
|
||||
b.Log.Infof("Using mattermost v6 methods: %t", b.v6)
|
||||
|
||||
if b.v6 {
|
||||
err := b.apiLogin6()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err := b.apiLogin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
default:
|
||||
b.Log.Info("Connecting using webhookbindaddress (receiving)")
|
||||
b.mh = matterhook.New(b.GetString("WebhookURL"),
|
||||
matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"),
|
||||
BindAddress: b.GetString("WebhookBindAddress")})
|
||||
matterhook.Config{
|
||||
InsecureSkipVerify: b.GetBool("SkipTLSVerify"),
|
||||
BindAddress: b.GetString("WebhookBindAddress"),
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -41,19 +66,39 @@ func (b *Bmattermost) doConnectWebhookBind() error {
|
||||
func (b *Bmattermost) doConnectWebhookURL() error {
|
||||
b.Log.Info("Connecting using webhookurl (sending)")
|
||||
b.mh = matterhook.New(b.GetString("WebhookURL"),
|
||||
matterhook.Config{InsecureSkipVerify: b.GetBool("SkipTLSVerify"),
|
||||
DisableServer: true})
|
||||
matterhook.Config{
|
||||
InsecureSkipVerify: b.GetBool("SkipTLSVerify"),
|
||||
DisableServer: true,
|
||||
})
|
||||
if b.GetString("Token") != "" {
|
||||
b.Log.Info("Connecting using token (receiving)")
|
||||
err := b.apiLogin()
|
||||
if err != nil {
|
||||
return err
|
||||
b.Log.Infof("Using mattermost v6 methods: %t", b.v6)
|
||||
|
||||
if b.v6 {
|
||||
err := b.apiLogin6()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err := b.apiLogin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if b.GetString("Login") != "" {
|
||||
b.Log.Info("Connecting using login/password (receiving)")
|
||||
err := b.apiLogin()
|
||||
if err != nil {
|
||||
return err
|
||||
b.Log.Infof("Using mattermost v6 methods: %t", b.v6)
|
||||
|
||||
if b.v6 {
|
||||
err := b.apiLogin6()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err := b.apiLogin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -84,6 +129,31 @@ func (b *Bmattermost) apiLogin() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// nolint:wrapcheck
|
||||
func (b *Bmattermost) apiLogin6() error {
|
||||
password := b.GetString("Password")
|
||||
if b.GetString("Token") != "" {
|
||||
password = "token=" + b.GetString("Token")
|
||||
}
|
||||
|
||||
b.mc6 = matterclient6.New(b.GetString("Login"), password, b.GetString("Team"), b.GetString("Server"), "")
|
||||
if b.GetBool("debug") {
|
||||
b.mc6.SetLogLevel("debug")
|
||||
}
|
||||
b.mc6.SkipTLSVerify = b.GetBool("SkipTLSVerify")
|
||||
b.mc6.SkipVersionCheck = b.GetBool("SkipVersionCheck")
|
||||
b.mc6.NoTLS = b.GetBool("NoTLS")
|
||||
b.Log.Infof("Connecting %s (team: %s) on %s", b.GetString("Login"), b.GetString("Team"), b.GetString("Server"))
|
||||
|
||||
if err := b.mc6.Login(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.Log.Info("Connection succeeded")
|
||||
b.TeamID = b.mc6.GetTeamID()
|
||||
return nil
|
||||
}
|
||||
|
||||
// replaceAction replace the message with the correct action (/me) code
|
||||
func (b *Bmattermost) replaceAction(text string) (string, bool) {
|
||||
if strings.HasPrefix(text, "*") && strings.HasSuffix(text, "*") {
|
||||
@@ -171,11 +241,17 @@ func (b *Bmattermost) skipMessage(message *matterclient.Message) bool {
|
||||
if b.GetBool("nosendjoinpart") {
|
||||
return true
|
||||
}
|
||||
|
||||
channelName := b.getChannelName(message.Post.ChannelId)
|
||||
if channelName == "" {
|
||||
channelName = message.Channel
|
||||
}
|
||||
|
||||
b.Log.Debugf("Sending JOIN_LEAVE event from %s to gateway", b.Account)
|
||||
b.Remote <- config.Message{
|
||||
Username: "system",
|
||||
Text: message.Text,
|
||||
Channel: message.Channel,
|
||||
Channel: channelName,
|
||||
Account: b.Account,
|
||||
Event: config.EventJoinLeave,
|
||||
}
|
||||
@@ -223,3 +299,119 @@ func (b *Bmattermost) skipMessage(message *matterclient.Message) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// skipMessages returns true if this message should not be handled
|
||||
// nolint:gocyclo,cyclop
|
||||
func (b *Bmattermost) skipMessage6(message *matterclient6.Message) bool {
|
||||
// Handle join/leave
|
||||
if message.Type == "system_join_leave" ||
|
||||
message.Type == "system_join_channel" ||
|
||||
message.Type == "system_leave_channel" {
|
||||
if b.GetBool("nosendjoinpart") {
|
||||
return true
|
||||
}
|
||||
|
||||
channelName := b.getChannelName(message.Post.ChannelId)
|
||||
if channelName == "" {
|
||||
channelName = message.Channel
|
||||
}
|
||||
|
||||
b.Log.Debugf("Sending JOIN_LEAVE event from %s to gateway", b.Account)
|
||||
b.Remote <- config.Message{
|
||||
Username: "system",
|
||||
Text: message.Text,
|
||||
Channel: channelName,
|
||||
Account: b.Account,
|
||||
Event: config.EventJoinLeave,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Handle edited messages
|
||||
if (message.Raw.EventType() == model6.WebsocketEventPostEdited) && b.GetBool("EditDisable") {
|
||||
return true
|
||||
}
|
||||
|
||||
// Ignore non-post messages
|
||||
if message.Post == nil {
|
||||
b.Log.Debugf("ignoring nil message.Post: %#v", message)
|
||||
return true
|
||||
}
|
||||
|
||||
// Ignore messages sent from matterbridge
|
||||
if message.Post.Props != nil {
|
||||
if _, ok := message.Post.Props["matterbridge_"+b.uuid].(bool); ok {
|
||||
b.Log.Debug("sent by matterbridge, ignoring")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore messages sent from a user logged in as the bot
|
||||
if b.mc6.User.Username == message.Username {
|
||||
b.Log.Debug("message from same user as bot, ignoring")
|
||||
return true
|
||||
}
|
||||
|
||||
// if the message has reactions don't repost it (for now, until we can correlate reaction with message)
|
||||
if message.Post.HasReactions {
|
||||
return true
|
||||
}
|
||||
|
||||
// ignore messages from other teams than ours
|
||||
if message.Raw.GetData()["team_id"].(string) != b.TeamID {
|
||||
b.Log.Debug("message from other team, ignoring")
|
||||
return true
|
||||
}
|
||||
|
||||
// only handle posted, edited or deleted events
|
||||
if !(message.Raw.EventType() == "posted" || message.Raw.EventType() == model6.WebsocketEventPostEdited ||
|
||||
message.Raw.EventType() == model6.WebsocketEventPostDeleted) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *Bmattermost) getVersion() string {
|
||||
proto := "https"
|
||||
|
||||
if b.GetBool("notls") {
|
||||
proto = "http"
|
||||
}
|
||||
|
||||
resp, err := http.Get(proto + "://" + b.GetString("server"))
|
||||
if err != nil {
|
||||
b.Log.Error("failed getting version")
|
||||
return ""
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
return resp.Header.Get("X-Version-Id")
|
||||
}
|
||||
|
||||
func (b *Bmattermost) getChannelID(name string) string {
|
||||
idcheck := strings.Split(name, "ID:")
|
||||
if len(idcheck) > 1 {
|
||||
return idcheck[1]
|
||||
}
|
||||
|
||||
if b.mc6 != nil {
|
||||
return b.mc6.GetChannelID(name, b.TeamID)
|
||||
}
|
||||
|
||||
return b.mc.GetChannelId(name, b.TeamID)
|
||||
}
|
||||
|
||||
func (b *Bmattermost) getChannelName(id string) string {
|
||||
b.channelsMutex.RLock()
|
||||
defer b.channelsMutex.RUnlock()
|
||||
|
||||
for _, c := range b.channelInfoMap {
|
||||
if c.Name == "ID:"+id {
|
||||
// if we have ID: specified in our gateway configuration return this
|
||||
return c.Name
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -3,29 +3,43 @@ package bmattermost
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/42wim/matterbridge/bridge"
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
"github.com/42wim/matterbridge/bridge/helper"
|
||||
"github.com/42wim/matterbridge/matterclient"
|
||||
"github.com/42wim/matterbridge/matterhook"
|
||||
matterclient6 "github.com/matterbridge/matterclient"
|
||||
"github.com/rs/xid"
|
||||
)
|
||||
|
||||
type Bmattermost struct {
|
||||
mh *matterhook.Client
|
||||
mc *matterclient.MMClient
|
||||
mc6 *matterclient6.Client
|
||||
v6 bool
|
||||
uuid string
|
||||
TeamID string
|
||||
*bridge.Config
|
||||
avatarMap map[string]string
|
||||
avatarMap map[string]string
|
||||
channelsMutex sync.RWMutex
|
||||
channelInfoMap map[string]*config.ChannelInfo
|
||||
}
|
||||
|
||||
const mattermostPlugin = "mattermost.plugin"
|
||||
|
||||
func New(cfg *bridge.Config) bridge.Bridger {
|
||||
b := &Bmattermost{Config: cfg, avatarMap: make(map[string]string)}
|
||||
b := &Bmattermost{
|
||||
Config: cfg,
|
||||
avatarMap: make(map[string]string),
|
||||
channelInfoMap: make(map[string]*config.ChannelInfo),
|
||||
}
|
||||
|
||||
b.v6 = b.GetBool("v6")
|
||||
b.uuid = xid.New().String()
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -37,6 +51,13 @@ func (b *Bmattermost) Connect() error {
|
||||
if b.Account == mattermostPlugin {
|
||||
return nil
|
||||
}
|
||||
|
||||
if strings.HasPrefix(b.getVersion(), "6.") {
|
||||
if !b.v6 {
|
||||
b.v6 = true
|
||||
}
|
||||
}
|
||||
|
||||
if b.GetString("WebhookBindAddress") != "" {
|
||||
if err := b.doConnectWebhookBind(); err != nil {
|
||||
return err
|
||||
@@ -53,16 +74,34 @@ func (b *Bmattermost) Connect() error {
|
||||
return nil
|
||||
case b.GetString("Token") != "":
|
||||
b.Log.Info("Connecting using token (sending and receiving)")
|
||||
err := b.apiLogin()
|
||||
if err != nil {
|
||||
return err
|
||||
b.Log.Infof("Using mattermost v6 methods: %t", b.v6)
|
||||
|
||||
if b.v6 {
|
||||
err := b.apiLogin6()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err := b.apiLogin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
go b.handleMatter()
|
||||
case b.GetString("Login") != "":
|
||||
b.Log.Info("Connecting using login/password (sending and receiving)")
|
||||
err := b.apiLogin()
|
||||
if err != nil {
|
||||
return err
|
||||
b.Log.Infof("Using mattermost v6 methods: %t", b.v6)
|
||||
|
||||
if b.v6 {
|
||||
err := b.apiLogin6()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err := b.apiLogin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
go b.handleMatter()
|
||||
}
|
||||
@@ -81,14 +120,25 @@ func (b *Bmattermost) JoinChannel(channel config.ChannelInfo) error {
|
||||
if b.Account == mattermostPlugin {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.channelsMutex.Lock()
|
||||
b.channelInfoMap[channel.ID] = &channel
|
||||
b.channelsMutex.Unlock()
|
||||
|
||||
// we can only join channels using the API
|
||||
if b.GetString("WebhookURL") == "" && b.GetString("WebhookBindAddress") == "" {
|
||||
id := b.mc.GetChannelId(channel.Name, b.TeamID)
|
||||
id := b.getChannelID(channel.Name)
|
||||
if id == "" {
|
||||
return fmt.Errorf("Could not find channel ID for channel %s", channel.Name)
|
||||
}
|
||||
|
||||
if b.mc6 != nil {
|
||||
return b.mc6.JoinChannel(id) // nolint:wrapcheck
|
||||
}
|
||||
|
||||
return b.mc.JoinChannel(id)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -118,6 +168,10 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) {
|
||||
if msg.ID == "" {
|
||||
return "", nil
|
||||
}
|
||||
if b.mc6 != nil {
|
||||
return msg.ID, b.mc6.DeleteMessage(msg.ID) // nolint:wrapcheck
|
||||
}
|
||||
|
||||
return msg.ID, b.mc.DeleteMessage(msg.ID)
|
||||
}
|
||||
|
||||
@@ -129,18 +183,36 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) {
|
||||
|
||||
// we only can reply to the root of the thread, not to a specific ID (like discord for example does)
|
||||
if msg.ParentID != "" {
|
||||
post, res := b.mc.Client.GetPost(msg.ParentID, "")
|
||||
if res.Error != nil {
|
||||
b.Log.Errorf("getting post %s failed: %s", msg.ParentID, res.Error.DetailedError)
|
||||
if b.mc6 != nil {
|
||||
post, _, err := b.mc6.Client.GetPost(msg.ParentID, "")
|
||||
if err != nil {
|
||||
b.Log.Errorf("getting post %s failed: %s", msg.ParentID, err)
|
||||
}
|
||||
if post.RootId != "" {
|
||||
msg.ParentID = post.RootId
|
||||
}
|
||||
} else {
|
||||
post, res := b.mc.Client.GetPost(msg.ParentID, "")
|
||||
if res.Error != nil {
|
||||
b.Log.Errorf("getting post %s failed: %s", msg.ParentID, res.Error.DetailedError)
|
||||
}
|
||||
if post.RootId != "" {
|
||||
msg.ParentID = post.RootId
|
||||
}
|
||||
}
|
||||
msg.ParentID = post.RootId
|
||||
}
|
||||
|
||||
// Upload a file if it exists
|
||||
if msg.Extra != nil {
|
||||
for _, rmsg := range helper.HandleExtra(&msg, b.General) {
|
||||
if _, err := b.mc.PostMessage(b.mc.GetChannelId(rmsg.Channel, b.TeamID), rmsg.Username+rmsg.Text, msg.ParentID); err != nil {
|
||||
b.Log.Errorf("PostMessage failed: %s", err)
|
||||
if b.mc6 != nil {
|
||||
if _, err := b.mc6.PostMessage(b.getChannelID(rmsg.Channel), rmsg.Username+rmsg.Text, msg.ParentID); err != nil {
|
||||
b.Log.Errorf("PostMessage failed: %s", err)
|
||||
}
|
||||
} else {
|
||||
if _, err := b.mc.PostMessage(b.getChannelID(rmsg.Channel), rmsg.Username+rmsg.Text, msg.ParentID); err != nil {
|
||||
b.Log.Errorf("PostMessage failed: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(msg.Extra["file"]) > 0 {
|
||||
@@ -155,9 +227,17 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) {
|
||||
|
||||
// Edit message if we have an ID
|
||||
if msg.ID != "" {
|
||||
if b.mc6 != nil {
|
||||
return b.mc6.EditMessage(msg.ID, msg.Text) // nolint:wrapcheck
|
||||
}
|
||||
|
||||
return b.mc.EditMessage(msg.ID, msg.Text)
|
||||
}
|
||||
|
||||
// Post normal message
|
||||
return b.mc.PostMessage(b.mc.GetChannelId(msg.Channel, b.TeamID), msg.Text, msg.ParentID)
|
||||
if b.mc6 != nil {
|
||||
return b.mc6.PostMessage(b.getChannelID(msg.Channel), msg.Text, msg.ParentID) // nolint:wrapcheck
|
||||
}
|
||||
|
||||
return b.mc.PostMessage(b.getChannelID(msg.Channel), msg.Text, msg.ParentID)
|
||||
}
|
||||
|
||||
@@ -19,8 +19,10 @@ import (
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
var defaultScopes = []string{"openid", "profile", "offline_access", "Group.Read.All", "Group.ReadWrite.All"}
|
||||
var attachRE = regexp.MustCompile(`<attachment id=.*?attachment>`)
|
||||
var (
|
||||
defaultScopes = []string{"openid", "profile", "offline_access", "Group.Read.All", "Group.ReadWrite.All"}
|
||||
attachRE = regexp.MustCompile(`<attachment id=.*?attachment>`)
|
||||
)
|
||||
|
||||
type Bmsteams struct {
|
||||
gc *msgraph.GraphServiceRequestBuilder
|
||||
@@ -50,7 +52,7 @@ func (b *Bmsteams) Connect() error {
|
||||
b.Log.Errorf("Couldn't save sessionfile in %s: %s", tokenCachePath, err)
|
||||
}
|
||||
// make file readable only for matterbridge user
|
||||
err = os.Chmod(tokenCachePath, 0600)
|
||||
err = os.Chmod(tokenCachePath, 0o600)
|
||||
if err != nil {
|
||||
b.Log.Errorf("Couldn't change permissions for %s: %s", tokenCachePath, err)
|
||||
}
|
||||
@@ -168,7 +170,7 @@ func (b *Bmsteams) poll(channelName string) error {
|
||||
}
|
||||
|
||||
// skip non-user message for now.
|
||||
if msg.From.User == nil {
|
||||
if msg.From == nil || msg.From.User == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"layeh.com/gumble/gumble"
|
||||
@@ -248,12 +249,14 @@ func (b *Bmumble) processMessage(msg *config.Message) {
|
||||
// If there is a maximum message length, split and truncate the lines
|
||||
var msgLines []string
|
||||
if maxLength := b.serverConfig.MaximumMessageLength; maxLength != nil {
|
||||
msgLines = helper.GetSubLines(msg.Text, *maxLength-len(msg.Username))
|
||||
msgLines = helper.GetSubLines(msg.Text, *maxLength-len(msg.Username), b.GetString("MessageClipped"))
|
||||
} else {
|
||||
msgLines = helper.GetSubLines(msg.Text, 0)
|
||||
msgLines = helper.GetSubLines(msg.Text, 0, b.GetString("MessageClipped"))
|
||||
}
|
||||
// Send the individual lindes
|
||||
// Send the individual lines
|
||||
for i := range msgLines {
|
||||
// Remove unnecessary newline character, since either way we're sending it as individual lines
|
||||
msgLines[i] = strings.TrimSuffix(msgLines[i], "\n")
|
||||
b.client.Self.Channel.Send(msg.Username+msgLines[i], false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,12 +74,6 @@ func (b *Btalk) JoinChannel(channel config.ChannelInfo) error {
|
||||
}
|
||||
b.rooms = append(b.rooms, newRoom)
|
||||
|
||||
// Config
|
||||
guestSuffix := " (Guest)"
|
||||
if b.IsKeySet("GuestSuffix") {
|
||||
guestSuffix = b.GetString("GuestSuffix")
|
||||
}
|
||||
|
||||
go func() {
|
||||
for msg := range c {
|
||||
msg := msg
|
||||
@@ -90,35 +84,23 @@ func (b *Btalk) JoinChannel(channel config.ChannelInfo) error {
|
||||
return
|
||||
}
|
||||
|
||||
// ignore messages that are one of the following
|
||||
// * not a message from a user
|
||||
// * from ourselves
|
||||
if msg.MessageType != ocs.MessageComment || msg.ActorID == b.user.User {
|
||||
continue
|
||||
}
|
||||
remoteMessage := config.Message{
|
||||
Text: formatRichObjectString(msg.Message, msg.MessageParameters),
|
||||
Channel: newRoom.room.Token,
|
||||
Username: DisplayName(msg, guestSuffix),
|
||||
UserID: msg.ActorID,
|
||||
Account: b.Account,
|
||||
}
|
||||
// It is possible for the ID to not be set on older versions of Talk so we only set it if
|
||||
// the ID is not blank
|
||||
if msg.ID != 0 {
|
||||
remoteMessage.ID = strconv.Itoa(msg.ID)
|
||||
}
|
||||
|
||||
// Handle Files
|
||||
err = b.handleFiles(&remoteMessage, &msg)
|
||||
if err != nil {
|
||||
b.Log.Errorf("Error handling file: %#v", msg)
|
||||
|
||||
// Ignore messages that are from the bot user
|
||||
if msg.ActorID == b.user.User || msg.ActorType == "bridged" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle deleting messages
|
||||
if msg.MessageType == ocs.MessageSystem && msg.Parent != nil && msg.Parent.MessageType == ocs.MessageDelete {
|
||||
b.handleDeletingMessage(&msg, &newRoom)
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle sending messages
|
||||
if msg.MessageType == ocs.MessageComment {
|
||||
b.handleSendingMessage(&msg, &newRoom)
|
||||
continue
|
||||
}
|
||||
|
||||
b.Log.Debugf("<= Message is %#v", remoteMessage)
|
||||
b.Remote <- remoteMessage
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
@@ -131,16 +113,40 @@ func (b *Btalk) Send(msg config.Message) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Talk currently only supports sending normal messages
|
||||
if msg.Event != "" {
|
||||
return "", nil
|
||||
// Standard Message Send
|
||||
if msg.Event == "" {
|
||||
// Handle sending files if they are included
|
||||
err := b.handleSendingFile(&msg, r)
|
||||
if err != nil {
|
||||
b.Log.Errorf("Could not send files in message to room %v from %v: %v", msg.Channel, msg.Username, err)
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
sentMessage, err := b.sendText(r, &msg, msg.Text)
|
||||
if err != nil {
|
||||
b.Log.Errorf("Could not send message to room %v from %v: %v", msg.Channel, msg.Username, err)
|
||||
|
||||
return "", nil
|
||||
}
|
||||
return strconv.Itoa(sentMessage.ID), nil
|
||||
}
|
||||
sentMessage, err := r.room.SendMessage(msg.Username + msg.Text)
|
||||
if err != nil {
|
||||
b.Log.Errorf("Could not send message to room %v from %v: %v", msg.Channel, msg.Username, err)
|
||||
return "", nil
|
||||
|
||||
// Message Deletion
|
||||
if msg.Event == config.EventMsgDelete {
|
||||
messageID, err := strconv.Atoi(msg.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
data, err := r.room.DeleteMessage(messageID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strconv.Itoa(data.ID), nil
|
||||
}
|
||||
return strconv.Itoa(sentMessage.ID), nil
|
||||
|
||||
// Message is not a type that is currently supported
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (b *Btalk) getRoom(token string) *Broom {
|
||||
@@ -152,6 +158,17 @@ func (b *Btalk) getRoom(token string) *Broom {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Btalk) sendText(r *Broom, msg *config.Message, text string) (*ocs.TalkRoomMessageData, error) {
|
||||
messageToSend := &room.Message{Message: msg.Username + text}
|
||||
|
||||
if b.GetBool("SeparateDisplayName") {
|
||||
messageToSend.Message = text
|
||||
messageToSend.ActorDisplayName = msg.Username
|
||||
}
|
||||
|
||||
return r.room.SendComplexMessage(messageToSend)
|
||||
}
|
||||
|
||||
func (b *Btalk) handleFiles(mmsg *config.Message, message *ocs.TalkRoomMessageData) error {
|
||||
for _, parameter := range message.MessageParameters {
|
||||
if parameter.Type == ocs.ROSTypeFile {
|
||||
@@ -177,6 +194,74 @@ func (b *Btalk) handleFiles(mmsg *config.Message, message *ocs.TalkRoomMessageDa
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Btalk) handleSendingFile(msg *config.Message, r *Broom) error {
|
||||
for _, f := range msg.Extra["file"] {
|
||||
fi := f.(config.FileInfo)
|
||||
if fi.URL == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
message := ""
|
||||
if fi.Comment != "" {
|
||||
message += fi.Comment + " "
|
||||
}
|
||||
message += fi.URL
|
||||
_, err := b.sendText(r, msg, message)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Btalk) handleSendingMessage(msg *ocs.TalkRoomMessageData, r *Broom) {
|
||||
remoteMessage := config.Message{
|
||||
Text: formatRichObjectString(msg.Message, msg.MessageParameters),
|
||||
Channel: r.room.Token,
|
||||
Username: DisplayName(msg, b.guestSuffix()),
|
||||
UserID: msg.ActorID,
|
||||
Account: b.Account,
|
||||
}
|
||||
// It is possible for the ID to not be set on older versions of Talk so we only set it if
|
||||
// the ID is not blank
|
||||
if msg.ID != 0 {
|
||||
remoteMessage.ID = strconv.Itoa(msg.ID)
|
||||
}
|
||||
|
||||
// Handle Files
|
||||
err := b.handleFiles(&remoteMessage, msg)
|
||||
if err != nil {
|
||||
b.Log.Errorf("Error handling file: %#v", msg)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
b.Log.Debugf("<= Message is %#v", remoteMessage)
|
||||
b.Remote <- remoteMessage
|
||||
}
|
||||
|
||||
func (b *Btalk) handleDeletingMessage(msg *ocs.TalkRoomMessageData, r *Broom) {
|
||||
remoteMessage := config.Message{
|
||||
Event: config.EventMsgDelete,
|
||||
Text: config.EventMsgDelete,
|
||||
Channel: r.room.Token,
|
||||
ID: strconv.Itoa(msg.Parent.ID),
|
||||
Account: b.Account,
|
||||
}
|
||||
b.Log.Debugf("<= Message being deleted is %#v", remoteMessage)
|
||||
b.Remote <- remoteMessage
|
||||
}
|
||||
|
||||
func (b *Btalk) guestSuffix() string {
|
||||
guestSuffix := " (Guest)"
|
||||
if b.IsKeySet("GuestSuffix") {
|
||||
guestSuffix = b.GetString("GuestSuffix")
|
||||
}
|
||||
|
||||
return guestSuffix
|
||||
}
|
||||
|
||||
// Spec: https://github.com/nextcloud/server/issues/1706#issue-182308785
|
||||
func formatRichObjectString(message string, parameters map[string]ocs.RichObjectString) string {
|
||||
for id, parameter := range parameters {
|
||||
@@ -197,7 +282,7 @@ func formatRichObjectString(message string, parameters map[string]ocs.RichObject
|
||||
return message
|
||||
}
|
||||
|
||||
func DisplayName(msg ocs.TalkRoomMessageData, suffix string) string {
|
||||
func DisplayName(msg *ocs.TalkRoomMessageData, suffix string) string {
|
||||
if msg.ActorType == ocs.ActorGuest {
|
||||
if msg.ActorDisplayName == "" {
|
||||
return "Guest"
|
||||
|
||||
@@ -27,7 +27,8 @@ func (b *Bslack) handleSlack() {
|
||||
b.Log.Debug("Start listening for Slack messages")
|
||||
for message := range messages {
|
||||
// don't do any action on deleted/typing messages
|
||||
if message.Event != config.EventUserTyping && message.Event != config.EventMsgDelete {
|
||||
if message.Event != config.EventUserTyping && message.Event != config.EventMsgDelete &&
|
||||
message.Event != config.EventFileDelete {
|
||||
b.Log.Debugf("<= Sending message from %s on %s to gateway", message.Username, b.Account)
|
||||
// cleanup the message
|
||||
message.Text = b.replaceMention(message.Text)
|
||||
@@ -76,6 +77,13 @@ func (b *Bslack) handleSlackClient(messages chan *config.Message) {
|
||||
continue
|
||||
}
|
||||
messages <- rmsg
|
||||
case *slack.FileDeletedEvent:
|
||||
rmsg, err := b.handleFileDeletedEvent(ev)
|
||||
if err != nil {
|
||||
b.Log.Errorf("%#v", err)
|
||||
continue
|
||||
}
|
||||
messages <- rmsg
|
||||
case *slack.OutgoingErrorEvent:
|
||||
b.Log.Debugf("%#v", ev.Error())
|
||||
case *slack.ChannelJoinedEvent:
|
||||
@@ -95,6 +103,8 @@ func (b *Bslack) handleSlackClient(messages chan *config.Message) {
|
||||
b.users.populateUser(ev.User)
|
||||
case *slack.HelloEvent, *slack.LatencyReport, *slack.ConnectingEvent:
|
||||
continue
|
||||
case *slack.UserChangeEvent:
|
||||
b.users.invalidateUser(ev.User.ID)
|
||||
default:
|
||||
b.Log.Debugf("Unhandled incoming event: %T", ev)
|
||||
}
|
||||
@@ -220,6 +230,26 @@ func (b *Bslack) handleMessageEvent(ev *slack.MessageEvent) (*config.Message, er
|
||||
return rmsg, nil
|
||||
}
|
||||
|
||||
func (b *Bslack) handleFileDeletedEvent(ev *slack.FileDeletedEvent) (*config.Message, error) {
|
||||
if rawChannel, ok := b.cache.Get(cfileDownloadChannel + ev.FileID); ok {
|
||||
channel, err := b.channels.getChannelByID(rawChannel.(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &config.Message{
|
||||
Event: config.EventFileDelete,
|
||||
Text: config.EventFileDelete,
|
||||
Channel: channel.Name,
|
||||
Account: b.Account,
|
||||
ID: ev.FileID,
|
||||
Protocol: b.Protocol,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("channel ID for file ID %s not found", ev.FileID)
|
||||
}
|
||||
|
||||
func (b *Bslack) handleStatusEvent(ev *slack.MessageEvent, rmsg *config.Message) bool {
|
||||
switch ev.SubType {
|
||||
case sChannelJoined, sMemberJoined:
|
||||
@@ -279,6 +309,8 @@ func (b *Bslack) handleAttachments(ev *slack.MessageEvent, rmsg *config.Message)
|
||||
|
||||
// If we have files attached, download them (in memory) and put a pointer to it in msg.Extra.
|
||||
for i := range ev.Files {
|
||||
// keep reference in cache on which channel we added this file
|
||||
b.cache.Add(cfileDownloadChannel+ev.Files[i].ID, ev.Channel)
|
||||
if err := b.handleDownloadFile(rmsg, &ev.Files[i], false); err != nil {
|
||||
b.Log.Errorf("Could not download incoming file: %#v", err)
|
||||
}
|
||||
@@ -328,7 +360,7 @@ func (b *Bslack) handleDownloadFile(rmsg *config.Message, file *slack.File, retr
|
||||
// that the comment is not duplicated.
|
||||
comment := rmsg.Text
|
||||
rmsg.Text = ""
|
||||
helper.HandleDownloadData(b.Log, rmsg, file.Name, comment, file.URLPrivateDownload, data, b.General)
|
||||
helper.HandleDownloadData2(b.Log, rmsg, file.Name, file.ID, comment, file.URLPrivateDownload, data, b.General)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -36,24 +36,25 @@ type Bslack struct {
|
||||
}
|
||||
|
||||
const (
|
||||
sHello = "hello"
|
||||
sChannelJoin = "channel_join"
|
||||
sChannelLeave = "channel_leave"
|
||||
sChannelJoined = "channel_joined"
|
||||
sMemberJoined = "member_joined_channel"
|
||||
sMessageChanged = "message_changed"
|
||||
sMessageDeleted = "message_deleted"
|
||||
sSlackAttachment = "slack_attachment"
|
||||
sPinnedItem = "pinned_item"
|
||||
sUnpinnedItem = "unpinned_item"
|
||||
sChannelTopic = "channel_topic"
|
||||
sChannelPurpose = "channel_purpose"
|
||||
sFileComment = "file_comment"
|
||||
sMeMessage = "me_message"
|
||||
sUserTyping = "user_typing"
|
||||
sLatencyReport = "latency_report"
|
||||
sSystemUser = "system"
|
||||
sSlackBotUser = "slackbot"
|
||||
sHello = "hello"
|
||||
sChannelJoin = "channel_join"
|
||||
sChannelLeave = "channel_leave"
|
||||
sChannelJoined = "channel_joined"
|
||||
sMemberJoined = "member_joined_channel"
|
||||
sMessageChanged = "message_changed"
|
||||
sMessageDeleted = "message_deleted"
|
||||
sSlackAttachment = "slack_attachment"
|
||||
sPinnedItem = "pinned_item"
|
||||
sUnpinnedItem = "unpinned_item"
|
||||
sChannelTopic = "channel_topic"
|
||||
sChannelPurpose = "channel_purpose"
|
||||
sFileComment = "file_comment"
|
||||
sMeMessage = "me_message"
|
||||
sUserTyping = "user_typing"
|
||||
sLatencyReport = "latency_report"
|
||||
sSystemUser = "system"
|
||||
sSlackBotUser = "slackbot"
|
||||
cfileDownloadChannel = "file_download_channel"
|
||||
|
||||
tokenConfig = "Token"
|
||||
incomingWebhookConfig = "WebhookBindAddress"
|
||||
@@ -156,7 +157,7 @@ func (b *Bslack) JoinChannel(channel config.ChannelInfo) error {
|
||||
|
||||
// try to join a channel when in legacy
|
||||
if b.legacy {
|
||||
_, err := b.sc.JoinChannel(channel.Name)
|
||||
_, _, _, err := b.sc.JoinConversation(channel.Name)
|
||||
if err != nil {
|
||||
switch err.Error() {
|
||||
case "name_taken", "restricted_action":
|
||||
@@ -195,7 +196,7 @@ func (b *Bslack) Send(msg config.Message) (string, error) {
|
||||
b.Log.Debugf("=> Receiving %#v", msg)
|
||||
}
|
||||
|
||||
msg.Text = helper.ClipMessage(msg.Text, messageLength)
|
||||
msg.Text = helper.ClipMessage(msg.Text, messageLength, b.GetString("MessageClipped"))
|
||||
msg.Text = b.replaceCodeFence(msg.Text)
|
||||
|
||||
// Make a action /me of the message
|
||||
@@ -459,7 +460,7 @@ func (b *Bslack) uploadFile(msg *config.Message, channelID string) {
|
||||
b.cache.Add("filename"+fi.Name, ts)
|
||||
initialComment := fmt.Sprintf("File from %s", msg.Username)
|
||||
if fi.Comment != "" {
|
||||
initialComment += fmt.Sprintf("with comment: %s", fi.Comment)
|
||||
initialComment += fmt.Sprintf(" with comment: %s", fi.Comment)
|
||||
}
|
||||
res, err := b.sc.UploadFile(slack.FileUploadParameters{
|
||||
Reader: bytes.NewReader(*fi.Data),
|
||||
|
||||
@@ -113,6 +113,12 @@ func (b *users) populateUser(userID string) {
|
||||
b.users[userID] = user
|
||||
}
|
||||
|
||||
func (b *users) invalidateUser(userID string) {
|
||||
b.usersMutex.Lock()
|
||||
defer b.usersMutex.Unlock()
|
||||
delete(b.users, userID)
|
||||
}
|
||||
|
||||
func (b *users) populateUsers(wait bool) {
|
||||
b.refreshMutex.Lock()
|
||||
if !wait && (time.Now().Before(b.earliestRefresh) || b.refreshInProgress) {
|
||||
@@ -283,8 +289,9 @@ func (b *channels) populateChannels(wait bool) {
|
||||
// We only retrieve public and private channels, not IMs
|
||||
// and MPIMs as those do not have a channel name.
|
||||
queryParams := &slack.GetConversationsParameters{
|
||||
ExcludeArchived: "true",
|
||||
ExcludeArchived: true,
|
||||
Types: []string{"public_channel,private_channel"},
|
||||
Limit: 1000,
|
||||
}
|
||||
for {
|
||||
channels, nextCursor, err := b.sc.GetConversations(queryParams)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package btelegram
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@@ -9,14 +10,27 @@ import (
|
||||
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
"github.com/42wim/matterbridge/bridge/helper"
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
)
|
||||
|
||||
func (b *Btelegram) handleUpdate(rmsg *config.Message, message, posted, edited *tgbotapi.Message) *tgbotapi.Message {
|
||||
// handle channels
|
||||
if posted != nil {
|
||||
message = posted
|
||||
rmsg.Text = message.Text
|
||||
if posted.Text == "/chatId" {
|
||||
chatID := strconv.FormatInt(posted.Chat.ID, 10)
|
||||
|
||||
_, err := b.Send(config.Message{
|
||||
Channel: chatID,
|
||||
Text: fmt.Sprintf("ID of this chat: %s", chatID),
|
||||
})
|
||||
if err != nil {
|
||||
b.Log.Warnf("Unable to send chatID to %s", chatID)
|
||||
}
|
||||
} else {
|
||||
message = posted
|
||||
rmsg.Text = message.Text
|
||||
}
|
||||
}
|
||||
|
||||
// edited channel message
|
||||
@@ -94,7 +108,7 @@ func (b *Btelegram) handleQuoting(rmsg *config.Message, message *tgbotapi.Messag
|
||||
// handleUsername handles the correct setting of the username
|
||||
func (b *Btelegram) handleUsername(rmsg *config.Message, message *tgbotapi.Message) {
|
||||
if message.From != nil {
|
||||
rmsg.UserID = strconv.Itoa(message.From.ID)
|
||||
rmsg.UserID = strconv.FormatInt(message.From.ID, 10)
|
||||
if b.GetBool("UseFirstName") {
|
||||
rmsg.Username = message.From.FirstName
|
||||
}
|
||||
@@ -110,6 +124,25 @@ func (b *Btelegram) handleUsername(rmsg *config.Message, message *tgbotapi.Messa
|
||||
}
|
||||
}
|
||||
|
||||
if message.SenderChat != nil { //nolint:nestif
|
||||
rmsg.UserID = strconv.FormatInt(message.SenderChat.ID, 10)
|
||||
if b.GetBool("UseFirstName") {
|
||||
rmsg.Username = message.SenderChat.FirstName
|
||||
}
|
||||
|
||||
if rmsg.Username == "" || rmsg.Username == "Channel_Bot" {
|
||||
rmsg.Username = message.SenderChat.UserName
|
||||
|
||||
if rmsg.Username == "" || rmsg.Username == "Channel_Bot" {
|
||||
rmsg.Username = message.SenderChat.FirstName
|
||||
}
|
||||
}
|
||||
// only download avatars if we have a place to upload them (configured mediaserver)
|
||||
if b.General.MediaServerUpload != "" || (b.General.MediaServerDownload != "" && b.General.MediaDownloadPath != "") {
|
||||
b.handleDownloadAvatar(message.SenderChat.ID, rmsg.Channel)
|
||||
}
|
||||
}
|
||||
|
||||
// if we really didn't find a username, set it to unknown
|
||||
if rmsg.Username == "" {
|
||||
rmsg.Username = unknownUser
|
||||
@@ -126,6 +159,10 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
|
||||
continue
|
||||
}
|
||||
|
||||
if b.GetInt("debuglevel") == 1 {
|
||||
spew.Dump(update.Message)
|
||||
}
|
||||
|
||||
var message *tgbotapi.Message
|
||||
|
||||
rmsg := config.Message{Account: b.Account, Extra: make(map[string][]interface{})}
|
||||
@@ -167,7 +204,7 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
|
||||
rmsg.Text = helper.RemoveEmptyNewLines(rmsg.Text)
|
||||
// channels don't have (always?) user information. see #410
|
||||
if message.From != nil {
|
||||
rmsg.Avatar = helper.GetAvatar(b.avatarMap, strconv.Itoa(message.From.ID), b.General)
|
||||
rmsg.Avatar = helper.GetAvatar(b.avatarMap, strconv.FormatInt(message.From.ID, 10), b.General)
|
||||
}
|
||||
|
||||
b.Log.Debugf("<= Sending message from %s on %s to gateway", rmsg.Username, b.Account)
|
||||
@@ -180,60 +217,52 @@ func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
|
||||
// handleDownloadAvatar downloads the avatar of userid from channel
|
||||
// sends a EVENT_AVATAR_DOWNLOAD message to the gateway if successful.
|
||||
// logs an error message if it fails
|
||||
func (b *Btelegram) handleDownloadAvatar(userid int, channel string) {
|
||||
func (b *Btelegram) handleDownloadAvatar(userid int64, channel string) {
|
||||
rmsg := config.Message{
|
||||
Username: "system",
|
||||
Text: "avatar",
|
||||
Channel: channel,
|
||||
Account: b.Account,
|
||||
UserID: strconv.Itoa(userid),
|
||||
UserID: strconv.FormatInt(userid, 10),
|
||||
Event: config.EventAvatarDownload,
|
||||
Extra: make(map[string][]interface{}),
|
||||
}
|
||||
|
||||
if _, ok := b.avatarMap[strconv.Itoa(userid)]; !ok {
|
||||
photos, err := b.c.GetUserProfilePhotos(tgbotapi.UserProfilePhotosConfig{UserID: userid, Limit: 1})
|
||||
if _, ok := b.avatarMap[strconv.FormatInt(userid, 10)]; ok {
|
||||
return
|
||||
}
|
||||
|
||||
photos, err := b.c.GetUserProfilePhotos(tgbotapi.UserProfilePhotosConfig{UserID: userid, Limit: 1})
|
||||
if err != nil {
|
||||
b.Log.Errorf("Userprofile download failed for %#v %s", userid, err)
|
||||
}
|
||||
|
||||
if len(photos.Photos) > 0 {
|
||||
photo := photos.Photos[0][0]
|
||||
url := b.getFileDirectURL(photo.FileID)
|
||||
name := strconv.FormatInt(userid, 10) + ".png"
|
||||
b.Log.Debugf("trying to download %#v fileid %#v with size %#v", name, photo.FileID, photo.FileSize)
|
||||
|
||||
err := helper.HandleDownloadSize(b.Log, &rmsg, name, int64(photo.FileSize), b.General)
|
||||
if err != nil {
|
||||
b.Log.Errorf("Userprofile download failed for %#v %s", userid, err)
|
||||
b.Log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(photos.Photos) > 0 {
|
||||
photo := photos.Photos[0][0]
|
||||
url := b.getFileDirectURL(photo.FileID)
|
||||
name := strconv.Itoa(userid) + ".png"
|
||||
b.Log.Debugf("trying to download %#v fileid %#v with size %#v", name, photo.FileID, photo.FileSize)
|
||||
|
||||
err := helper.HandleDownloadSize(b.Log, &rmsg, name, int64(photo.FileSize), b.General)
|
||||
if err != nil {
|
||||
b.Log.Error(err)
|
||||
return
|
||||
}
|
||||
data, err := helper.DownloadFile(url)
|
||||
if err != nil {
|
||||
b.Log.Errorf("download %s failed %#v", url, err)
|
||||
return
|
||||
}
|
||||
helper.HandleDownloadData(b.Log, &rmsg, name, rmsg.Text, "", data, b.General)
|
||||
b.Remote <- rmsg
|
||||
data, err := helper.DownloadFile(url)
|
||||
if err != nil {
|
||||
b.Log.Errorf("download %s failed %#v", url, err)
|
||||
return
|
||||
}
|
||||
helper.HandleDownloadData(b.Log, &rmsg, name, rmsg.Text, "", data, b.General)
|
||||
b.Remote <- rmsg
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Btelegram) maybeConvertTgs(name *string, data *[]byte) {
|
||||
var format string
|
||||
switch b.GetString("MediaConvertTgs") {
|
||||
case FormatWebp:
|
||||
b.Log.Debugf("Tgs to WebP conversion enabled, converting %v", name)
|
||||
format = FormatWebp
|
||||
case FormatPng:
|
||||
// The WebP to PNG converter can't handle animated webp files yet,
|
||||
// and I'm not going to write a path for x/image/webp.
|
||||
// The error message would be:
|
||||
// conversion failed: webp: non-Alpha VP8X is not implemented
|
||||
// So instead, we tell lottie to directly go to PNG.
|
||||
b.Log.Debugf("Tgs to PNG conversion enabled, converting %v", name)
|
||||
format = FormatPng
|
||||
default:
|
||||
format := b.GetString("MediaConvertTgs")
|
||||
if helper.SupportsFormat(format) {
|
||||
b.Log.Debugf("Format supported by %s, converting %v", helper.LottieBackend(), name)
|
||||
} else {
|
||||
// Otherwise, no conversion was requested. Trying to run the usual webp
|
||||
// converter would fail, because '.tgs.webp' is actually a gzipped JSON
|
||||
// file, and has nothing to do with WebP.
|
||||
@@ -282,7 +311,7 @@ func (b *Btelegram) handleDownload(rmsg *config.Message, message *tgbotapi.Messa
|
||||
name = message.Document.FileName
|
||||
text = " " + message.Document.FileName + " : " + url
|
||||
case message.Photo != nil:
|
||||
photos := *message.Photo
|
||||
photos := message.Photo
|
||||
size = photos[len(photos)-1].FileSize
|
||||
text, name, url = b.getDownloadInfo(photos[len(photos)-1].FileID, "", true)
|
||||
}
|
||||
@@ -341,11 +370,15 @@ func (b *Btelegram) handleDelete(msg *config.Message, chatid int64) (string, err
|
||||
if msg.ID == "" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
msgid, err := strconv.Atoi(msg.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_, err = b.c.DeleteMessage(tgbotapi.DeleteMessageConfig{ChatID: chatid, MessageID: msgid})
|
||||
|
||||
cfg := tgbotapi.NewDeleteMessage(chatid, msgid)
|
||||
_, err = b.c.Send(cfg)
|
||||
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -393,23 +426,23 @@ func (b *Btelegram) handleUploadFile(msg *config.Message, chatid int64) string {
|
||||
}
|
||||
switch filepath.Ext(fi.Name) {
|
||||
case ".jpg", ".jpe", ".png":
|
||||
pc := tgbotapi.NewPhotoUpload(chatid, file)
|
||||
pc := tgbotapi.NewPhoto(chatid, file)
|
||||
pc.Caption, pc.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment)
|
||||
c = pc
|
||||
case ".mp4", ".m4v":
|
||||
vc := tgbotapi.NewVideoUpload(chatid, file)
|
||||
vc := tgbotapi.NewVideo(chatid, file)
|
||||
vc.Caption, vc.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment)
|
||||
c = vc
|
||||
case ".mp3", ".oga":
|
||||
ac := tgbotapi.NewAudioUpload(chatid, file)
|
||||
ac := tgbotapi.NewAudio(chatid, file)
|
||||
ac.Caption, ac.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment)
|
||||
c = ac
|
||||
case ".ogg":
|
||||
voc := tgbotapi.NewVoiceUpload(chatid, file)
|
||||
voc := tgbotapi.NewVoice(chatid, file)
|
||||
voc.Caption, voc.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment)
|
||||
c = voc
|
||||
default:
|
||||
dc := tgbotapi.NewDocumentUpload(chatid, file)
|
||||
dc := tgbotapi.NewDocument(chatid, file)
|
||||
dc.Caption, dc.ParseMode = TGGetParseMode(b, msg.Username, fi.Comment)
|
||||
c = dc
|
||||
}
|
||||
@@ -445,8 +478,11 @@ func (b *Btelegram) handleEntities(rmsg *config.Message, message *tgbotapi.Messa
|
||||
if message.Entities == nil {
|
||||
return
|
||||
}
|
||||
|
||||
indexMovedBy := 0
|
||||
|
||||
// for now only do URL replacements
|
||||
for _, e := range *message.Entities {
|
||||
for _, e := range message.Entities {
|
||||
if e.Type == "text_link" {
|
||||
url, err := e.ParseURL()
|
||||
if err != nil {
|
||||
@@ -461,5 +497,17 @@ func (b *Btelegram) handleEntities(rmsg *config.Message, message *tgbotapi.Messa
|
||||
link := utf16.Decode(utfEncodedString[e.Offset : e.Offset+e.Length])
|
||||
rmsg.Text = strings.Replace(rmsg.Text, string(link), url.String(), 1)
|
||||
}
|
||||
|
||||
if e.Type == "code" {
|
||||
offset := e.Offset + indexMovedBy
|
||||
rmsg.Text = rmsg.Text[:offset] + "`" + rmsg.Text[offset:offset+e.Length] + "`" + rmsg.Text[offset+e.Length:]
|
||||
indexMovedBy += 2
|
||||
}
|
||||
|
||||
if e.Type == "pre" {
|
||||
offset := e.Offset + indexMovedBy
|
||||
rmsg.Text = rmsg.Text[:offset] + "```\n" + rmsg.Text[offset:offset+e.Length] + "\n```" + rmsg.Text[offset+e.Length:]
|
||||
indexMovedBy += 8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/42wim/matterbridge/bridge"
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
"github.com/42wim/matterbridge/bridge/helper"
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -17,8 +17,6 @@ const (
|
||||
HTMLFormat = "HTML"
|
||||
HTMLNick = "htmlnick"
|
||||
MarkdownV2 = "MarkdownV2"
|
||||
FormatPng = "png"
|
||||
FormatWebp = "webp"
|
||||
)
|
||||
|
||||
type Btelegram struct {
|
||||
@@ -32,10 +30,10 @@ func New(cfg *bridge.Config) bridge.Bridger {
|
||||
if tgsConvertFormat != "" {
|
||||
err := helper.CanConvertTgsToX()
|
||||
if err != nil {
|
||||
log.Fatalf("Telegram bridge configured to convert .tgs files to '%s', but lottie does not appear to work:\n%#v", tgsConvertFormat, err)
|
||||
log.Fatalf("Telegram bridge configured to convert .tgs files to '%s', but %s does not appear to work:\n%#v", tgsConvertFormat, helper.LottieBackend(), err)
|
||||
}
|
||||
if tgsConvertFormat != FormatPng && tgsConvertFormat != FormatWebp {
|
||||
log.Fatalf("Telegram bridge configured to convert .tgs files to '%s', but only '%s' and '%s' are supported.", FormatPng, FormatWebp, tgsConvertFormat)
|
||||
if !helper.SupportsFormat(tgsConvertFormat) {
|
||||
log.Fatalf("Telegram bridge configured to convert .tgs files to '%s', but %s doesn't support it.", tgsConvertFormat, helper.LottieBackend())
|
||||
}
|
||||
}
|
||||
return &Btelegram{Config: cfg, avatarMap: make(map[string]string)}
|
||||
@@ -51,11 +49,7 @@ func (b *Btelegram) Connect() error {
|
||||
}
|
||||
u := tgbotapi.NewUpdate(0)
|
||||
u.Timeout = 60
|
||||
updates, err := b.c.GetUpdatesChan(u)
|
||||
if err != nil {
|
||||
b.Log.Debugf("%#v", err)
|
||||
return err
|
||||
}
|
||||
updates := b.c.GetUpdatesChan(u)
|
||||
b.Log.Info("Connection succeeded")
|
||||
go b.handleRecv(updates)
|
||||
return nil
|
||||
|
||||
@@ -34,6 +34,7 @@ type user struct {
|
||||
|
||||
type Bvk struct {
|
||||
c *api.VK
|
||||
lp *longpoll.LongPoll
|
||||
usernamesMap map[int]user // cache of user names and avatar URLs
|
||||
*bridge.Config
|
||||
}
|
||||
@@ -45,21 +46,23 @@ func New(cfg *bridge.Config) bridge.Bridger {
|
||||
func (b *Bvk) Connect() error {
|
||||
b.Log.Info("Connecting")
|
||||
b.c = api.NewVK(b.GetString("Token"))
|
||||
lp, err := longpoll.NewLongPoll(b.c, b.GetInt("GroupID"))
|
||||
|
||||
var err error
|
||||
b.lp, err = longpoll.NewLongPollCommunity(b.c)
|
||||
if err != nil {
|
||||
b.Log.Debugf("%#v", err)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
lp.MessageNew(func(ctx context.Context, obj events.MessageNewObject) {
|
||||
b.lp.MessageNew(func(ctx context.Context, obj events.MessageNewObject) {
|
||||
b.handleMessage(obj.Message, false)
|
||||
})
|
||||
|
||||
b.Log.Info("Connection succeeded")
|
||||
|
||||
go func() {
|
||||
err := lp.Run()
|
||||
err := b.lp.Run()
|
||||
if err != nil {
|
||||
b.Log.Fatal("Enable longpoll in group management")
|
||||
}
|
||||
@@ -69,6 +72,8 @@ func (b *Bvk) Connect() error {
|
||||
}
|
||||
|
||||
func (b *Bvk) Disconnect() error {
|
||||
b.lp.Shutdown()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -175,6 +175,11 @@ func (b *Bwhatsapp) HandleImageMessage(message whatsapp.ImageMessage) {
|
||||
fileExt[0] = ".jpg"
|
||||
}
|
||||
|
||||
// rename .jpe to .jpg https://github.com/42wim/matterbridge/issues/1463
|
||||
if fileExt[0] == ".jpe" {
|
||||
fileExt[0] = ".jpg"
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf("%v%v", message.Info.Id, fileExt[0])
|
||||
|
||||
b.Log.Debugf("Trying to download %s with type %s", filename, message.Type)
|
||||
@@ -232,6 +237,10 @@ func (b *Bwhatsapp) HandleVideoMessage(message whatsapp.VideoMessage) {
|
||||
return
|
||||
}
|
||||
|
||||
if len(fileExt) == 0 {
|
||||
fileExt = append(fileExt, ".mp4")
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf("%v%v", message.Info.Id, fileExt[0])
|
||||
|
||||
b.Log.Debugf("Trying to download %s with size %#v and type %s", filename, message.Length, message.Type)
|
||||
@@ -312,3 +321,60 @@ func (b *Bwhatsapp) HandleAudioMessage(message whatsapp.AudioMessage) {
|
||||
|
||||
b.Remote <- rmsg
|
||||
}
|
||||
|
||||
// HandleDocumentMessage downloads documents
|
||||
func (b *Bwhatsapp) HandleDocumentMessage(message whatsapp.DocumentMessage) {
|
||||
if message.Info.FromMe || message.Info.Timestamp < b.startedAt {
|
||||
return
|
||||
}
|
||||
|
||||
senderJID := message.Info.SenderJid
|
||||
if len(message.Info.SenderJid) == 0 && message.Info.Source != nil && message.Info.Source.Participant != nil {
|
||||
senderJID = *message.Info.Source.Participant
|
||||
}
|
||||
|
||||
senderName := b.getSenderName(message.Info.SenderJid)
|
||||
if senderName == "" {
|
||||
senderName = "Someone" // don't expose telephone number
|
||||
}
|
||||
|
||||
rmsg := config.Message{
|
||||
UserID: senderJID,
|
||||
Username: senderName,
|
||||
Channel: message.Info.RemoteJid,
|
||||
Account: b.Account,
|
||||
Protocol: b.Protocol,
|
||||
Extra: make(map[string][]interface{}),
|
||||
ID: message.Info.Id,
|
||||
}
|
||||
|
||||
if avatarURL, exists := b.userAvatars[senderJID]; exists {
|
||||
rmsg.Avatar = avatarURL
|
||||
}
|
||||
|
||||
fileExt, err := mime.ExtensionsByType(message.Type)
|
||||
if err != nil {
|
||||
b.Log.Errorf("Mimetype detection error: %s", err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf("%v", message.FileName)
|
||||
|
||||
b.Log.Debugf("Trying to download %s with extension %s and type %s", filename, fileExt, message.Type)
|
||||
|
||||
data, err := message.Download()
|
||||
if err != nil {
|
||||
b.Log.Errorf("Download document message failed: %s", err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Move file to bridge storage
|
||||
helper.HandleDownloadData(b.Log, &rmsg, filename, "document", "", &data, b.General)
|
||||
|
||||
b.Log.Debugf("<= Sending message from %s on %s to gateway", senderJID, b.Account)
|
||||
b.Log.Debugf("<= Message is %#v", rmsg)
|
||||
|
||||
b.Remote <- rmsg
|
||||
}
|
||||
|
||||
@@ -128,7 +128,6 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) {
|
||||
var msgReplaceID string
|
||||
msgID := xid.New().String()
|
||||
if msg.ID != "" {
|
||||
msgID = msg.ID
|
||||
msgReplaceID = msg.ID
|
||||
}
|
||||
b.Log.Debugf("=> Sending message %#v", msg)
|
||||
@@ -169,11 +168,21 @@ func (b *Bxmpp) postSlackCompatibleWebhook(msg config.Message) error {
|
||||
}
|
||||
|
||||
func (b *Bxmpp) createXMPP() error {
|
||||
if !strings.Contains(b.GetString("Jid"), "@") {
|
||||
return fmt.Errorf("the Jid %s doesn't contain an @", b.GetString("Jid"))
|
||||
var serverName string
|
||||
switch {
|
||||
case !b.GetBool("Anonymous"):
|
||||
if !strings.Contains(b.GetString("Jid"), "@") {
|
||||
return fmt.Errorf("the Jid %s doesn't contain an @", b.GetString("Jid"))
|
||||
}
|
||||
serverName = strings.Split(b.GetString("Jid"), "@")[1]
|
||||
case !strings.Contains(b.GetString("Server"), ":"):
|
||||
serverName = strings.Split(b.GetString("Server"), ":")[0]
|
||||
default:
|
||||
serverName = b.GetString("Server")
|
||||
}
|
||||
|
||||
tc := &tls.Config{
|
||||
ServerName: strings.Split(b.GetString("Jid"), "@")[1],
|
||||
ServerName: serverName,
|
||||
InsecureSkipVerify: b.GetBool("SkipTLSVerify"), // nolint: gosec
|
||||
}
|
||||
|
||||
@@ -274,7 +283,13 @@ func (b *Bxmpp) handleXMPP() error {
|
||||
for {
|
||||
m, err := b.xc.Recv()
|
||||
if err != nil {
|
||||
return err
|
||||
// An error together with AvatarData is non-fatal
|
||||
switch m.(type) {
|
||||
case xmpp.AvatarData:
|
||||
continue
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
switch v := m.(type) {
|
||||
@@ -385,7 +400,7 @@ func (b *Bxmpp) handleUploadFile(msg *config.Message) error {
|
||||
|
||||
func (b *Bxmpp) parseNick(remote string) string {
|
||||
s := strings.Split(remote, "@")
|
||||
if len(s) > 0 {
|
||||
if len(s) > 1 {
|
||||
s = strings.Split(s[1], "/")
|
||||
if len(s) == 2 {
|
||||
return s[1] // nick
|
||||
|
||||
@@ -2,6 +2,7 @@ package bzulip
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
"github.com/42wim/matterbridge/bridge"
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
"github.com/42wim/matterbridge/bridge/helper"
|
||||
"github.com/42wim/matterbridge/version"
|
||||
gzb "github.com/matterbridge/gozulipbot"
|
||||
)
|
||||
|
||||
@@ -27,7 +29,7 @@ func New(cfg *bridge.Config) bridge.Bridger {
|
||||
}
|
||||
|
||||
func (b *Bzulip) Connect() error {
|
||||
bot := gzb.Bot{APIKey: b.GetString("token"), APIURL: b.GetString("server") + "/api/v1/", Email: b.GetString("login")}
|
||||
bot := gzb.Bot{APIKey: b.GetString("token"), APIURL: b.GetString("server") + "/api/v1/", Email: b.GetString("login"), UserAgent: fmt.Sprintf("matterbridge/%s", version.Release)}
|
||||
bot.Init()
|
||||
q, err := bot.RegisterAll()
|
||||
b.q = q
|
||||
@@ -125,6 +127,7 @@ func (b *Bzulip) handleQueue() error {
|
||||
b.Log.Debug("heartbeat received.")
|
||||
default:
|
||||
b.Log.Debugf("receiving error: %#v", err)
|
||||
time.Sleep(time.Second * 10)
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
|
||||
79
changelog.md
@@ -1,3 +1,82 @@
|
||||
# v1.23.2
|
||||
|
||||
If you're running whatsapp you should update.
|
||||
|
||||
## Bugfix
|
||||
|
||||
- whatsapp: Update go-whatsapp version (#1630)
|
||||
|
||||
# v1.23.1
|
||||
|
||||
If you're running mattermost 6 you should update.
|
||||
|
||||
## Bugfix
|
||||
|
||||
- mattermost: Do not check cache on deleted messages (mattermost). Fixes #1555 (#1624)
|
||||
- mattermost: Fix crash on users updating info. Update matterclient dep. Fixes #1617
|
||||
- matrix: Keep the logger on a disabled bridge. Fixes #1616 (#1621)
|
||||
- msteams: Fix panic in msteams. Fixes #1588 (#1622)
|
||||
- xmpp: Do not fail on no avatar data (xmpp) #1529 (#1627)
|
||||
- xmpp: Use a new msgID when replacing messages (xmpp). Fixes #1584 (#1623)
|
||||
- zulip: Add better error handling on Zulip (#1589)
|
||||
|
||||
# v1.23.0
|
||||
|
||||
## New features
|
||||
|
||||
- irc: Add UserName and RealName options for IRC (#1590)
|
||||
- mattermost: Add support for mattermost v6
|
||||
- nctalk: Add support for separate display name (nctalk) (#1506)
|
||||
- xmpp: Add support for anonymous connection (xmpp) (#1548)
|
||||
|
||||
## Enhancements
|
||||
|
||||
- general: Update vendored libraries
|
||||
- docker: Use github actions to build dockerhub/ghcr.io images
|
||||
- docker: Update GH actions to multi arch (arm64) (#1614)
|
||||
- telegram: Convert .tgs with go libraries (and cgo) (telegram) (#1569)
|
||||
|
||||
## Bugfix
|
||||
|
||||
- mumble: Remove newline character in bridge multiline messages (mumble) (#1572)
|
||||
- slack: Add space before file upload comment (slack) (#1554)
|
||||
- slack: Invalidate user in cache on user change event (#1604)
|
||||
- xmpp: Fix XMPP parseNick function (#1547)
|
||||
|
||||
This release couldn't exist without the following contributors:
|
||||
@powerjungle, @gary-kim, @KingPin, @Benau, @keenan-v1, @tytan652, @KidA001,@minecraftchest1, @irydacea
|
||||
|
||||
# v1.22.3
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- whatsapp: Update Rhymen/go-whatsapp module to latest master (2b8a3e9b8aa2) (#1518)
|
||||
|
||||
This release couldn't exist without the following contributors:
|
||||
@nathanaelhoun
|
||||
|
||||
# v1.22.2
|
||||
|
||||
## Enhancements
|
||||
|
||||
- general: Add a MessageClipped option to set your own clipped message. Closes #1359 (#1487)
|
||||
- discord: Add AllowMention to restrict allowed mentions (#1462)
|
||||
- matrix: Add MxId/Token login option for Matrix (#1438)
|
||||
- nctalk: Support sending file URLs (nctalk) (#1489)
|
||||
- nctalk: Add support for message deletion (nctalk) (#1492)
|
||||
- whatsapp: Handle document messages (whatsapp) (#1475)
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- general: Update vendored libs
|
||||
- matrix: Fix content body issue for redactions (matrix) (#1496)
|
||||
- telegram: Add libwebp-dev to tgs.Dockerfile fixes Telegram sticker to WebP rendering (#1476)
|
||||
- whatsapp: Rename .jpe files to .jpg Fixes #1463 (whatsapp) (#1485)
|
||||
- whatsapp: Fix crash on encountering VideoMessage (whatsapp) (#1483)
|
||||
|
||||
This release couldn't exist without the following contributors:
|
||||
@AvinashReddy3108, @chrisbobbe, @jaywink, @Funatiker, @computeronix, @alexandregv, @gary-kim, @SuperSandro2000
|
||||
|
||||
# v1.22.1
|
||||
|
||||
## Enhancements
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
FROM alpine:edge as certs
|
||||
RUN apk --update add ca-certificates
|
||||
ARG VERSION=1.22.3
|
||||
ADD https://github.com/42wim/matterbridge/releases/download/v${VERSION}/matterbridge-${VERSION}-linux-arm64 /bin/matterbridge
|
||||
RUN chmod +x /bin/matterbridge
|
||||
|
||||
FROM scratch
|
||||
ARG VERSION=1.12.3
|
||||
COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
ADD https://github.com/42wim/matterbridge/releases/download/v${VERSION}/matterbridge-linux-arm /bin/matterbridge
|
||||
RUN chmod +x /bin/matterbridge
|
||||
COPY --from=certs /bin/matterbridge /bin/matterbridge
|
||||
ENTRYPOINT ["/bin/matterbridge"]
|
||||
|
||||
12
gateway/bridgemap/bharmony.go
Normal file
@@ -0,0 +1,12 @@
|
||||
//go:build !noharmony
|
||||
// +build !noharmony
|
||||
|
||||
package bridgemap
|
||||
|
||||
import (
|
||||
bharmony "github.com/42wim/matterbridge/bridge/harmony"
|
||||
)
|
||||
|
||||
func init() {
|
||||
FullMap["harmony"] = bharmony.New
|
||||
}
|
||||
@@ -66,7 +66,7 @@ func New(rootLogger *logrus.Logger, cfg *config.Gateway, r *Router) *Gateway {
|
||||
func (gw *Gateway) FindCanonicalMsgID(protocol string, mID string) string {
|
||||
ID := protocol + " " + mID
|
||||
if gw.Messages.Contains(ID) {
|
||||
return mID
|
||||
return ID
|
||||
}
|
||||
|
||||
// If not keyed, iterate through cache for downstream, and infer upstream.
|
||||
@@ -75,7 +75,7 @@ func (gw *Gateway) FindCanonicalMsgID(protocol string, mID string) string {
|
||||
ids := v.([]*BrMsgID)
|
||||
for _, downstreamMsgObj := range ids {
|
||||
if ID == downstreamMsgObj.ID {
|
||||
return strings.Replace(mid.(string), protocol+" ", "", 1)
|
||||
return mid.(string)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -447,16 +447,19 @@ func (gw *Gateway) SendMessage(
|
||||
msg.Avatar = gw.modifyAvatar(rmsg, dest)
|
||||
msg.Username = gw.modifyUsername(rmsg, dest)
|
||||
|
||||
msg.ID = gw.getDestMsgID(rmsg.Protocol+" "+rmsg.ID, dest, channel)
|
||||
// exclude file delete event as the msg ID here is the native file ID that needs to be deleted
|
||||
if msg.Event != config.EventFileDelete {
|
||||
msg.ID = gw.getDestMsgID(rmsg.Protocol+" "+rmsg.ID, dest, channel)
|
||||
}
|
||||
|
||||
// for api we need originchannel as channel
|
||||
if dest.Protocol == apiProtocol {
|
||||
msg.Channel = rmsg.Channel
|
||||
}
|
||||
|
||||
msg.ParentID = gw.getDestMsgID(rmsg.Protocol+" "+canonicalParentMsgID, dest, channel)
|
||||
msg.ParentID = gw.getDestMsgID(canonicalParentMsgID, dest, channel)
|
||||
if msg.ParentID == "" {
|
||||
msg.ParentID = canonicalParentMsgID
|
||||
msg.ParentID = strings.Replace(canonicalParentMsgID, dest.Protocol+" ", "", 1)
|
||||
}
|
||||
|
||||
// if the parentID is still empty and we have a parentID set in the original message
|
||||
|
||||
@@ -110,7 +110,9 @@ func (r *Router) disableBridge(br *bridge.Bridge, err error) bool {
|
||||
if r.BridgeValues().General.IgnoreFailureOnStart {
|
||||
r.logger.Error(err)
|
||||
// setting this bridge empty
|
||||
*br = bridge.Bridge{}
|
||||
*br = bridge.Bridge{
|
||||
Log: br.Log,
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
138
go.mod
@@ -3,37 +3,36 @@ module github.com/42wim/matterbridge
|
||||
require (
|
||||
github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557
|
||||
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f
|
||||
github.com/Jeffail/gabs v1.4.0 // indirect
|
||||
github.com/Benau/tgsconverter v0.0.0-20210809170556-99f4a4f6337f
|
||||
github.com/Philipp15b/go-steam v1.0.1-0.20200727090957-6ae9b3c0a560
|
||||
github.com/Rhymen/go-whatsapp v0.1.2-0.20210126174449-3c094ebae0ce
|
||||
github.com/SevereCloud/vksdk/v2 v2.9.0
|
||||
github.com/d5/tengo/v2 v2.7.0
|
||||
github.com/Rhymen/go-whatsapp v0.1.2-0.20211102134409-31a2e740845c
|
||||
github.com/SevereCloud/vksdk/v2 v2.13.0
|
||||
github.com/d5/tengo/v2 v2.10.0
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v1.0.1-0.20200524105306-7434b0456e81
|
||||
github.com/gomarkdown/markdown v0.0.0-20210208175418-bda154fe17d8
|
||||
github.com/google/gops v0.3.17
|
||||
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.1
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
|
||||
github.com/gomarkdown/markdown v0.0.0-20211207152620-5d6539fd8bfc
|
||||
github.com/google/gops v0.3.22
|
||||
github.com/gorilla/schema v1.2.0
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/harmony-development/shibshib v0.0.0-20211127182844-512296f7c548
|
||||
github.com/hashicorp/golang-lru v0.5.4
|
||||
github.com/jpillora/backoff v1.0.0
|
||||
github.com/keybase/go-keybase-chat-bot v0.0.0-20200505163032-5cacf52379da
|
||||
github.com/keybase/go-keybase-chat-bot v0.0.0-20211201215354-ee4b23828b55
|
||||
github.com/kyokomi/emoji/v2 v2.2.8
|
||||
github.com/labstack/echo/v4 v4.2.1
|
||||
github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7
|
||||
github.com/labstack/echo/v4 v4.6.3
|
||||
github.com/lrstanley/girc v0.0.0-20211023233735-147f0ff77566
|
||||
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16
|
||||
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20210403163225-761e8622445d
|
||||
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20211016222428-79310a412696
|
||||
github.com/matterbridge/discordgo v0.21.2-0.20210201201054-fb39a175b4f7
|
||||
github.com/matterbridge/go-xmpp v0.0.0-20200418225040-c8a3a57b4050
|
||||
github.com/matterbridge/gozulipbot v0.0.0-20200820220548-be5824faa913
|
||||
github.com/matterbridge/go-xmpp v0.0.0-20211030125215-791a06c5f1be
|
||||
github.com/matterbridge/gozulipbot v0.0.0-20211023205727-a19d6c1f3b75
|
||||
github.com/matterbridge/logrus-prefixed-formatter v0.5.3-0.20200523233437-d971309a77ba
|
||||
github.com/mattermost/mattermost-server/v5 v5.30.1
|
||||
github.com/matterbridge/matterclient v0.0.0-20211107234719-faca3cd42315
|
||||
github.com/mattermost/mattermost-server/v5 v5.39.3
|
||||
github.com/mattermost/mattermost-server/v6 v6.3.0
|
||||
github.com/mattn/godown v0.0.1
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/missdeer/golib v1.0.4
|
||||
github.com/mreiferson/go-httpclient v0.0.0-20201222173833-5e475fde3a4d // indirect
|
||||
github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff // indirect
|
||||
github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9
|
||||
github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c
|
||||
github.com/rs/xid v1.3.0
|
||||
@@ -41,21 +40,102 @@ require (
|
||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca
|
||||
github.com/shazow/ssh-chat v1.10.1
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/slack-go/slack v0.8.2
|
||||
github.com/spf13/afero v1.3.4 // indirect
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/spf13/viper v1.7.1
|
||||
github.com/slack-go/slack v0.10.0
|
||||
github.com/spf13/viper v1.10.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/vincent-petithory/dataurl v0.0.0-20191104211930-d1553a71de50
|
||||
github.com/vincent-petithory/dataurl v1.0.0
|
||||
github.com/writeas/go-strip-markdown v2.0.1+incompatible
|
||||
github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect
|
||||
github.com/yaegashi/msgraph.go v0.1.4
|
||||
github.com/zfjagann/golang-ring v0.0.0-20210116075443-7c86fdb43134
|
||||
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb
|
||||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602
|
||||
gomod.garykim.dev/nc-talk v0.1.7
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
|
||||
gomod.garykim.dev/nc-talk v0.3.0
|
||||
gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376
|
||||
layeh.com/gumble v0.0.0-20200818122324-146f9205029b
|
||||
)
|
||||
|
||||
go 1.15
|
||||
require (
|
||||
github.com/Benau/go_rlottie v0.0.0-20210807002906-98c1b2421989 // indirect
|
||||
github.com/Jeffail/gabs v1.4.0 // indirect
|
||||
github.com/apex/log v1.9.0 // indirect
|
||||
github.com/av-elier/go-decimal-to-rational v0.0.0-20191127152832-89e6aad02ecf // indirect
|
||||
github.com/blang/semver v3.5.1+incompatible // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/dyatlov/go-opengraph v0.0.0-20210112100619-dae8665a5b09 // indirect
|
||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/gopackage/ddp v0.0.3 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kettek/apng v0.0.0-20191108220231-414630eed80f // indirect
|
||||
github.com/klauspost/compress v1.14.1 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
||||
github.com/labstack/gommon v0.3.1 // indirect
|
||||
github.com/magiconair/properties v1.8.5 // indirect
|
||||
github.com/mattermost/go-i18n v1.11.1-0.20211013152124-5c415071e404 // indirect
|
||||
github.com/mattermost/ldap v0.0.0-20201202150706-ee0e6284187d // indirect
|
||||
github.com/mattermost/logr v1.0.13 // indirect
|
||||
github.com/mattermost/logr/v2 v2.0.15 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/minio-go/v7 v7.0.16 // indirect
|
||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/monaco-io/request v1.0.5 // indirect
|
||||
github.com/mreiferson/go-httpclient v0.0.0-20201222173833-5e475fde3a4d // indirect
|
||||
github.com/mrexodia/wray v0.0.0-20160318003008-78a2c1f284ff // indirect
|
||||
github.com/pborman/uuid v1.2.1 // indirect
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
github.com/philhofer/fwd v1.1.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rickb777/date v1.12.4 // indirect
|
||||
github.com/rickb777/plural v1.2.0 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/shazow/rateio v0.0.0-20200113175441-4461efc8bdc4 // indirect
|
||||
github.com/sizeofint/webpanimation v0.0.0-20210809145948-1d2b32119882 // indirect
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||
github.com/spf13/afero v1.6.0 // indirect
|
||||
github.com/spf13/cast v1.4.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.2.0 // indirect
|
||||
github.com/tinylib/msgp v1.1.6 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.1 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/wiggin77/cfg v1.0.2 // indirect
|
||||
github.com/wiggin77/merror v1.0.3 // indirect
|
||||
github.com/wiggin77/srslog v1.0.1 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.7.0 // indirect
|
||||
go.uber.org/zap v1.17.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 // indirect
|
||||
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 // indirect
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/ini.v1 v1.66.2 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
)
|
||||
|
||||
replace github.com/matrix-org/gomatrix => github.com/matterbridge/gomatrix v0.0.0-20220205235239-607eb9ee6419
|
||||
|
||||
go 1.17
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// Message for rocketchat outgoing webhook.
|
||||
@@ -68,7 +69,6 @@ func (c *Client) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
msg := Message{}
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
log.Println(string(body))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.NotFound(w, r)
|
||||
@@ -89,7 +89,11 @@ func (c *Client) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
msg.ChannelName = "#" + msg.ChannelName
|
||||
if c.Token != "" {
|
||||
if msg.Token != c.Token {
|
||||
log.Println("invalid token " + msg.Token + " from " + r.RemoteAddr)
|
||||
if regexp.MustCompile(`[^a-zA-Z0-9]+`).MatchString(msg.Token) {
|
||||
log.Println("invalid token " + msg.Token + " from " + r.RemoteAddr)
|
||||
} else {
|
||||
log.Println("invalid token from " + r.RemoteAddr)
|
||||
}
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 270 KiB |
|
Before Width: | Height: | Size: 170 KiB |
|
Before Width: | Height: | Size: 282 KiB |
|
Before Width: | Height: | Size: 204 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 62 KiB |
@@ -10,15 +10,13 @@ import (
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
"github.com/42wim/matterbridge/gateway"
|
||||
"github.com/42wim/matterbridge/gateway/bridgemap"
|
||||
"github.com/42wim/matterbridge/version"
|
||||
"github.com/google/gops/agent"
|
||||
prefixed "github.com/matterbridge/logrus-prefixed-formatter"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
version = "1.22.1"
|
||||
githash string
|
||||
|
||||
flagConfig = flag.String("conf", "matterbridge.toml", "config file")
|
||||
flagDebug = flag.Bool("debug", false, "enable debug")
|
||||
flagVersion = flag.Bool("version", false, "show version")
|
||||
@@ -28,7 +26,7 @@ var (
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if *flagVersion {
|
||||
fmt.Printf("version: %s %s\n", version, githash)
|
||||
fmt.Printf("version: %s %s\n", version.Release, version.GitHash)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -43,8 +41,8 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
logger.Printf("Running version %s %s", version, githash)
|
||||
if strings.Contains(version, "-dev") {
|
||||
logger.Printf("Running version %s %s", version.Release, version.GitHash)
|
||||
if strings.Contains(version.Release, "-dev") {
|
||||
logger.Println("WARNING: THIS IS A DEVELOPMENT VERSION. Things may break.")
|
||||
}
|
||||
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
[irc]
|
||||
|
||||
#You can configure multiple servers "[irc.name]" or "[irc.name2]"
|
||||
#In this example we use [irc.freenode]
|
||||
#In this example we use [irc.libera]
|
||||
#REQUIRED
|
||||
[irc.freenode]
|
||||
[irc.libera]
|
||||
#irc server to connect to.
|
||||
#REQUIRED
|
||||
Server="irc.freenode.net:6667"
|
||||
Server="irc.libera.chat:6667"
|
||||
|
||||
#Password for irc server (if necessary)
|
||||
#OPTIONAL (default "")
|
||||
@@ -24,7 +24,14 @@ Password=""
|
||||
#OPTIONAL (default false)
|
||||
UseTLS=false
|
||||
|
||||
#Enable SASL (PLAIN) authentication. (freenode requires this from eg AWS hosts)
|
||||
#Use client certificate - see CertFP https://libera.chat/guides/certfp.html
|
||||
#Specify filename which contains private key and cert
|
||||
#OPTIONAL (default "")
|
||||
#
|
||||
#TLSClientCertificate="cert.pem"
|
||||
TLSClientCertificate=""
|
||||
|
||||
#Enable SASL (PLAIN) authentication. (libera requires this from eg AWS hosts)
|
||||
#It uses NickServNick and NickServPassword as login and password
|
||||
#OPTIONAL (default false)
|
||||
UseSASL=false
|
||||
@@ -34,6 +41,11 @@ UseSASL=false
|
||||
#OPTIONAL (default false)
|
||||
SkipTLSVerify=true
|
||||
|
||||
#Local address to use for server connection
|
||||
#Note that Server and Bind must resolve to addresses of the same family.
|
||||
#OPTIONAL (default "")
|
||||
Bind=""
|
||||
|
||||
#If you know your charset, you can specify it manually.
|
||||
#Otherwise it tries to detect this automatically. Select one below
|
||||
# "iso-8859-2:1987", "iso-8859-9:1989", "866", "latin9", "iso-8859-10:1992", "iso-ir-109", "hebrew",
|
||||
@@ -55,7 +67,15 @@ Charset=""
|
||||
#REQUIRED
|
||||
Nick="matterbot"
|
||||
|
||||
#If you registered your bot with a service like Nickserv on freenode.
|
||||
#Real name/gecos displayed in e.g. /WHOIS and /WHO
|
||||
#OPTIONAL (defaults to the nick)
|
||||
RealName="Matterbridge instance on IRC"
|
||||
|
||||
#IRC username/ident preceding the hostname in hostmasks and /WHOIS
|
||||
#OPTIONAL (defaults to the nick)
|
||||
UserName="bridge"
|
||||
|
||||
#If you registered your bot with a service like Nickserv on libera.
|
||||
#Also being used when UseSASL=true
|
||||
#
|
||||
#Note: if you want do to quakenet auth, set NickServNick="Q@CServe.quakenet.org"
|
||||
@@ -76,20 +96,24 @@ MessageDelay=1300
|
||||
|
||||
#Maximum amount of messages to hold in queue. If queue is full
|
||||
#messages will be dropped.
|
||||
#<message clipped> will be add to the message that fills the queue.
|
||||
#<clipped message> will be add to the message that fills the queue.
|
||||
#OPTIONAL (default 30)
|
||||
MessageQueue=30
|
||||
|
||||
#Maximum length of message sent to irc server. If it exceeds
|
||||
#<message clipped> will be add to the message.
|
||||
#<clipped message> will be add to the message.
|
||||
#OPTIONAL (default 400)
|
||||
MessageLength=400
|
||||
|
||||
#Split messages on MessageLength instead of showing the <message clipped>
|
||||
#Split messages on MessageLength instead of showing the <clipped message>
|
||||
#WARNING: this could lead to flooding
|
||||
#OPTIONAL (default false)
|
||||
MessageSplit=false
|
||||
|
||||
#Message to show when a message is too big
|
||||
#Default "<clipped message>"
|
||||
MessageClipped="<clipped message>"
|
||||
|
||||
#Delay in seconds to rejoin a channel when kicked
|
||||
#OPTIONAL (default 0)
|
||||
RejoinDelay=0
|
||||
@@ -219,12 +243,16 @@ UseRelayMsg=false
|
||||
#REQUIRED
|
||||
Server="jabber.example.com:5222"
|
||||
|
||||
#Use anonymous MUC login
|
||||
#OPTIONAL (default false)
|
||||
Anonymous=false
|
||||
|
||||
#Jid
|
||||
#REQUIRED
|
||||
#REQUIRED if Anonymous=false
|
||||
Jid="user@example.com"
|
||||
|
||||
#Password
|
||||
#REQUIRED
|
||||
#REQUIRED if Anonymous=false
|
||||
Password="yourpass"
|
||||
|
||||
#MUC
|
||||
@@ -380,6 +408,10 @@ SkipTLSVerify=true
|
||||
## RELOADABLE SETTINGS
|
||||
## Settings below can be reloaded by editing the file
|
||||
|
||||
# UseUserName shows the username instead of the server nickname
|
||||
# OPTIONAL (default false)
|
||||
UseUserName=false
|
||||
|
||||
#how to format the list of IRC nicks when displayed in mattermost.
|
||||
#Possible options are "table" and "plain"
|
||||
#OPTIONAL (default plain)
|
||||
@@ -826,6 +858,10 @@ PreserveThreading=false
|
||||
#OPTIONAL (default false)
|
||||
ShowUserTyping=false
|
||||
|
||||
#Message to show when a message is too big
|
||||
#Default "<clipped message>"
|
||||
MessageClipped="<clipped message>"
|
||||
|
||||
###################################################################
|
||||
#discord section
|
||||
###################################################################
|
||||
@@ -848,6 +884,14 @@ Server="yourservername"
|
||||
## All settings below can be reloaded by editing the file.
|
||||
## They are also all optional.
|
||||
|
||||
# AllowMention controls which mentions are allowed. If not specified, all mentions are allowed.
|
||||
# Note that even when a mention is not allowed, it will still be displayed nicely and be clickable. It just prevents the ping/notification.
|
||||
#
|
||||
# "everyone" allows @everyone and @here mentions
|
||||
# "roles" allows @role mentions
|
||||
# "users" allows @user mentions
|
||||
AllowMention=["everyone", "roles", "users"]
|
||||
|
||||
# ShowEmbeds shows the title, description and URL of embedded messages (sent by other bots)
|
||||
ShowEmbeds=false
|
||||
|
||||
@@ -953,6 +997,10 @@ ShowTopicChange=false
|
||||
# Supported from the following bridges: slack
|
||||
SyncTopic=false
|
||||
|
||||
#Message to show when a message is too big
|
||||
#Default "<clipped message>"
|
||||
MessageClipped="<clipped message>"
|
||||
|
||||
###################################################################
|
||||
#telegram section
|
||||
###################################################################
|
||||
@@ -1011,6 +1059,13 @@ QuoteFormat="{MESSAGE} (re @{QUOTENICK}: {QUOTEMESSAGE})"
|
||||
#OPTIONAL (default false)
|
||||
MediaConvertWebPToPNG=false
|
||||
|
||||
#Convert Tgs (Telegram animated sticker) images to PNG before upload.
|
||||
#This is useful when your bridge also contains platforms that do not support animated WebP files, like Discord.
|
||||
#This requires the external dependency `lottie`, which can be installed like this:
|
||||
#`pip install lottie cairosvg`
|
||||
#https://github.com/42wim/matterbridge/issues/874
|
||||
#MediaConvertTgs="png"
|
||||
|
||||
#Disable sending of edits to other bridges
|
||||
#OPTIONAL (default false)
|
||||
EditDisable=false
|
||||
@@ -1230,12 +1285,16 @@ ShowTopicChange=false
|
||||
#REQUIRED
|
||||
Server="https://matrix.org"
|
||||
|
||||
#login/pass of your bot.
|
||||
#Authentication for your bot.
|
||||
#You can use either login/password OR mxid/token. The latter will be preferred if found.
|
||||
#Use a dedicated user for this and not your own!
|
||||
#Messages sent from this user will not be relayed to avoid loops.
|
||||
#REQUIRED
|
||||
Login="yourlogin"
|
||||
Password="yourpass"
|
||||
#OR
|
||||
MxID="@yourlogin:domain.tld"
|
||||
Token="tokenforthebotuser"
|
||||
|
||||
#Whether to send the homeserver suffix. eg ":matrix.org" in @username:matrix.org
|
||||
#to other bridges, or only send "username".(true only sends username)
|
||||
@@ -1253,13 +1312,6 @@ HTMLDisable=false
|
||||
# UseUserName shows the username instead of the server nickname
|
||||
UseUserName=false
|
||||
|
||||
#Whether to prefix messages from other bridges to matrix with the sender's nick.
|
||||
#Useful if username overrides for incoming webhooks isn't enabled on the
|
||||
#matrix server. If you set PrefixMessagesWithNick to true, each message
|
||||
#from bridge to matrix will by default be prefixed by the RemoteNickFormat setting. i
|
||||
#OPTIONAL (default false)
|
||||
PrefixMessagesWithNick=false
|
||||
|
||||
#Nicks you want to ignore.
|
||||
#Regular expressions supported
|
||||
#Messages from those users will not be sent to other bridges.
|
||||
@@ -1416,9 +1468,7 @@ StripNick=false
|
||||
ShowTopicChange=false
|
||||
|
||||
###################################################################
|
||||
#
|
||||
# NCTalk (Nextcloud Talk)
|
||||
#
|
||||
###################################################################
|
||||
|
||||
[nctalk.bridge]
|
||||
@@ -1440,10 +1490,11 @@ Password = "talkuserpass"
|
||||
# Suffix for Guest Users
|
||||
GuestSuffix = " (Guest)"
|
||||
|
||||
# Separate display name (Note: needs to be configured from Nextcloud Talk to work)
|
||||
SeparateDisplayName=false
|
||||
|
||||
###################################################################
|
||||
#
|
||||
# Mumble
|
||||
#
|
||||
###################################################################
|
||||
|
||||
[mumble.bridge]
|
||||
@@ -1486,22 +1537,21 @@ TLSCACertificate=mumble-ca.crt
|
||||
# OPTIONAL (default false)
|
||||
SkipTLSVerify=false
|
||||
|
||||
#Message to show when a message is too big
|
||||
#Default "<clipped message>"
|
||||
MessageClipped="<clipped message>"
|
||||
|
||||
###################################################################
|
||||
#VK
|
||||
###################################################################
|
||||
#
|
||||
[vk.myvk]
|
||||
#Group access token
|
||||
#See https://vk.com/dev/bots_docs
|
||||
Token="Yourtokenhere"
|
||||
|
||||
#Group ID
|
||||
#For example in URL https://vk.com/public168963511 group ID is 168963511
|
||||
GroupID=123456789
|
||||
|
||||
###################################################################
|
||||
#
|
||||
# WhatsApp
|
||||
#
|
||||
###################################################################
|
||||
|
||||
[whatsapp.bridge]
|
||||
@@ -1528,9 +1578,7 @@ Label="Organization"
|
||||
|
||||
|
||||
###################################################################
|
||||
#
|
||||
# zulip
|
||||
#
|
||||
###################################################################
|
||||
|
||||
[zulip]
|
||||
@@ -1619,6 +1667,18 @@ StripNick=false
|
||||
#OPTIONAL (default false)
|
||||
ShowTopicChange=false
|
||||
|
||||
###################################################################
|
||||
# Harmony
|
||||
###################################################################
|
||||
|
||||
[harmony.chat_harmonyapp_io]
|
||||
Homeserver = "https://chat.harmonyapp.io:2289"
|
||||
Token = "your token goes here"
|
||||
UserID = "user id of the bot account"
|
||||
Community = "community id that channels will be located in"
|
||||
UseUserName = true
|
||||
RemoteNickFormat = "{NICK}"
|
||||
|
||||
###################################################################
|
||||
#API
|
||||
###################################################################
|
||||
@@ -1664,6 +1724,7 @@ RemoteNickFormat="{NICK}"
|
||||
|
||||
#RemoteNickFormat defines how remote users appear on this bridge
|
||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick.
|
||||
#The string "{NOPINGNICK}" (case sensitive) will be replaced by the actual nick / username, but with a ZWSP inside the nick, so the irc user with the same nick won't get pinged.
|
||||
#The string "{USERID}" (case sensitive) will be replaced by the user ID.
|
||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||
#The string "{LABEL}" (case sensitive) will be replaced by label= field of the sending bridge
|
||||
@@ -1690,7 +1751,7 @@ StripNick=false
|
||||
#The MediaServerDownload will be used so that bridges without native uploading support:
|
||||
#gitter, irc and xmpp will be shown links to the files on MediaServerDownload
|
||||
#
|
||||
#More information https://github.com/42wim/matterbridge/wiki/Mediaserver-setup-%5Badvanced%5D
|
||||
#More information https://github.com/42wim/matterbridge/wiki/Mediaserver-setup-%28advanced%29
|
||||
#OPTIONAL (default empty)
|
||||
MediaServerUpload="https://user:pass@yourserver.com/upload"
|
||||
#OPTIONAL (default empty)
|
||||
@@ -1819,7 +1880,7 @@ enable=true
|
||||
|
||||
# account specified above
|
||||
# REQUIRED
|
||||
account="irc.freenode"
|
||||
account="irc.libera"
|
||||
|
||||
# The channel key in each gateway is mapped to a similar group chat ID on the chat platform
|
||||
# To find the group chat ID for different platforms, refer to the table below
|
||||
@@ -1836,7 +1897,8 @@ enable=true
|
||||
# -------------------------------------------------------------------------------------------------------------------------------------
|
||||
# irc | channel | #general | The # symbol is required and should be lowercase!
|
||||
# -------------------------------------------------------------------------------------------------------------------------------------
|
||||
# mattermost | channel | general | This is the channel name as seen in the URL, not the display name
|
||||
# | channel | general | This is the channel name as seen in the URL, not the display name
|
||||
# mattermost | channel id | ID:oc4wifyuojgw5f3nsuweesmz8w | This is the channel ID (only use if you know what you're doing)
|
||||
# -------------------------------------------------------------------------------------------------------------------------------------
|
||||
# matrix | #channel:server | #yourchannel:matrix.org | Encrypted rooms are not supported in matrix
|
||||
# -------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -1855,14 +1917,14 @@ enable=true
|
||||
# -------------------------------------------------------------------------------------------------------------------------------------
|
||||
# telegram | chatid | -123456789 | A large negative number. see https://www.linkedin.com/pulse/telegram-bots-beginners-marco-frau
|
||||
# -------------------------------------------------------------------------------------------------------------------------------------
|
||||
# vk | peerid | 2000000002 | A number that starts form 2000000000. Use --debug and send any message in chat to get PeerID in the logs
|
||||
# vk | peerid | 2000000002 | A number that starts form 2000000000. Use --debug and send any message in chat to get PeerID in the logs
|
||||
# -------------------------------------------------------------------------------------------------------------------------------------
|
||||
# whatsapp | group JID | 48111222333-123455678999@g.us | A unique group JID. If you specify an empty string, bridge will list all the possibilities
|
||||
# | "Group Name" | "Family Chat" | if you specify a group name, the bridge will find hint the JID to specify. Names can change over time and are not stable.
|
||||
# -------------------------------------------------------------------------------------------------------------------------------------
|
||||
# xmpp | channel | general | The room name
|
||||
# -------------------------------------------------------------------------------------------------------------------------------------
|
||||
# zulip | stream/topic:topic | general/off-topic:food | Do not use the # when specifying a topic
|
||||
# zulip | stream/topic:topic | general/topic:food | Do not use the # when specifying a topic
|
||||
# -------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
#
|
||||
@@ -1877,7 +1939,7 @@ enable=true
|
||||
|
||||
#[[gateway.out]] specifies the account and channels we will sent messages to.
|
||||
[[gateway.out]]
|
||||
account="irc.freenode"
|
||||
account="irc.libera"
|
||||
channel="#testing"
|
||||
|
||||
#OPTIONAL - only used for IRC and XMPP protocols at the moment
|
||||
@@ -1911,6 +1973,10 @@ enable=true
|
||||
account="zulip.streamchat"
|
||||
channel="general/topic:mytopic"
|
||||
|
||||
[[gateway.inout]]
|
||||
account="harmony.chat_harmonyapp_io"
|
||||
channel="channel id goes here"
|
||||
|
||||
#API example
|
||||
#[[gateway.inout]]
|
||||
#account="api.local"
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
func (m *MMClient) parseActionPost(rmsg *Message) {
|
||||
// add post to cache, if it already exists don't relay this again.
|
||||
// this should fix reposts
|
||||
if ok, _ := m.lruCache.ContainsOrAdd(digestString(rmsg.Raw.Data["post"].(string)), true); ok {
|
||||
if ok, _ := m.lruCache.ContainsOrAdd(digestString(rmsg.Raw.Data["post"].(string)), true); ok && rmsg.Raw.Event != model.WEBSOCKET_EVENT_POST_DELETED {
|
||||
m.logger.Debugf("message %#v in cache, not processing again", rmsg.Raw.Data["post"].(string))
|
||||
rmsg.Text = ""
|
||||
return
|
||||
@@ -111,7 +111,7 @@ func (m *MMClient) GetFileLinks(filenames []string) []string {
|
||||
}
|
||||
|
||||
func (m *MMClient) GetPosts(channelId string, limit int) *model.PostList { //nolint:golint
|
||||
res, resp := m.Client.GetPostsForChannel(channelId, 0, limit, "")
|
||||
res, resp := m.Client.GetPostsForChannel(channelId, 0, limit, "", true)
|
||||
if resp.Error != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -119,7 +119,7 @@ func (m *MMClient) GetPosts(channelId string, limit int) *model.PostList { //nol
|
||||
}
|
||||
|
||||
func (m *MMClient) GetPostsSince(channelId string, time int64) *model.PostList { //nolint:golint
|
||||
res, resp := m.Client.GetPostsSince(channelId, time)
|
||||
res, resp := m.Client.GetPostsSince(channelId, time, true)
|
||||
if resp.Error != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
FROM alpine AS builder
|
||||
|
||||
COPY . /go/src/github.com/42wim/matterbridge
|
||||
COPY . /go/src/matterbridge
|
||||
RUN apk add \
|
||||
go \
|
||||
git \
|
||||
gcc \
|
||||
musl-dev \
|
||||
&& cd /go/src/github.com/42wim/matterbridge \
|
||||
&& export GOPATH=/go \
|
||||
&& go get \
|
||||
&& go build -x -ldflags "-X main.githash=$(git log --pretty=format:'%h' -n 1)" -o /bin/matterbridge
|
||||
&& cd /go/src/matterbridge \
|
||||
&& CGO_ENABLED=0 go build -mod vendor -ldflags "-X github.com/42wim/matterbridge/version.GitHash=$(git log --pretty=format:'%h' -n 1)" -o /bin/matterbridge
|
||||
|
||||
FROM alpine
|
||||
RUN apk --no-cache add \
|
||||
ca-certificates \
|
||||
cairo \
|
||||
libjpeg-turbo \
|
||||
libwebp-dev \
|
||||
mailcap \
|
||||
py3-webencodings \
|
||||
python3 \
|
||||
|
||||
24
vendor/github.com/Benau/go_rlottie/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2021, (see AUTHORS)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
1
vendor/github.com/Benau/go_rlottie/README.md
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Go binding for https://github.com/Samsung/rlottie, example at https://github.com/Benau/tgsconverter
|
||||
284
vendor/github.com/Benau/go_rlottie/binding_c_lottieanimation_capi.cpp
generated
vendored
Normal file
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "rlottie.h"
|
||||
#include "rlottie_capi.h"
|
||||
#include "vector_vdebug.h"
|
||||
|
||||
using namespace rlottie;
|
||||
|
||||
extern "C" {
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
struct Lottie_Animation_S
|
||||
{
|
||||
std::unique_ptr<Animation> mAnimation;
|
||||
std::future<Surface> mRenderTask;
|
||||
uint32_t *mBufferRef;
|
||||
LOTMarkerList *mMarkerList;
|
||||
};
|
||||
|
||||
RLOTTIE_API Lottie_Animation_S *lottie_animation_from_file(const char *path)
|
||||
{
|
||||
if (auto animation = Animation::loadFromFile(path) ) {
|
||||
Lottie_Animation_S *handle = new Lottie_Animation_S();
|
||||
handle->mAnimation = std::move(animation);
|
||||
return handle;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
RLOTTIE_API Lottie_Animation_S *lottie_animation_from_data(const char *data, const char *key, const char *resourcePath)
|
||||
{
|
||||
if (auto animation = Animation::loadFromData(data, key, resourcePath) ) {
|
||||
Lottie_Animation_S *handle = new Lottie_Animation_S();
|
||||
handle->mAnimation = std::move(animation);
|
||||
return handle;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
RLOTTIE_API void lottie_animation_destroy(Lottie_Animation_S *animation)
|
||||
{
|
||||
if (animation) {
|
||||
if (animation->mMarkerList) {
|
||||
for(size_t i = 0; i < animation->mMarkerList->size; i++) {
|
||||
if (animation->mMarkerList->ptr[i].name) free(animation->mMarkerList->ptr[i].name);
|
||||
}
|
||||
delete[] animation->mMarkerList->ptr;
|
||||
delete animation->mMarkerList;
|
||||
}
|
||||
|
||||
if (animation->mRenderTask.valid()) {
|
||||
animation->mRenderTask.get();
|
||||
}
|
||||
animation->mAnimation = nullptr;
|
||||
delete animation;
|
||||
}
|
||||
}
|
||||
|
||||
RLOTTIE_API void lottie_animation_get_size(const Lottie_Animation_S *animation, size_t *width, size_t *height)
|
||||
{
|
||||
if (!animation) return;
|
||||
|
||||
animation->mAnimation->size(*width, *height);
|
||||
}
|
||||
|
||||
RLOTTIE_API double lottie_animation_get_duration(const Lottie_Animation_S *animation)
|
||||
{
|
||||
if (!animation) return 0;
|
||||
|
||||
return animation->mAnimation->duration();
|
||||
}
|
||||
|
||||
RLOTTIE_API size_t lottie_animation_get_totalframe(const Lottie_Animation_S *animation)
|
||||
{
|
||||
if (!animation) return 0;
|
||||
|
||||
return animation->mAnimation->totalFrame();
|
||||
}
|
||||
|
||||
|
||||
RLOTTIE_API double lottie_animation_get_framerate(const Lottie_Animation_S *animation)
|
||||
{
|
||||
if (!animation) return 0;
|
||||
|
||||
return animation->mAnimation->frameRate();
|
||||
}
|
||||
|
||||
RLOTTIE_API const LOTLayerNode * lottie_animation_render_tree(Lottie_Animation_S *animation, size_t frame_num, size_t width, size_t height)
|
||||
{
|
||||
if (!animation) return nullptr;
|
||||
|
||||
return animation->mAnimation->renderTree(frame_num, width, height);
|
||||
}
|
||||
|
||||
RLOTTIE_API size_t
|
||||
lottie_animation_get_frame_at_pos(const Lottie_Animation_S *animation, float pos)
|
||||
{
|
||||
if (!animation) return 0;
|
||||
|
||||
return animation->mAnimation->frameAtPos(pos);
|
||||
}
|
||||
|
||||
RLOTTIE_API void
|
||||
lottie_animation_render(Lottie_Animation_S *animation,
|
||||
size_t frame_number,
|
||||
uint32_t *buffer,
|
||||
size_t width,
|
||||
size_t height,
|
||||
size_t bytes_per_line)
|
||||
{
|
||||
if (!animation) return;
|
||||
|
||||
rlottie::Surface surface(buffer, width, height, bytes_per_line);
|
||||
animation->mAnimation->renderSync(frame_number, surface);
|
||||
}
|
||||
|
||||
RLOTTIE_API void
|
||||
lottie_animation_render_async(Lottie_Animation_S *animation,
|
||||
size_t frame_number,
|
||||
uint32_t *buffer,
|
||||
size_t width,
|
||||
size_t height,
|
||||
size_t bytes_per_line)
|
||||
{
|
||||
if (!animation) return;
|
||||
|
||||
rlottie::Surface surface(buffer, width, height, bytes_per_line);
|
||||
animation->mRenderTask = animation->mAnimation->render(frame_number, surface);
|
||||
animation->mBufferRef = buffer;
|
||||
}
|
||||
|
||||
RLOTTIE_API uint32_t *
|
||||
lottie_animation_render_flush(Lottie_Animation_S *animation)
|
||||
{
|
||||
if (!animation) return nullptr;
|
||||
|
||||
if (animation->mRenderTask.valid()) {
|
||||
animation->mRenderTask.get();
|
||||
}
|
||||
|
||||
return animation->mBufferRef;
|
||||
}
|
||||
|
||||
RLOTTIE_API void
|
||||
lottie_animation_property_override(Lottie_Animation_S *animation,
|
||||
const Lottie_Animation_Property type,
|
||||
const char *keypath,
|
||||
...)
|
||||
{
|
||||
va_list prop;
|
||||
va_start(prop, keypath);
|
||||
const int arg_count = [type](){
|
||||
switch (type) {
|
||||
case LOTTIE_ANIMATION_PROPERTY_FILLCOLOR:
|
||||
case LOTTIE_ANIMATION_PROPERTY_STROKECOLOR:
|
||||
return 3;
|
||||
case LOTTIE_ANIMATION_PROPERTY_FILLOPACITY:
|
||||
case LOTTIE_ANIMATION_PROPERTY_STROKEOPACITY:
|
||||
case LOTTIE_ANIMATION_PROPERTY_STROKEWIDTH:
|
||||
case LOTTIE_ANIMATION_PROPERTY_TR_ROTATION:
|
||||
return 1;
|
||||
case LOTTIE_ANIMATION_PROPERTY_TR_POSITION:
|
||||
case LOTTIE_ANIMATION_PROPERTY_TR_SCALE:
|
||||
return 2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}();
|
||||
double v[3] = {0};
|
||||
for (int i = 0; i < arg_count ; i++) {
|
||||
v[i] = va_arg(prop, double);
|
||||
}
|
||||
va_end(prop);
|
||||
|
||||
switch(type) {
|
||||
case LOTTIE_ANIMATION_PROPERTY_FILLCOLOR: {
|
||||
double r = v[0];
|
||||
double g = v[1];
|
||||
double b = v[2];
|
||||
if (r > 1 || r < 0 || g > 1 || g < 0 || b > 1 || b < 0) break;
|
||||
animation->mAnimation->setValue<rlottie::Property::FillColor>(keypath, rlottie::Color(r, g, b));
|
||||
break;
|
||||
}
|
||||
case LOTTIE_ANIMATION_PROPERTY_FILLOPACITY: {
|
||||
double opacity = v[0];
|
||||
if (opacity > 100 || opacity < 0) break;
|
||||
animation->mAnimation->setValue<rlottie::Property::FillOpacity>(keypath, (float)opacity);
|
||||
break;
|
||||
}
|
||||
case LOTTIE_ANIMATION_PROPERTY_STROKECOLOR: {
|
||||
double r = v[0];
|
||||
double g = v[1];
|
||||
double b = v[2];
|
||||
if (r > 1 || r < 0 || g > 1 || g < 0 || b > 1 || b < 0) break;
|
||||
animation->mAnimation->setValue<rlottie::Property::StrokeColor>(keypath, rlottie::Color(r, g, b));
|
||||
break;
|
||||
}
|
||||
case LOTTIE_ANIMATION_PROPERTY_STROKEOPACITY: {
|
||||
double opacity = v[0];
|
||||
if (opacity > 100 || opacity < 0) break;
|
||||
animation->mAnimation->setValue<rlottie::Property::StrokeOpacity>(keypath, (float)opacity);
|
||||
break;
|
||||
}
|
||||
case LOTTIE_ANIMATION_PROPERTY_STROKEWIDTH: {
|
||||
double width = v[0];
|
||||
if (width < 0) break;
|
||||
animation->mAnimation->setValue<rlottie::Property::StrokeWidth>(keypath, (float)width);
|
||||
break;
|
||||
}
|
||||
case LOTTIE_ANIMATION_PROPERTY_TR_POSITION: {
|
||||
double x = v[0];
|
||||
double y = v[1];
|
||||
animation->mAnimation->setValue<rlottie::Property::TrPosition>(keypath, rlottie::Point((float)x, (float)y));
|
||||
break;
|
||||
}
|
||||
case LOTTIE_ANIMATION_PROPERTY_TR_SCALE: {
|
||||
double w = v[0];
|
||||
double h = v[1];
|
||||
animation->mAnimation->setValue<rlottie::Property::TrScale>(keypath, rlottie::Size((float)w, (float)h));
|
||||
break;
|
||||
}
|
||||
case LOTTIE_ANIMATION_PROPERTY_TR_ROTATION: {
|
||||
double r = v[0];
|
||||
animation->mAnimation->setValue<rlottie::Property::TrRotation>(keypath, (float)r);
|
||||
break;
|
||||
}
|
||||
case LOTTIE_ANIMATION_PROPERTY_TR_ANCHOR:
|
||||
case LOTTIE_ANIMATION_PROPERTY_TR_OPACITY:
|
||||
//@TODO handle propery update.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RLOTTIE_API const LOTMarkerList*
|
||||
lottie_animation_get_markerlist(Lottie_Animation_S *animation)
|
||||
{
|
||||
if (!animation) return nullptr;
|
||||
|
||||
auto markers = animation->mAnimation->markers();
|
||||
if (markers.size() == 0) return nullptr;
|
||||
if (animation->mMarkerList) return (const LOTMarkerList*)animation->mMarkerList;
|
||||
|
||||
animation->mMarkerList = new LOTMarkerList();
|
||||
animation->mMarkerList->size = markers.size();
|
||||
animation->mMarkerList->ptr = new LOTMarker[markers.size()]();
|
||||
|
||||
for(size_t i = 0; i < markers.size(); i++) {
|
||||
animation->mMarkerList->ptr[i].name = strdup(std::get<0>(markers[i]).c_str());
|
||||
animation->mMarkerList->ptr[i].startframe= std::get<1>(markers[i]);
|
||||
animation->mMarkerList->ptr[i].endframe= std::get<2>(markers[i]);
|
||||
}
|
||||
return (const LOTMarkerList*)animation->mMarkerList;
|
||||
}
|
||||
|
||||
RLOTTIE_API void
|
||||
lottie_configure_model_cache_size(size_t cacheSize)
|
||||
{
|
||||
rlottie::configureModelCacheSize(cacheSize);
|
||||
}
|
||||
|
||||
}
|
||||
10
vendor/github.com/Benau/go_rlottie/config.h
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef GO_RLOTTIE_HPP
|
||||
#define GO_RLOTTIE_HPP
|
||||
#ifndef __APPLE__
|
||||
#ifdef __ARM_NEON__
|
||||
#define USE_ARM_NEON
|
||||
#endif
|
||||
#endif
|
||||
#define LOTTIE_THREAD_SUPPORT
|
||||
#define LOTTIE_CACHE_SUPPORT
|
||||
#endif
|
||||
122
vendor/github.com/Benau/go_rlottie/generate_from_rlottie.py
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
#!/usr/bin/python3
|
||||
# ./generate_from_rlottie.py /path/to/clean/rlottie/src/ /path/to/clean/rlottie/inc/
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
FILE_KEYS = {}
|
||||
|
||||
def get_closest_local_header(header):
|
||||
for full_path, local in FILE_KEYS.items():
|
||||
if os.path.basename(full_path) == header:
|
||||
return local
|
||||
return ''
|
||||
|
||||
def fix_headers(code_text):
|
||||
out = ''
|
||||
has_neon = False
|
||||
for line in code_text:
|
||||
# Special fixes
|
||||
if line == '#include <vpoint.h>':
|
||||
line = '#include "vpoint.h"'
|
||||
if line == '#include <vsharedptr.h>':
|
||||
line = '#include "vsharedptr.h"'
|
||||
if line == '#include <vglobal.h>':
|
||||
line = '#include "vglobal.h"'
|
||||
if line == '#include <vrect.h>':
|
||||
line = '#include "vrect.h"'
|
||||
# ARM on apple fixes
|
||||
if '__ARM_NEON__' in line:
|
||||
has_neon = True
|
||||
line = line.replace('__ARM_NEON__', 'USE_ARM_NEON')
|
||||
header_file = re.match('#include\s+["]([^"]+)["].*', line)
|
||||
# regex to search for <, > too
|
||||
#header_file = re.match('#include\s+[<"]([^>"]+)[>"].*', line)
|
||||
if header_file:
|
||||
header = header_file.groups()[0]
|
||||
abs_header = os.path.abspath(header)
|
||||
header_exists = os.path.exists(abs_header)
|
||||
if header_exists and abs_header in FILE_KEYS:
|
||||
out += '#include "' + FILE_KEYS[abs_header] + '"\n'
|
||||
else:
|
||||
local = get_closest_local_header(header)
|
||||
if local != '':
|
||||
out += '#include "' + local + '"\n'
|
||||
else:
|
||||
out += line + '\n'
|
||||
else:
|
||||
out += line + '\n'
|
||||
if has_neon:
|
||||
out = '#include "config.h"\n' + out
|
||||
return out
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print('usage: ./generate_from_rlottie.py /path/to/clean/rlottie/src/ /path/to/clean/rlottie/inc/')
|
||||
os._exit(1)
|
||||
|
||||
code = ['.c', '.s', '.S', '.sx', 'cc', 'cpp', 'cpp' ]
|
||||
header = ['.h', '.hh', '.hpp', '.hxx' ]
|
||||
|
||||
# Remove old files
|
||||
files = os.listdir('.')
|
||||
for file in files:
|
||||
if file.endswith(tuple(code)) or file.endswith(tuple(header)):
|
||||
os.remove(os.path.join('.', file))
|
||||
|
||||
paths = []
|
||||
it = iter(sys.argv)
|
||||
next(it, None)
|
||||
for argv in it:
|
||||
paths.append(argv)
|
||||
|
||||
for path in paths:
|
||||
for file in glob.iglob(path + '/**', recursive=True):
|
||||
# Ignore msvc config.h and wasm file
|
||||
if file.endswith('config.h') or 'wasm' in file:
|
||||
continue
|
||||
if file.endswith(tuple(code)) or file.endswith(tuple(header)):
|
||||
key = os.path.abspath(file)
|
||||
val = file.replace(path, '').replace('/', '_')
|
||||
FILE_KEYS[key] = val
|
||||
|
||||
header_check = []
|
||||
for full_path, local in FILE_KEYS.items():
|
||||
header_file = os.path.basename(full_path)
|
||||
if header_file.endswith(tuple(code)):
|
||||
continue
|
||||
if not header_file in header_check:
|
||||
header_check.append(header_file)
|
||||
else:
|
||||
print('WARNING: ' + header_file + ' has multiple reference in subdirectories')
|
||||
|
||||
cur_dir = os.path.abspath('.')
|
||||
for full_path, local in FILE_KEYS.items():
|
||||
os.chdir(os.path.dirname(full_path))
|
||||
with open(full_path) as code:
|
||||
code_text = code.read().splitlines()
|
||||
code.close()
|
||||
fixed = fix_headers(code_text)
|
||||
os.chdir(cur_dir)
|
||||
local_file = open(local, "w")
|
||||
local_file.write(fixed)
|
||||
local_file.close()
|
||||
|
||||
# Write config.h
|
||||
config = '#ifndef GO_RLOTTIE_HPP\n#define GO_RLOTTIE_HPP\n'
|
||||
# ARM on apple won't compile
|
||||
config += '#ifndef __APPLE__\n#ifdef __ARM_NEON__\n#define USE_ARM_NEON\n#endif\n#endif\n'
|
||||
config += '#define LOTTIE_THREAD_SUPPORT\n#define LOTTIE_CACHE_SUPPORT\n'
|
||||
config += '#endif\n'
|
||||
config_file = open('config.h', "w")
|
||||
config_file.write(config)
|
||||
config_file.close()
|
||||
|
||||
# Fix vector_pixman_pixman-arm-neon-asm.S
|
||||
with open('vector_pixman_pixman-arm-neon-asm.S') as code:
|
||||
assembly = code.read()
|
||||
code.close()
|
||||
assembly = '#include "config.h"\n#ifdef USE_ARM_NEON\n' + assembly + '#endif\n'
|
||||
fixed_assembly = open('vector_pixman_pixman-arm-neon-asm.S', "w")
|
||||
fixed_assembly.write(assembly)
|
||||
fixed_assembly.close()
|
||||
56
vendor/github.com/Benau/go_rlottie/go_rlottie.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
package go_rlottie
|
||||
|
||||
/*
|
||||
#cgo !windows LDFLAGS: -lm
|
||||
#cgo windows CFLAGS: -DRLOTTIE_BUILD=0
|
||||
#cgo windows CXXFLAGS: -DRLOTTIE_BUILD=0
|
||||
#cgo CXXFLAGS: -std=c++14 -fno-exceptions -fno-asynchronous-unwind-tables -fno-rtti -Wall -fvisibility=hidden -Wnon-virtual-dtor -Woverloaded-virtual -Wno-unused-parameter
|
||||
#include "rlottie_capi.h"
|
||||
void lottie_configure_model_cache_size(size_t cacheSize);
|
||||
*/
|
||||
import "C"
|
||||
import "unsafe"
|
||||
|
||||
type Lottie_Animation *C.Lottie_Animation
|
||||
|
||||
func LottieConfigureModelCacheSize(size uint) {
|
||||
C.lottie_configure_model_cache_size(C.size_t(size))
|
||||
}
|
||||
|
||||
func LottieAnimationFromData(data string, key string, resource_path string) Lottie_Animation {
|
||||
var animation Lottie_Animation
|
||||
animation = C.lottie_animation_from_data(C.CString(data), C.CString(key), C.CString(resource_path))
|
||||
return animation
|
||||
}
|
||||
|
||||
func LottieAnimationDestroy(animation Lottie_Animation) {
|
||||
C.lottie_animation_destroy(animation)
|
||||
}
|
||||
|
||||
func LottieAnimationGetSize(animation Lottie_Animation) (uint, uint) {
|
||||
var width C.size_t
|
||||
var height C.size_t
|
||||
C.lottie_animation_get_size(animation, &width, &height)
|
||||
return uint(width), uint(height)
|
||||
}
|
||||
|
||||
func LottieAnimationGetTotalframe(animation Lottie_Animation) uint {
|
||||
return uint(C.lottie_animation_get_totalframe(animation))
|
||||
}
|
||||
|
||||
func LottieAnimationGetFramerate(animation Lottie_Animation) float64 {
|
||||
return float64(C.lottie_animation_get_framerate(animation))
|
||||
}
|
||||
|
||||
func LottieAnimationGetFrameAtPos(animation Lottie_Animation, pos float32) uint {
|
||||
return uint(C.lottie_animation_get_frame_at_pos(animation, C.float(pos)))
|
||||
}
|
||||
|
||||
func LottieAnimationGetDuration(animation Lottie_Animation) float64 {
|
||||
return float64(C.lottie_animation_get_duration(animation))
|
||||
}
|
||||
|
||||
func LottieAnimationRender(animation Lottie_Animation, frame_num uint, buffer []byte, width uint, height uint, bytes_per_line uint) {
|
||||
var ptr *C.uint32_t = (*C.uint32_t)(unsafe.Pointer(&buffer[0]));
|
||||
C.lottie_animation_render(animation, C.size_t(frame_num), ptr, C.size_t(width), C.size_t(height), C.size_t(bytes_per_line))
|
||||
}
|
||||
457
vendor/github.com/Benau/go_rlottie/lottie_lottieanimation.cpp
generated
vendored
Normal file
@@ -0,0 +1,457 @@
|
||||
#include "config.h"
|
||||
/*
|
||||
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "lottie_lottieitem.h"
|
||||
#include "lottie_lottiemodel.h"
|
||||
#include "rlottie.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
using namespace rlottie;
|
||||
using namespace rlottie::internal;
|
||||
|
||||
RLOTTIE_API void rlottie::configureModelCacheSize(size_t cacheSize)
|
||||
{
|
||||
internal::model::configureModelCacheSize(cacheSize);
|
||||
}
|
||||
|
||||
struct RenderTask {
|
||||
RenderTask() { receiver = sender.get_future(); }
|
||||
std::promise<Surface> sender;
|
||||
std::future<Surface> receiver;
|
||||
AnimationImpl * playerImpl{nullptr};
|
||||
size_t frameNo{0};
|
||||
Surface surface;
|
||||
bool keepAspectRatio{true};
|
||||
};
|
||||
using SharedRenderTask = std::shared_ptr<RenderTask>;
|
||||
|
||||
class AnimationImpl {
|
||||
public:
|
||||
void init(std::shared_ptr<model::Composition> composition);
|
||||
bool update(size_t frameNo, const VSize &size, bool keepAspectRatio);
|
||||
VSize size() const { return mModel->size(); }
|
||||
double duration() const { return mModel->duration(); }
|
||||
double frameRate() const { return mModel->frameRate(); }
|
||||
size_t totalFrame() const { return mModel->totalFrame(); }
|
||||
size_t frameAtPos(double pos) const { return mModel->frameAtPos(pos); }
|
||||
Surface render(size_t frameNo, const Surface &surface,
|
||||
bool keepAspectRatio);
|
||||
std::future<Surface> renderAsync(size_t frameNo, Surface &&surface,
|
||||
bool keepAspectRatio);
|
||||
const LOTLayerNode * renderTree(size_t frameNo, const VSize &size);
|
||||
|
||||
const LayerInfoList &layerInfoList() const
|
||||
{
|
||||
if (mLayerList.empty()) {
|
||||
mLayerList = mModel->layerInfoList();
|
||||
}
|
||||
return mLayerList;
|
||||
}
|
||||
const MarkerList &markers() const { return mModel->markers(); }
|
||||
void setValue(const std::string &keypath, LOTVariant &&value);
|
||||
void removeFilter(const std::string &keypath, Property prop);
|
||||
|
||||
private:
|
||||
mutable LayerInfoList mLayerList;
|
||||
model::Composition * mModel;
|
||||
SharedRenderTask mTask;
|
||||
std::atomic<bool> mRenderInProgress;
|
||||
std::unique_ptr<renderer::Composition> mRenderer{nullptr};
|
||||
};
|
||||
|
||||
void AnimationImpl::setValue(const std::string &keypath, LOTVariant &&value)
|
||||
{
|
||||
if (keypath.empty()) return;
|
||||
mRenderer->setValue(keypath, value);
|
||||
}
|
||||
|
||||
const LOTLayerNode *AnimationImpl::renderTree(size_t frameNo, const VSize &size)
|
||||
{
|
||||
if (update(frameNo, size, true)) {
|
||||
mRenderer->buildRenderTree();
|
||||
}
|
||||
return mRenderer->renderTree();
|
||||
}
|
||||
|
||||
bool AnimationImpl::update(size_t frameNo, const VSize &size,
|
||||
bool keepAspectRatio)
|
||||
{
|
||||
frameNo += mModel->startFrame();
|
||||
|
||||
if (frameNo > mModel->endFrame()) frameNo = mModel->endFrame();
|
||||
|
||||
if (frameNo < mModel->startFrame()) frameNo = mModel->startFrame();
|
||||
|
||||
return mRenderer->update(int(frameNo), size, keepAspectRatio);
|
||||
}
|
||||
|
||||
Surface AnimationImpl::render(size_t frameNo, const Surface &surface,
|
||||
bool keepAspectRatio)
|
||||
{
|
||||
bool renderInProgress = mRenderInProgress.load();
|
||||
if (renderInProgress) {
|
||||
vCritical << "Already Rendering Scheduled for this Animation";
|
||||
return surface;
|
||||
}
|
||||
|
||||
mRenderInProgress.store(true);
|
||||
update(
|
||||
frameNo,
|
||||
VSize(int(surface.drawRegionWidth()), int(surface.drawRegionHeight())),
|
||||
keepAspectRatio);
|
||||
mRenderer->render(surface);
|
||||
mRenderInProgress.store(false);
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
void AnimationImpl::init(std::shared_ptr<model::Composition> composition)
|
||||
{
|
||||
mModel = composition.get();
|
||||
mRenderer = std::make_unique<renderer::Composition>(composition);
|
||||
mRenderInProgress = false;
|
||||
}
|
||||
|
||||
#ifdef LOTTIE_THREAD_SUPPORT
|
||||
|
||||
#include <thread>
|
||||
#include "vector_vtaskqueue.h"
|
||||
|
||||
/*
|
||||
* Implement a task stealing schduler to perform render task
|
||||
* As each player draws into its own buffer we can delegate this
|
||||
* task to a slave thread. The scheduler creates a threadpool depending
|
||||
* on the number of cores available in the system and does a simple fair
|
||||
* scheduling by assigning the task in a round-robin fashion. Each thread
|
||||
* in the threadpool has its own queue. once it finishes all the task on its
|
||||
* own queue it goes through rest of the queue and looks for task if it founds
|
||||
* one it steals the task from it and executes. if it couldn't find one then it
|
||||
* just waits for new task on its own queue.
|
||||
*/
|
||||
class RenderTaskScheduler {
|
||||
const unsigned _count{std::thread::hardware_concurrency()};
|
||||
std::vector<std::thread> _threads;
|
||||
std::vector<TaskQueue<SharedRenderTask>> _q{_count};
|
||||
std::atomic<unsigned> _index{0};
|
||||
|
||||
void run(unsigned i)
|
||||
{
|
||||
while (true) {
|
||||
bool success = false;
|
||||
SharedRenderTask task;
|
||||
for (unsigned n = 0; n != _count * 2; ++n) {
|
||||
if (_q[(i + n) % _count].try_pop(task)) {
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!success && !_q[i].pop(task)) break;
|
||||
|
||||
auto result = task->playerImpl->render(task->frameNo, task->surface,
|
||||
task->keepAspectRatio);
|
||||
task->sender.set_value(result);
|
||||
}
|
||||
}
|
||||
|
||||
RenderTaskScheduler()
|
||||
{
|
||||
for (unsigned n = 0; n != _count; ++n) {
|
||||
_threads.emplace_back([&, n] { run(n); });
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
static RenderTaskScheduler &instance()
|
||||
{
|
||||
static RenderTaskScheduler singleton;
|
||||
return singleton;
|
||||
}
|
||||
|
||||
~RenderTaskScheduler()
|
||||
{
|
||||
for (auto &e : _q) e.done();
|
||||
|
||||
for (auto &e : _threads) e.join();
|
||||
}
|
||||
|
||||
std::future<Surface> process(SharedRenderTask task)
|
||||
{
|
||||
auto receiver = std::move(task->receiver);
|
||||
auto i = _index++;
|
||||
|
||||
for (unsigned n = 0; n != _count; ++n) {
|
||||
if (_q[(i + n) % _count].try_push(std::move(task))) return receiver;
|
||||
}
|
||||
|
||||
if (_count > 0) {
|
||||
_q[i % _count].push(std::move(task));
|
||||
}
|
||||
|
||||
return receiver;
|
||||
}
|
||||
};
|
||||
|
||||
#else
|
||||
class RenderTaskScheduler {
|
||||
public:
|
||||
static RenderTaskScheduler &instance()
|
||||
{
|
||||
static RenderTaskScheduler singleton;
|
||||
return singleton;
|
||||
}
|
||||
|
||||
std::future<Surface> process(SharedRenderTask task)
|
||||
{
|
||||
auto result = task->playerImpl->render(task->frameNo, task->surface,
|
||||
task->keepAspectRatio);
|
||||
task->sender.set_value(result);
|
||||
return std::move(task->receiver);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
std::future<Surface> AnimationImpl::renderAsync(size_t frameNo,
|
||||
Surface &&surface,
|
||||
bool keepAspectRatio)
|
||||
{
|
||||
if (!mTask) {
|
||||
mTask = std::make_shared<RenderTask>();
|
||||
} else {
|
||||
mTask->sender = std::promise<Surface>();
|
||||
mTask->receiver = mTask->sender.get_future();
|
||||
}
|
||||
mTask->playerImpl = this;
|
||||
mTask->frameNo = frameNo;
|
||||
mTask->surface = std::move(surface);
|
||||
mTask->keepAspectRatio = keepAspectRatio;
|
||||
|
||||
return RenderTaskScheduler::instance().process(mTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* \breif Brief abput the Api.
|
||||
* Description about the setFilePath Api
|
||||
* @param path add the details
|
||||
*/
|
||||
std::unique_ptr<Animation> Animation::loadFromData(
|
||||
std::string jsonData, const std::string &key,
|
||||
const std::string &resourcePath, bool cachePolicy)
|
||||
{
|
||||
if (jsonData.empty()) {
|
||||
vWarning << "jason data is empty";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto composition = model::loadFromData(std::move(jsonData), key,
|
||||
resourcePath, cachePolicy);
|
||||
if (composition) {
|
||||
auto animation = std::unique_ptr<Animation>(new Animation);
|
||||
animation->d->init(std::move(composition));
|
||||
return animation;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Animation> Animation::loadFromData(std::string jsonData,
|
||||
std::string resourcePath,
|
||||
ColorFilter filter)
|
||||
{
|
||||
if (jsonData.empty()) {
|
||||
vWarning << "jason data is empty";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto composition = model::loadFromData(
|
||||
std::move(jsonData), std::move(resourcePath), std::move(filter));
|
||||
if (composition) {
|
||||
auto animation = std::unique_ptr<Animation>(new Animation);
|
||||
animation->d->init(std::move(composition));
|
||||
return animation;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Animation> Animation::loadFromFile(const std::string &path,
|
||||
bool cachePolicy)
|
||||
{
|
||||
if (path.empty()) {
|
||||
vWarning << "File path is empty";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto composition = model::loadFromFile(path, cachePolicy);
|
||||
if (composition) {
|
||||
auto animation = std::unique_ptr<Animation>(new Animation);
|
||||
animation->d->init(std::move(composition));
|
||||
return animation;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Animation::size(size_t &width, size_t &height) const
|
||||
{
|
||||
VSize sz = d->size();
|
||||
|
||||
width = sz.width();
|
||||
height = sz.height();
|
||||
}
|
||||
|
||||
double Animation::duration() const
|
||||
{
|
||||
return d->duration();
|
||||
}
|
||||
|
||||
double Animation::frameRate() const
|
||||
{
|
||||
return d->frameRate();
|
||||
}
|
||||
|
||||
size_t Animation::totalFrame() const
|
||||
{
|
||||
return d->totalFrame();
|
||||
}
|
||||
|
||||
size_t Animation::frameAtPos(double pos)
|
||||
{
|
||||
return d->frameAtPos(pos);
|
||||
}
|
||||
|
||||
const LOTLayerNode *Animation::renderTree(size_t frameNo, size_t width,
|
||||
size_t height) const
|
||||
{
|
||||
return d->renderTree(frameNo, VSize(int(width), int(height)));
|
||||
}
|
||||
|
||||
std::future<Surface> Animation::render(size_t frameNo, Surface surface,
|
||||
bool keepAspectRatio)
|
||||
{
|
||||
return d->renderAsync(frameNo, std::move(surface), keepAspectRatio);
|
||||
}
|
||||
|
||||
void Animation::renderSync(size_t frameNo, Surface surface,
|
||||
bool keepAspectRatio)
|
||||
{
|
||||
d->render(frameNo, surface, keepAspectRatio);
|
||||
}
|
||||
|
||||
const LayerInfoList &Animation::layers() const
|
||||
{
|
||||
return d->layerInfoList();
|
||||
}
|
||||
|
||||
const MarkerList &Animation::markers() const
|
||||
{
|
||||
return d->markers();
|
||||
}
|
||||
|
||||
void Animation::setValue(Color_Type, Property prop, const std::string &keypath,
|
||||
Color value)
|
||||
{
|
||||
d->setValue(keypath,
|
||||
LOTVariant(prop, [value](const FrameInfo &) { return value; }));
|
||||
}
|
||||
|
||||
void Animation::setValue(Float_Type, Property prop, const std::string &keypath,
|
||||
float value)
|
||||
{
|
||||
d->setValue(keypath,
|
||||
LOTVariant(prop, [value](const FrameInfo &) { return value; }));
|
||||
}
|
||||
|
||||
void Animation::setValue(Size_Type, Property prop, const std::string &keypath,
|
||||
Size value)
|
||||
{
|
||||
d->setValue(keypath,
|
||||
LOTVariant(prop, [value](const FrameInfo &) { return value; }));
|
||||
}
|
||||
|
||||
void Animation::setValue(Point_Type, Property prop, const std::string &keypath,
|
||||
Point value)
|
||||
{
|
||||
d->setValue(keypath,
|
||||
LOTVariant(prop, [value](const FrameInfo &) { return value; }));
|
||||
}
|
||||
|
||||
void Animation::setValue(Color_Type, Property prop, const std::string &keypath,
|
||||
std::function<Color(const FrameInfo &)> &&value)
|
||||
{
|
||||
d->setValue(keypath, LOTVariant(prop, value));
|
||||
}
|
||||
|
||||
void Animation::setValue(Float_Type, Property prop, const std::string &keypath,
|
||||
std::function<float(const FrameInfo &)> &&value)
|
||||
{
|
||||
d->setValue(keypath, LOTVariant(prop, value));
|
||||
}
|
||||
|
||||
void Animation::setValue(Size_Type, Property prop, const std::string &keypath,
|
||||
std::function<Size(const FrameInfo &)> &&value)
|
||||
{
|
||||
d->setValue(keypath, LOTVariant(prop, value));
|
||||
}
|
||||
|
||||
void Animation::setValue(Point_Type, Property prop, const std::string &keypath,
|
||||
std::function<Point(const FrameInfo &)> &&value)
|
||||
{
|
||||
d->setValue(keypath, LOTVariant(prop, value));
|
||||
}
|
||||
|
||||
Animation::~Animation() = default;
|
||||
Animation::Animation() : d(std::make_unique<AnimationImpl>()) {}
|
||||
|
||||
Surface::Surface(uint32_t *buffer, size_t width, size_t height,
|
||||
size_t bytesPerLine)
|
||||
: mBuffer(buffer),
|
||||
mWidth(width),
|
||||
mHeight(height),
|
||||
mBytesPerLine(bytesPerLine)
|
||||
{
|
||||
mDrawArea.w = mWidth;
|
||||
mDrawArea.h = mHeight;
|
||||
}
|
||||
|
||||
void Surface::setDrawRegion(size_t x, size_t y, size_t width, size_t height)
|
||||
{
|
||||
if ((x + width > mWidth) || (y + height > mHeight)) return;
|
||||
|
||||
mDrawArea.x = x;
|
||||
mDrawArea.y = y;
|
||||
mDrawArea.w = width;
|
||||
mDrawArea.h = height;
|
||||
}
|
||||
|
||||
#ifdef LOTTIE_LOGGING_SUPPORT
|
||||
void initLogging()
|
||||
{
|
||||
#if defined(USE_ARM_NEON)
|
||||
set_log_level(LogLevel::OFF);
|
||||
#else
|
||||
initialize(GuaranteedLogger(), "/tmp/", "rlottie", 1);
|
||||
set_log_level(LogLevel::INFO);
|
||||
#endif
|
||||
}
|
||||
|
||||
V_CONSTRUCTOR_FUNCTION(initLogging)
|
||||
#endif
|
||||
435
vendor/github.com/Benau/go_rlottie/lottie_lottiefiltermodel.h
generated
vendored
Normal file
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef LOTTIEFILTERMODEL_H
|
||||
#define LOTTIEFILTERMODEL_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
#include <cassert>
|
||||
#include "lottie_lottiemodel.h"
|
||||
#include "rlottie.h"
|
||||
|
||||
using namespace rlottie::internal;
|
||||
// Naive way to implement std::variant
|
||||
// refactor it when we move to c++17
|
||||
// users should make sure proper combination
|
||||
// of id and value are passed while creating the object.
|
||||
class LOTVariant {
|
||||
public:
|
||||
using ValueFunc = std::function<float(const rlottie::FrameInfo&)>;
|
||||
using ColorFunc = std::function<rlottie::Color(const rlottie::FrameInfo&)>;
|
||||
using PointFunc = std::function<rlottie::Point(const rlottie::FrameInfo&)>;
|
||||
using SizeFunc = std::function<rlottie::Size(const rlottie::FrameInfo&)>;
|
||||
|
||||
LOTVariant(rlottie::Property prop, const ValueFunc& v)
|
||||
: mPropery(prop), mTag(Value)
|
||||
{
|
||||
construct(impl.valueFunc, v);
|
||||
}
|
||||
|
||||
LOTVariant(rlottie::Property prop, ValueFunc&& v)
|
||||
: mPropery(prop), mTag(Value)
|
||||
{
|
||||
moveConstruct(impl.valueFunc, std::move(v));
|
||||
}
|
||||
|
||||
LOTVariant(rlottie::Property prop, const ColorFunc& v)
|
||||
: mPropery(prop), mTag(Color)
|
||||
{
|
||||
construct(impl.colorFunc, v);
|
||||
}
|
||||
|
||||
LOTVariant(rlottie::Property prop, ColorFunc&& v)
|
||||
: mPropery(prop), mTag(Color)
|
||||
{
|
||||
moveConstruct(impl.colorFunc, std::move(v));
|
||||
}
|
||||
|
||||
LOTVariant(rlottie::Property prop, const PointFunc& v)
|
||||
: mPropery(prop), mTag(Point)
|
||||
{
|
||||
construct(impl.pointFunc, v);
|
||||
}
|
||||
|
||||
LOTVariant(rlottie::Property prop, PointFunc&& v)
|
||||
: mPropery(prop), mTag(Point)
|
||||
{
|
||||
moveConstruct(impl.pointFunc, std::move(v));
|
||||
}
|
||||
|
||||
LOTVariant(rlottie::Property prop, const SizeFunc& v)
|
||||
: mPropery(prop), mTag(Size)
|
||||
{
|
||||
construct(impl.sizeFunc, v);
|
||||
}
|
||||
|
||||
LOTVariant(rlottie::Property prop, SizeFunc&& v)
|
||||
: mPropery(prop), mTag(Size)
|
||||
{
|
||||
moveConstruct(impl.sizeFunc, std::move(v));
|
||||
}
|
||||
|
||||
rlottie::Property property() const { return mPropery; }
|
||||
|
||||
const ColorFunc& color() const
|
||||
{
|
||||
assert(mTag == Color);
|
||||
return impl.colorFunc;
|
||||
}
|
||||
|
||||
const ValueFunc& value() const
|
||||
{
|
||||
assert(mTag == Value);
|
||||
return impl.valueFunc;
|
||||
}
|
||||
|
||||
const PointFunc& point() const
|
||||
{
|
||||
assert(mTag == Point);
|
||||
return impl.pointFunc;
|
||||
}
|
||||
|
||||
const SizeFunc& size() const
|
||||
{
|
||||
assert(mTag == Size);
|
||||
return impl.sizeFunc;
|
||||
}
|
||||
|
||||
LOTVariant() = default;
|
||||
~LOTVariant() noexcept { Destroy(); }
|
||||
LOTVariant(const LOTVariant& other) { Copy(other); }
|
||||
LOTVariant(LOTVariant&& other) noexcept { Move(std::move(other)); }
|
||||
LOTVariant& operator=(LOTVariant&& other)
|
||||
{
|
||||
Destroy();
|
||||
Move(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
LOTVariant& operator=(const LOTVariant& other)
|
||||
{
|
||||
Destroy();
|
||||
Copy(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void construct(T& member, const T& val)
|
||||
{
|
||||
new (&member) T(val);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void moveConstruct(T& member, T&& val)
|
||||
{
|
||||
new (&member) T(std::move(val));
|
||||
}
|
||||
|
||||
void Move(LOTVariant&& other)
|
||||
{
|
||||
switch (other.mTag) {
|
||||
case Type::Value:
|
||||
moveConstruct(impl.valueFunc, std::move(other.impl.valueFunc));
|
||||
break;
|
||||
case Type::Color:
|
||||
moveConstruct(impl.colorFunc, std::move(other.impl.colorFunc));
|
||||
break;
|
||||
case Type::Point:
|
||||
moveConstruct(impl.pointFunc, std::move(other.impl.pointFunc));
|
||||
break;
|
||||
case Type::Size:
|
||||
moveConstruct(impl.sizeFunc, std::move(other.impl.sizeFunc));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
mTag = other.mTag;
|
||||
mPropery = other.mPropery;
|
||||
other.mTag = MonoState;
|
||||
}
|
||||
|
||||
void Copy(const LOTVariant& other)
|
||||
{
|
||||
switch (other.mTag) {
|
||||
case Type::Value:
|
||||
construct(impl.valueFunc, other.impl.valueFunc);
|
||||
break;
|
||||
case Type::Color:
|
||||
construct(impl.colorFunc, other.impl.colorFunc);
|
||||
break;
|
||||
case Type::Point:
|
||||
construct(impl.pointFunc, other.impl.pointFunc);
|
||||
break;
|
||||
case Type::Size:
|
||||
construct(impl.sizeFunc, other.impl.sizeFunc);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
mTag = other.mTag;
|
||||
mPropery = other.mPropery;
|
||||
}
|
||||
|
||||
void Destroy()
|
||||
{
|
||||
switch (mTag) {
|
||||
case MonoState: {
|
||||
break;
|
||||
}
|
||||
case Value: {
|
||||
impl.valueFunc.~ValueFunc();
|
||||
break;
|
||||
}
|
||||
case Color: {
|
||||
impl.colorFunc.~ColorFunc();
|
||||
break;
|
||||
}
|
||||
case Point: {
|
||||
impl.pointFunc.~PointFunc();
|
||||
break;
|
||||
}
|
||||
case Size: {
|
||||
impl.sizeFunc.~SizeFunc();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Type { MonoState, Value, Color, Point, Size };
|
||||
rlottie::Property mPropery;
|
||||
Type mTag{MonoState};
|
||||
union details {
|
||||
ColorFunc colorFunc;
|
||||
ValueFunc valueFunc;
|
||||
PointFunc pointFunc;
|
||||
SizeFunc sizeFunc;
|
||||
details() {}
|
||||
~details() noexcept {}
|
||||
} impl;
|
||||
};
|
||||
|
||||
namespace rlottie {
|
||||
|
||||
namespace internal {
|
||||
|
||||
namespace model {
|
||||
|
||||
class FilterData {
|
||||
public:
|
||||
void addValue(LOTVariant& value)
|
||||
{
|
||||
uint index = static_cast<uint>(value.property());
|
||||
if (mBitset.test(index)) {
|
||||
std::replace_if(mFilters.begin(), mFilters.end(),
|
||||
[&value](const LOTVariant& e) {
|
||||
return e.property() == value.property();
|
||||
},
|
||||
value);
|
||||
} else {
|
||||
mBitset.set(index);
|
||||
mFilters.push_back(value);
|
||||
}
|
||||
}
|
||||
|
||||
void removeValue(LOTVariant& value)
|
||||
{
|
||||
uint index = static_cast<uint>(value.property());
|
||||
if (mBitset.test(index)) {
|
||||
mBitset.reset(index);
|
||||
mFilters.erase(std::remove_if(mFilters.begin(), mFilters.end(),
|
||||
[&value](const LOTVariant& e) {
|
||||
return e.property() ==
|
||||
value.property();
|
||||
}),
|
||||
mFilters.end());
|
||||
}
|
||||
}
|
||||
bool hasFilter(rlottie::Property prop) const
|
||||
{
|
||||
return mBitset.test(static_cast<uint>(prop));
|
||||
}
|
||||
model::Color color(rlottie::Property prop, int frame) const
|
||||
{
|
||||
rlottie::FrameInfo info(frame);
|
||||
rlottie::Color col = data(prop).color()(info);
|
||||
return model::Color(col.r(), col.g(), col.b());
|
||||
}
|
||||
VPointF point(rlottie::Property prop, int frame) const
|
||||
{
|
||||
rlottie::FrameInfo info(frame);
|
||||
rlottie::Point pt = data(prop).point()(info);
|
||||
return VPointF(pt.x(), pt.y());
|
||||
}
|
||||
VSize scale(rlottie::Property prop, int frame) const
|
||||
{
|
||||
rlottie::FrameInfo info(frame);
|
||||
rlottie::Size sz = data(prop).size()(info);
|
||||
return VSize(sz.w(), sz.h());
|
||||
}
|
||||
float opacity(rlottie::Property prop, int frame) const
|
||||
{
|
||||
rlottie::FrameInfo info(frame);
|
||||
float val = data(prop).value()(info);
|
||||
return val / 100;
|
||||
}
|
||||
float value(rlottie::Property prop, int frame) const
|
||||
{
|
||||
rlottie::FrameInfo info(frame);
|
||||
return data(prop).value()(info);
|
||||
}
|
||||
|
||||
private:
|
||||
const LOTVariant& data(rlottie::Property prop) const
|
||||
{
|
||||
auto result = std::find_if(
|
||||
mFilters.begin(), mFilters.end(),
|
||||
[prop](const LOTVariant& e) { return e.property() == prop; });
|
||||
return *result;
|
||||
}
|
||||
std::bitset<32> mBitset{0};
|
||||
std::vector<LOTVariant> mFilters;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct FilterBase
|
||||
{
|
||||
FilterBase(T *model): model_(model){}
|
||||
|
||||
const char* name() const { return model_->name(); }
|
||||
|
||||
FilterData* filter() {
|
||||
if (!filterData_) filterData_ = std::make_unique<FilterData>();
|
||||
return filterData_.get();
|
||||
}
|
||||
|
||||
const FilterData * filter() const { return filterData_.get(); }
|
||||
const T* model() const { return model_;}
|
||||
|
||||
bool hasFilter(rlottie::Property prop) const {
|
||||
return filterData_ ? filterData_->hasFilter(prop)
|
||||
: false;
|
||||
}
|
||||
|
||||
T* model_{nullptr};
|
||||
std::unique_ptr<FilterData> filterData_{nullptr};
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
class Filter : public FilterBase<T> {
|
||||
public:
|
||||
Filter(T* model): FilterBase<T>(model){}
|
||||
model::Color color(int frame) const
|
||||
{
|
||||
if (this->hasFilter(rlottie::Property::StrokeColor)) {
|
||||
return this->filter()->color(rlottie::Property::StrokeColor, frame);
|
||||
}
|
||||
return this->model()->color(frame);
|
||||
}
|
||||
float opacity(int frame) const
|
||||
{
|
||||
if (this->hasFilter(rlottie::Property::StrokeOpacity)) {
|
||||
return this->filter()->opacity(rlottie::Property::StrokeOpacity, frame);
|
||||
}
|
||||
return this->model()->opacity(frame);
|
||||
}
|
||||
|
||||
float strokeWidth(int frame) const
|
||||
{
|
||||
if (this->hasFilter(rlottie::Property::StrokeWidth)) {
|
||||
return this->filter()->value(rlottie::Property::StrokeWidth, frame);
|
||||
}
|
||||
return this->model()->strokeWidth(frame);
|
||||
}
|
||||
|
||||
float miterLimit() const { return this->model()->miterLimit(); }
|
||||
CapStyle capStyle() const { return this->model()->capStyle(); }
|
||||
JoinStyle joinStyle() const { return this->model()->joinStyle(); }
|
||||
bool hasDashInfo() const { return this->model()->hasDashInfo(); }
|
||||
void getDashInfo(int frameNo, std::vector<float>& result) const
|
||||
{
|
||||
return this->model()->getDashInfo(frameNo, result);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
class Filter<model::Fill>: public FilterBase<model::Fill>
|
||||
{
|
||||
public:
|
||||
Filter(model::Fill* model) : FilterBase<model::Fill>(model) {}
|
||||
|
||||
model::Color color(int frame) const
|
||||
{
|
||||
if (this->hasFilter(rlottie::Property::FillColor)) {
|
||||
return this->filter()->color(rlottie::Property::FillColor, frame);
|
||||
}
|
||||
return this->model()->color(frame);
|
||||
}
|
||||
|
||||
float opacity(int frame) const
|
||||
{
|
||||
if (this->hasFilter(rlottie::Property::FillOpacity)) {
|
||||
return this->filter()->opacity(rlottie::Property::FillOpacity, frame);
|
||||
}
|
||||
return this->model()->opacity(frame);
|
||||
}
|
||||
|
||||
FillRule fillRule() const { return this->model()->fillRule(); }
|
||||
};
|
||||
|
||||
template <>
|
||||
class Filter<model::Group> : public FilterBase<model::Group>
|
||||
{
|
||||
public:
|
||||
Filter(model::Group* model = nullptr) : FilterBase<model::Group>(model) {}
|
||||
|
||||
bool hasModel() const { return this->model() ? true : false; }
|
||||
|
||||
model::Transform* transform() const { return this->model() ? this->model()->mTransform : nullptr; }
|
||||
VMatrix matrix(int frame) const
|
||||
{
|
||||
VMatrix mS, mR, mT;
|
||||
if (this->hasFilter(rlottie::Property::TrScale)) {
|
||||
VSize s = this->filter()->scale(rlottie::Property::TrScale, frame);
|
||||
mS.scale(s.width() / 100.0, s.height() / 100.0);
|
||||
}
|
||||
if (this->hasFilter(rlottie::Property::TrRotation)) {
|
||||
mR.rotate(this->filter()->value(rlottie::Property::TrRotation, frame));
|
||||
}
|
||||
if (this->hasFilter(rlottie::Property::TrPosition)) {
|
||||
mT.translate(this->filter()->point(rlottie::Property::TrPosition, frame));
|
||||
}
|
||||
|
||||
return this->model()->mTransform->matrix(frame) * mS * mR * mT;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace model
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace rlottie
|
||||
|
||||
#endif // LOTTIEFILTERMODEL_H
|
||||
1491
vendor/github.com/Benau/go_rlottie/lottie_lottieitem.cpp
generated
vendored
Normal file
626
vendor/github.com/Benau/go_rlottie/lottie_lottieitem.h
generated
vendored
Normal file
@@ -0,0 +1,626 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef LOTTIEITEM_H
|
||||
#define LOTTIEITEM_H
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include "lottie_lottiekeypath.h"
|
||||
#include "lottie_lottiefiltermodel.h"
|
||||
#include "rlottie.h"
|
||||
#include "rlottiecommon.h"
|
||||
#include "vector_varenaalloc.h"
|
||||
#include "vector_vdrawable.h"
|
||||
#include "vector_vmatrix.h"
|
||||
#include "vector_vpainter.h"
|
||||
#include "vector_vpath.h"
|
||||
#include "vector_vpathmesure.h"
|
||||
#include "vector_vpoint.h"
|
||||
|
||||
V_USE_NAMESPACE
|
||||
|
||||
namespace rlottie {
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <class T>
|
||||
class VSpan {
|
||||
public:
|
||||
using reference = T &;
|
||||
using pointer = T *;
|
||||
using const_pointer = T const *;
|
||||
using const_reference = T const &;
|
||||
using index_type = size_t;
|
||||
|
||||
using iterator = pointer;
|
||||
using const_iterator = const_pointer;
|
||||
|
||||
VSpan() = default;
|
||||
VSpan(pointer data, index_type size) : _data(data), _size(size) {}
|
||||
|
||||
constexpr pointer data() const noexcept { return _data; }
|
||||
constexpr index_type size() const noexcept { return _size; }
|
||||
constexpr bool empty() const noexcept { return size() == 0; }
|
||||
constexpr iterator begin() const noexcept { return data(); }
|
||||
constexpr iterator end() const noexcept { return data() + size(); }
|
||||
constexpr const_iterator cbegin() const noexcept { return data(); }
|
||||
constexpr const_iterator cend() const noexcept { return data() + size(); }
|
||||
constexpr reference operator[](index_type idx) const
|
||||
{
|
||||
return *(data() + idx);
|
||||
}
|
||||
|
||||
private:
|
||||
pointer _data{nullptr};
|
||||
index_type _size{0};
|
||||
};
|
||||
|
||||
namespace renderer {
|
||||
|
||||
using DrawableList = VSpan<VDrawable *>;
|
||||
|
||||
enum class DirtyFlagBit : uchar {
|
||||
None = 0x00,
|
||||
Matrix = 0x01,
|
||||
Alpha = 0x02,
|
||||
All = (Matrix | Alpha)
|
||||
};
|
||||
typedef vFlag<DirtyFlagBit> DirtyFlag;
|
||||
|
||||
class SurfaceCache {
|
||||
public:
|
||||
SurfaceCache() { mCache.reserve(10); }
|
||||
|
||||
VBitmap make_surface(
|
||||
size_t width, size_t height,
|
||||
VBitmap::Format format = VBitmap::Format::ARGB32_Premultiplied)
|
||||
{
|
||||
if (mCache.empty()) return {width, height, format};
|
||||
|
||||
auto surface = mCache.back();
|
||||
surface.reset(width, height, format);
|
||||
|
||||
mCache.pop_back();
|
||||
return surface;
|
||||
}
|
||||
|
||||
void release_surface(VBitmap &surface) { mCache.push_back(surface); }
|
||||
|
||||
private:
|
||||
std::vector<VBitmap> mCache;
|
||||
};
|
||||
|
||||
class Drawable final : public VDrawable {
|
||||
public:
|
||||
void sync();
|
||||
|
||||
public:
|
||||
std::unique_ptr<LOTNode> mCNode{nullptr};
|
||||
|
||||
~Drawable() noexcept
|
||||
{
|
||||
if (mCNode && mCNode->mGradient.stopPtr)
|
||||
free(mCNode->mGradient.stopPtr);
|
||||
}
|
||||
};
|
||||
|
||||
struct CApiData {
|
||||
CApiData();
|
||||
LOTLayerNode mLayer;
|
||||
std::vector<LOTMask> mMasks;
|
||||
std::vector<LOTLayerNode *> mLayers;
|
||||
std::vector<LOTNode *> mCNodeList;
|
||||
};
|
||||
|
||||
class Clipper {
|
||||
public:
|
||||
explicit Clipper(VSize size) : mSize(size) {}
|
||||
void update(const VMatrix &matrix);
|
||||
void preprocess(const VRect &clip);
|
||||
VRle rle(const VRle &mask);
|
||||
|
||||
public:
|
||||
VSize mSize;
|
||||
VPath mPath;
|
||||
VRle mMaskedRle;
|
||||
VRasterizer mRasterizer;
|
||||
bool mRasterRequest{false};
|
||||
};
|
||||
|
||||
class Mask {
|
||||
public:
|
||||
explicit Mask(model::Mask *data) : mData(data) {}
|
||||
void update(int frameNo, const VMatrix &parentMatrix, float parentAlpha,
|
||||
const DirtyFlag &flag);
|
||||
model::Mask::Mode maskMode() const { return mData->mMode; }
|
||||
VRle rle();
|
||||
void preprocess(const VRect &clip);
|
||||
bool inverted() const { return mData->mInv; }
|
||||
public:
|
||||
model::Mask *mData{nullptr};
|
||||
VPath mLocalPath;
|
||||
VPath mFinalPath;
|
||||
VRasterizer mRasterizer;
|
||||
float mCombinedAlpha{0};
|
||||
bool mRasterRequest{false};
|
||||
};
|
||||
|
||||
/*
|
||||
* Handels mask property of a layer item
|
||||
*/
|
||||
class LayerMask {
|
||||
public:
|
||||
explicit LayerMask(model::Layer *layerData);
|
||||
void update(int frameNo, const VMatrix &parentMatrix, float parentAlpha,
|
||||
const DirtyFlag &flag);
|
||||
bool isStatic() const { return mStatic; }
|
||||
VRle maskRle(const VRect &clipRect);
|
||||
void preprocess(const VRect &clip);
|
||||
|
||||
public:
|
||||
std::vector<Mask> mMasks;
|
||||
VRle mRle;
|
||||
bool mStatic{true};
|
||||
bool mDirty{true};
|
||||
};
|
||||
|
||||
class Layer;
|
||||
|
||||
class Composition {
|
||||
public:
|
||||
explicit Composition(std::shared_ptr<model::Composition> composition);
|
||||
bool update(int frameNo, const VSize &size, bool keepAspectRatio);
|
||||
VSize size() const { return mViewSize; }
|
||||
void buildRenderTree();
|
||||
const LOTLayerNode *renderTree() const;
|
||||
bool render(const rlottie::Surface &surface);
|
||||
void setValue(const std::string &keypath, LOTVariant &value);
|
||||
|
||||
private:
|
||||
SurfaceCache mSurfaceCache;
|
||||
VBitmap mSurface;
|
||||
VMatrix mScaleMatrix;
|
||||
VSize mViewSize;
|
||||
std::shared_ptr<model::Composition> mModel;
|
||||
Layer * mRootLayer{nullptr};
|
||||
VArenaAlloc mAllocator{2048};
|
||||
int mCurFrameNo;
|
||||
bool mKeepAspectRatio{true};
|
||||
};
|
||||
|
||||
class Layer {
|
||||
public:
|
||||
virtual ~Layer() = default;
|
||||
Layer &operator=(Layer &&) noexcept = delete;
|
||||
Layer(model::Layer *layerData);
|
||||
int id() const { return mLayerData->id(); }
|
||||
int parentId() const { return mLayerData->parentId(); }
|
||||
void setParentLayer(Layer *parent) { mParentLayer = parent; }
|
||||
void setComplexContent(bool value) { mComplexContent = value; }
|
||||
bool complexContent() const { return mComplexContent; }
|
||||
virtual void update(int frameNo, const VMatrix &parentMatrix,
|
||||
float parentAlpha);
|
||||
VMatrix matrix(int frameNo) const;
|
||||
void preprocess(const VRect &clip);
|
||||
virtual DrawableList renderList() { return {}; }
|
||||
virtual void render(VPainter *painter, const VRle &mask,
|
||||
const VRle &matteRle, SurfaceCache &cache);
|
||||
bool hasMatte()
|
||||
{
|
||||
if (mLayerData->mMatteType == model::MatteType::None) return false;
|
||||
return true;
|
||||
}
|
||||
model::MatteType matteType() const { return mLayerData->mMatteType; }
|
||||
bool visible() const;
|
||||
virtual void buildLayerNode();
|
||||
LOTLayerNode & clayer() { return mCApiData->mLayer; }
|
||||
std::vector<LOTLayerNode *> &clayers() { return mCApiData->mLayers; }
|
||||
std::vector<LOTMask> & cmasks() { return mCApiData->mMasks; }
|
||||
std::vector<LOTNode *> & cnodes() { return mCApiData->mCNodeList; }
|
||||
const char * name() const { return mLayerData->name(); }
|
||||
virtual bool resolveKeyPath(LOTKeyPath &keyPath, uint depth,
|
||||
LOTVariant &value);
|
||||
|
||||
protected:
|
||||
virtual void preprocessStage(const VRect &clip) = 0;
|
||||
virtual void updateContent() = 0;
|
||||
inline VMatrix combinedMatrix() const { return mCombinedMatrix; }
|
||||
inline int frameNo() const { return mFrameNo; }
|
||||
inline float combinedAlpha() const { return mCombinedAlpha; }
|
||||
inline bool isStatic() const { return mLayerData->isStatic(); }
|
||||
float opacity(int frameNo) const { return mLayerData->opacity(frameNo); }
|
||||
inline DirtyFlag flag() const { return mDirtyFlag; }
|
||||
bool skipRendering() const
|
||||
{
|
||||
return (!visible() || vIsZero(combinedAlpha()));
|
||||
}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<LayerMask> mLayerMask;
|
||||
model::Layer * mLayerData{nullptr};
|
||||
Layer * mParentLayer{nullptr};
|
||||
VMatrix mCombinedMatrix;
|
||||
float mCombinedAlpha{0.0};
|
||||
int mFrameNo{-1};
|
||||
DirtyFlag mDirtyFlag{DirtyFlagBit::All};
|
||||
bool mComplexContent{false};
|
||||
std::unique_ptr<CApiData> mCApiData;
|
||||
};
|
||||
|
||||
class CompLayer final : public Layer {
|
||||
public:
|
||||
explicit CompLayer(model::Layer *layerData, VArenaAlloc *allocator);
|
||||
|
||||
void render(VPainter *painter, const VRle &mask, const VRle &matteRle,
|
||||
SurfaceCache &cache) final;
|
||||
void buildLayerNode() final;
|
||||
bool resolveKeyPath(LOTKeyPath &keyPath, uint depth,
|
||||
LOTVariant &value) override;
|
||||
|
||||
protected:
|
||||
void preprocessStage(const VRect &clip) final;
|
||||
void updateContent() final;
|
||||
|
||||
private:
|
||||
void renderHelper(VPainter *painter, const VRle &mask, const VRle &matteRle,
|
||||
SurfaceCache &cache);
|
||||
void renderMatteLayer(VPainter *painter, const VRle &inheritMask,
|
||||
const VRle &matteRle, Layer *layer, Layer *src,
|
||||
SurfaceCache &cache);
|
||||
|
||||
private:
|
||||
std::vector<Layer *> mLayers;
|
||||
std::unique_ptr<Clipper> mClipper;
|
||||
};
|
||||
|
||||
class SolidLayer final : public Layer {
|
||||
public:
|
||||
explicit SolidLayer(model::Layer *layerData);
|
||||
void buildLayerNode() final;
|
||||
DrawableList renderList() final;
|
||||
|
||||
protected:
|
||||
void preprocessStage(const VRect &clip) final;
|
||||
void updateContent() final;
|
||||
|
||||
private:
|
||||
Drawable mRenderNode;
|
||||
VPath mPath;
|
||||
VDrawable *mDrawableList{nullptr}; // to work with the Span api
|
||||
};
|
||||
|
||||
class Group;
|
||||
|
||||
class ShapeLayer final : public Layer {
|
||||
public:
|
||||
explicit ShapeLayer(model::Layer *layerData, VArenaAlloc *allocator);
|
||||
DrawableList renderList() final;
|
||||
void buildLayerNode() final;
|
||||
bool resolveKeyPath(LOTKeyPath &keyPath, uint depth,
|
||||
LOTVariant &value) override;
|
||||
|
||||
protected:
|
||||
void preprocessStage(const VRect &clip) final;
|
||||
void updateContent() final;
|
||||
std::vector<VDrawable *> mDrawableList;
|
||||
Group * mRoot{nullptr};
|
||||
};
|
||||
|
||||
class NullLayer final : public Layer {
|
||||
public:
|
||||
explicit NullLayer(model::Layer *layerData);
|
||||
|
||||
protected:
|
||||
void preprocessStage(const VRect &) final {}
|
||||
void updateContent() final;
|
||||
};
|
||||
|
||||
class ImageLayer final : public Layer {
|
||||
public:
|
||||
explicit ImageLayer(model::Layer *layerData);
|
||||
void buildLayerNode() final;
|
||||
DrawableList renderList() final;
|
||||
|
||||
protected:
|
||||
void preprocessStage(const VRect &clip) final;
|
||||
void updateContent() final;
|
||||
|
||||
private:
|
||||
Drawable mRenderNode;
|
||||
VTexture mTexture;
|
||||
VPath mPath;
|
||||
VDrawable *mDrawableList{nullptr}; // to work with the Span api
|
||||
};
|
||||
|
||||
class Object {
|
||||
public:
|
||||
enum class Type : uchar { Unknown, Group, Shape, Paint, Trim };
|
||||
virtual ~Object() = default;
|
||||
Object & operator=(Object &&) noexcept = delete;
|
||||
virtual void update(int frameNo, const VMatrix &parentMatrix,
|
||||
float parentAlpha, const DirtyFlag &flag) = 0;
|
||||
virtual void renderList(std::vector<VDrawable *> &) {}
|
||||
virtual bool resolveKeyPath(LOTKeyPath &, uint, LOTVariant &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual Object::Type type() const { return Object::Type::Unknown; }
|
||||
};
|
||||
|
||||
class Shape;
|
||||
class Group : public Object {
|
||||
public:
|
||||
Group() = default;
|
||||
explicit Group(model::Group *data, VArenaAlloc *allocator);
|
||||
void addChildren(model::Group *data, VArenaAlloc *allocator);
|
||||
void update(int frameNo, const VMatrix &parentMatrix, float parentAlpha,
|
||||
const DirtyFlag &flag) override;
|
||||
void applyTrim();
|
||||
void processTrimItems(std::vector<Shape *> &list);
|
||||
void processPaintItems(std::vector<Shape *> &list);
|
||||
void renderList(std::vector<VDrawable *> &list) override;
|
||||
Object::Type type() const final { return Object::Type::Group; }
|
||||
const VMatrix &matrix() const { return mMatrix; }
|
||||
const char * name() const
|
||||
{
|
||||
static const char *TAG = "__";
|
||||
return mModel.hasModel() ? mModel.name() : TAG;
|
||||
}
|
||||
bool resolveKeyPath(LOTKeyPath &keyPath, uint depth,
|
||||
LOTVariant &value) override;
|
||||
|
||||
protected:
|
||||
std::vector<Object *> mContents;
|
||||
VMatrix mMatrix;
|
||||
|
||||
private:
|
||||
model::Filter<model::Group> mModel;
|
||||
};
|
||||
|
||||
class Shape : public Object {
|
||||
public:
|
||||
Shape(bool staticPath) : mStaticPath(staticPath) {}
|
||||
void update(int frameNo, const VMatrix &parentMatrix, float parentAlpha,
|
||||
const DirtyFlag &flag) final;
|
||||
Object::Type type() const final { return Object::Type::Shape; }
|
||||
bool dirty() const { return mDirtyPath; }
|
||||
const VPath &localPath() const { return mTemp; }
|
||||
void finalPath(VPath &result);
|
||||
void updatePath(const VPath &path)
|
||||
{
|
||||
mTemp = path;
|
||||
mDirtyPath = true;
|
||||
}
|
||||
bool staticPath() const { return mStaticPath; }
|
||||
void setParent(Group *parent) { mParent = parent; }
|
||||
Group *parent() const { return mParent; }
|
||||
|
||||
protected:
|
||||
virtual void updatePath(VPath &path, int frameNo) = 0;
|
||||
virtual bool hasChanged(int prevFrame, int curFrame) = 0;
|
||||
|
||||
private:
|
||||
bool hasChanged(int frameNo)
|
||||
{
|
||||
int prevFrame = mFrameNo;
|
||||
mFrameNo = frameNo;
|
||||
if (prevFrame == -1) return true;
|
||||
if (mStaticPath || (prevFrame == frameNo)) return false;
|
||||
return hasChanged(prevFrame, frameNo);
|
||||
}
|
||||
Group *mParent{nullptr};
|
||||
VPath mLocalPath;
|
||||
VPath mTemp;
|
||||
int mFrameNo{-1};
|
||||
bool mDirtyPath{true};
|
||||
bool mStaticPath;
|
||||
};
|
||||
|
||||
class Rect final : public Shape {
|
||||
public:
|
||||
explicit Rect(model::Rect *data);
|
||||
|
||||
protected:
|
||||
void updatePath(VPath &path, int frameNo) final;
|
||||
model::Rect *mData{nullptr};
|
||||
|
||||
bool hasChanged(int prevFrame, int curFrame) final
|
||||
{
|
||||
return (mData->mPos.changed(prevFrame, curFrame) ||
|
||||
mData->mSize.changed(prevFrame, curFrame) ||
|
||||
mData->roundnessChanged(prevFrame, curFrame));
|
||||
}
|
||||
};
|
||||
|
||||
class Ellipse final : public Shape {
|
||||
public:
|
||||
explicit Ellipse(model::Ellipse *data);
|
||||
|
||||
private:
|
||||
void updatePath(VPath &path, int frameNo) final;
|
||||
model::Ellipse *mData{nullptr};
|
||||
bool hasChanged(int prevFrame, int curFrame) final
|
||||
{
|
||||
return (mData->mPos.changed(prevFrame, curFrame) ||
|
||||
mData->mSize.changed(prevFrame, curFrame));
|
||||
}
|
||||
};
|
||||
|
||||
class Path final : public Shape {
|
||||
public:
|
||||
explicit Path(model::Path *data);
|
||||
|
||||
private:
|
||||
void updatePath(VPath &path, int frameNo) final;
|
||||
model::Path *mData{nullptr};
|
||||
bool hasChanged(int prevFrame, int curFrame) final
|
||||
{
|
||||
return mData->mShape.changed(prevFrame, curFrame);
|
||||
}
|
||||
};
|
||||
|
||||
class Polystar final : public Shape {
|
||||
public:
|
||||
explicit Polystar(model::Polystar *data);
|
||||
|
||||
private:
|
||||
void updatePath(VPath &path, int frameNo) final;
|
||||
model::Polystar *mData{nullptr};
|
||||
|
||||
bool hasChanged(int prevFrame, int curFrame) final
|
||||
{
|
||||
return (mData->mPos.changed(prevFrame, curFrame) ||
|
||||
mData->mPointCount.changed(prevFrame, curFrame) ||
|
||||
mData->mInnerRadius.changed(prevFrame, curFrame) ||
|
||||
mData->mOuterRadius.changed(prevFrame, curFrame) ||
|
||||
mData->mInnerRoundness.changed(prevFrame, curFrame) ||
|
||||
mData->mOuterRoundness.changed(prevFrame, curFrame) ||
|
||||
mData->mRotation.changed(prevFrame, curFrame));
|
||||
}
|
||||
};
|
||||
|
||||
class Paint : public Object {
|
||||
public:
|
||||
Paint(bool staticContent);
|
||||
void addPathItems(std::vector<Shape *> &list, size_t startOffset);
|
||||
void update(int frameNo, const VMatrix &parentMatrix, float parentAlpha,
|
||||
const DirtyFlag &flag) override;
|
||||
void renderList(std::vector<VDrawable *> &list) final;
|
||||
Object::Type type() const final { return Object::Type::Paint; }
|
||||
|
||||
protected:
|
||||
virtual bool updateContent(int frameNo, const VMatrix &matrix,
|
||||
float alpha) = 0;
|
||||
|
||||
private:
|
||||
void updateRenderNode();
|
||||
|
||||
protected:
|
||||
std::vector<Shape *> mPathItems;
|
||||
Drawable mDrawable;
|
||||
VPath mPath;
|
||||
DirtyFlag mFlag;
|
||||
bool mStaticContent;
|
||||
bool mRenderNodeUpdate{true};
|
||||
bool mContentToRender{true};
|
||||
};
|
||||
|
||||
class Fill final : public Paint {
|
||||
public:
|
||||
explicit Fill(model::Fill *data);
|
||||
|
||||
protected:
|
||||
bool updateContent(int frameNo, const VMatrix &matrix, float alpha) final;
|
||||
bool resolveKeyPath(LOTKeyPath &keyPath, uint depth,
|
||||
LOTVariant &value) final;
|
||||
|
||||
private:
|
||||
model::Filter<model::Fill> mModel;
|
||||
};
|
||||
|
||||
class GradientFill final : public Paint {
|
||||
public:
|
||||
explicit GradientFill(model::GradientFill *data);
|
||||
|
||||
protected:
|
||||
bool updateContent(int frameNo, const VMatrix &matrix, float alpha) final;
|
||||
|
||||
private:
|
||||
model::GradientFill * mData{nullptr};
|
||||
std::unique_ptr<VGradient> mGradient;
|
||||
};
|
||||
|
||||
class Stroke : public Paint {
|
||||
public:
|
||||
explicit Stroke(model::Stroke *data);
|
||||
|
||||
protected:
|
||||
bool updateContent(int frameNo, const VMatrix &matrix, float alpha) final;
|
||||
bool resolveKeyPath(LOTKeyPath &keyPath, uint depth,
|
||||
LOTVariant &value) final;
|
||||
|
||||
private:
|
||||
model::Filter<model::Stroke> mModel;
|
||||
};
|
||||
|
||||
class GradientStroke final : public Paint {
|
||||
public:
|
||||
explicit GradientStroke(model::GradientStroke *data);
|
||||
|
||||
protected:
|
||||
bool updateContent(int frameNo, const VMatrix &matrix, float alpha) final;
|
||||
|
||||
private:
|
||||
model::GradientStroke * mData{nullptr};
|
||||
std::unique_ptr<VGradient> mGradient;
|
||||
};
|
||||
|
||||
class Trim final : public Object {
|
||||
public:
|
||||
explicit Trim(model::Trim *data) : mData(data) {}
|
||||
void update(int frameNo, const VMatrix &parentMatrix, float parentAlpha,
|
||||
const DirtyFlag &flag) final;
|
||||
Object::Type type() const final { return Object::Type::Trim; }
|
||||
void update();
|
||||
void addPathItems(std::vector<Shape *> &list, size_t startOffset);
|
||||
|
||||
private:
|
||||
bool pathDirty() const
|
||||
{
|
||||
for (auto &i : mPathItems) {
|
||||
if (i->dirty()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
struct Cache {
|
||||
int mFrameNo{-1};
|
||||
model::Trim::Segment mSegment{};
|
||||
};
|
||||
Cache mCache;
|
||||
std::vector<Shape *> mPathItems;
|
||||
model::Trim * mData{nullptr};
|
||||
VPathMesure mPathMesure;
|
||||
bool mDirty{true};
|
||||
};
|
||||
|
||||
class Repeater final : public Group {
|
||||
public:
|
||||
explicit Repeater(model::Repeater *data, VArenaAlloc *allocator);
|
||||
void update(int frameNo, const VMatrix &parentMatrix, float parentAlpha,
|
||||
const DirtyFlag &flag) final;
|
||||
void renderList(std::vector<VDrawable *> &list) final;
|
||||
|
||||
private:
|
||||
model::Repeater *mRepeaterData{nullptr};
|
||||
bool mHidden{false};
|
||||
int mCopies{0};
|
||||
};
|
||||
|
||||
} // namespace renderer
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace rlottie
|
||||
|
||||
#endif // LOTTIEITEM_H
|
||||
339
vendor/github.com/Benau/go_rlottie/lottie_lottieitem_capi.cpp
generated
vendored
Normal file
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* Implements LottieItem functions needed
|
||||
* to support renderTree() api.
|
||||
* Moving all those implementation to its own
|
||||
* file make clear separation as well easy of
|
||||
* maintenance.
|
||||
*/
|
||||
|
||||
#include "lottie_lottieitem.h"
|
||||
#include "vector_vdasher.h"
|
||||
|
||||
using namespace rlottie::internal;
|
||||
|
||||
renderer::CApiData::CApiData()
|
||||
{
|
||||
mLayer.mMaskList.ptr = nullptr;
|
||||
mLayer.mMaskList.size = 0;
|
||||
mLayer.mLayerList.ptr = nullptr;
|
||||
mLayer.mLayerList.size = 0;
|
||||
mLayer.mNodeList.ptr = nullptr;
|
||||
mLayer.mNodeList.size = 0;
|
||||
mLayer.mMatte = MatteNone;
|
||||
mLayer.mVisible = 0;
|
||||
mLayer.mAlpha = 255;
|
||||
mLayer.mClipPath.ptPtr = nullptr;
|
||||
mLayer.mClipPath.elmPtr = nullptr;
|
||||
mLayer.mClipPath.ptCount = 0;
|
||||
mLayer.mClipPath.elmCount = 0;
|
||||
mLayer.keypath = nullptr;
|
||||
}
|
||||
|
||||
void renderer::Composition::buildRenderTree()
|
||||
{
|
||||
mRootLayer->buildLayerNode();
|
||||
}
|
||||
|
||||
const LOTLayerNode *renderer::Composition::renderTree() const
|
||||
{
|
||||
return &mRootLayer->clayer();
|
||||
}
|
||||
|
||||
void renderer::CompLayer::buildLayerNode()
|
||||
{
|
||||
renderer::Layer::buildLayerNode();
|
||||
if (mClipper) {
|
||||
const auto &elm = mClipper->mPath.elements();
|
||||
const auto &pts = mClipper->mPath.points();
|
||||
auto ptPtr = reinterpret_cast<const float *>(pts.data());
|
||||
auto elmPtr = reinterpret_cast<const char *>(elm.data());
|
||||
clayer().mClipPath.ptPtr = ptPtr;
|
||||
clayer().mClipPath.elmPtr = elmPtr;
|
||||
clayer().mClipPath.ptCount = 2 * pts.size();
|
||||
clayer().mClipPath.elmCount = elm.size();
|
||||
}
|
||||
if (mLayers.size() != clayers().size()) {
|
||||
for (const auto &layer : mLayers) {
|
||||
layer->buildLayerNode();
|
||||
clayers().push_back(&layer->clayer());
|
||||
}
|
||||
clayer().mLayerList.ptr = clayers().data();
|
||||
clayer().mLayerList.size = clayers().size();
|
||||
} else {
|
||||
for (const auto &layer : mLayers) {
|
||||
layer->buildLayerNode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void renderer::ShapeLayer::buildLayerNode()
|
||||
{
|
||||
renderer::Layer::buildLayerNode();
|
||||
|
||||
auto renderlist = renderList();
|
||||
|
||||
cnodes().clear();
|
||||
for (auto &i : renderlist) {
|
||||
auto lotDrawable = static_cast<renderer::Drawable *>(i);
|
||||
lotDrawable->sync();
|
||||
cnodes().push_back(lotDrawable->mCNode.get());
|
||||
}
|
||||
clayer().mNodeList.ptr = cnodes().data();
|
||||
clayer().mNodeList.size = cnodes().size();
|
||||
}
|
||||
|
||||
void renderer::Layer::buildLayerNode()
|
||||
{
|
||||
if (!mCApiData) {
|
||||
mCApiData = std::make_unique<renderer::CApiData>();
|
||||
clayer().keypath = name();
|
||||
}
|
||||
if (complexContent()) clayer().mAlpha = uchar(combinedAlpha() * 255.f);
|
||||
clayer().mVisible = visible();
|
||||
// update matte
|
||||
if (hasMatte()) {
|
||||
switch (mLayerData->mMatteType) {
|
||||
case model::MatteType::Alpha:
|
||||
clayer().mMatte = MatteAlpha;
|
||||
break;
|
||||
case model::MatteType::AlphaInv:
|
||||
clayer().mMatte = MatteAlphaInv;
|
||||
break;
|
||||
case model::MatteType::Luma:
|
||||
clayer().mMatte = MatteLuma;
|
||||
break;
|
||||
case model::MatteType::LumaInv:
|
||||
clayer().mMatte = MatteLumaInv;
|
||||
break;
|
||||
default:
|
||||
clayer().mMatte = MatteNone;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mLayerMask) {
|
||||
cmasks().clear();
|
||||
cmasks().resize(mLayerMask->mMasks.size());
|
||||
size_t i = 0;
|
||||
for (const auto &mask : mLayerMask->mMasks) {
|
||||
auto & cNode = cmasks()[i++];
|
||||
const auto &elm = mask.mFinalPath.elements();
|
||||
const auto &pts = mask.mFinalPath.points();
|
||||
auto ptPtr = reinterpret_cast<const float *>(pts.data());
|
||||
auto elmPtr = reinterpret_cast<const char *>(elm.data());
|
||||
cNode.mPath.ptPtr = ptPtr;
|
||||
cNode.mPath.ptCount = 2 * pts.size();
|
||||
cNode.mPath.elmPtr = elmPtr;
|
||||
cNode.mPath.elmCount = elm.size();
|
||||
cNode.mAlpha = uchar(mask.mCombinedAlpha * 255.0f);
|
||||
switch (mask.maskMode()) {
|
||||
case model::Mask::Mode::Add:
|
||||
cNode.mMode = MaskAdd;
|
||||
break;
|
||||
case model::Mask::Mode::Substarct:
|
||||
cNode.mMode = MaskSubstract;
|
||||
break;
|
||||
case model::Mask::Mode::Intersect:
|
||||
cNode.mMode = MaskIntersect;
|
||||
break;
|
||||
case model::Mask::Mode::Difference:
|
||||
cNode.mMode = MaskDifference;
|
||||
break;
|
||||
default:
|
||||
cNode.mMode = MaskAdd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
clayer().mMaskList.ptr = cmasks().data();
|
||||
clayer().mMaskList.size = cmasks().size();
|
||||
}
|
||||
}
|
||||
|
||||
void renderer::SolidLayer::buildLayerNode()
|
||||
{
|
||||
renderer::Layer::buildLayerNode();
|
||||
|
||||
auto renderlist = renderList();
|
||||
|
||||
cnodes().clear();
|
||||
for (auto &i : renderlist) {
|
||||
auto lotDrawable = static_cast<renderer::Drawable *>(i);
|
||||
lotDrawable->sync();
|
||||
cnodes().push_back(lotDrawable->mCNode.get());
|
||||
}
|
||||
clayer().mNodeList.ptr = cnodes().data();
|
||||
clayer().mNodeList.size = cnodes().size();
|
||||
}
|
||||
|
||||
void renderer::ImageLayer::buildLayerNode()
|
||||
{
|
||||
renderer::Layer::buildLayerNode();
|
||||
|
||||
auto renderlist = renderList();
|
||||
|
||||
cnodes().clear();
|
||||
for (auto &i : renderlist) {
|
||||
auto lotDrawable = static_cast<renderer::Drawable *>(i);
|
||||
lotDrawable->sync();
|
||||
|
||||
lotDrawable->mCNode->mImageInfo.data =
|
||||
lotDrawable->mBrush.mTexture->mBitmap.data();
|
||||
lotDrawable->mCNode->mImageInfo.width =
|
||||
int(lotDrawable->mBrush.mTexture->mBitmap.width());
|
||||
lotDrawable->mCNode->mImageInfo.height =
|
||||
int(lotDrawable->mBrush.mTexture->mBitmap.height());
|
||||
|
||||
lotDrawable->mCNode->mImageInfo.mMatrix.m11 = combinedMatrix().m_11();
|
||||
lotDrawable->mCNode->mImageInfo.mMatrix.m12 = combinedMatrix().m_12();
|
||||
lotDrawable->mCNode->mImageInfo.mMatrix.m13 = combinedMatrix().m_13();
|
||||
|
||||
lotDrawable->mCNode->mImageInfo.mMatrix.m21 = combinedMatrix().m_21();
|
||||
lotDrawable->mCNode->mImageInfo.mMatrix.m22 = combinedMatrix().m_22();
|
||||
lotDrawable->mCNode->mImageInfo.mMatrix.m23 = combinedMatrix().m_23();
|
||||
|
||||
lotDrawable->mCNode->mImageInfo.mMatrix.m31 = combinedMatrix().m_tx();
|
||||
lotDrawable->mCNode->mImageInfo.mMatrix.m32 = combinedMatrix().m_ty();
|
||||
lotDrawable->mCNode->mImageInfo.mMatrix.m33 = combinedMatrix().m_33();
|
||||
|
||||
// Alpha calculation already combined.
|
||||
lotDrawable->mCNode->mImageInfo.mAlpha =
|
||||
uchar(lotDrawable->mBrush.mTexture->mAlpha);
|
||||
|
||||
cnodes().push_back(lotDrawable->mCNode.get());
|
||||
}
|
||||
clayer().mNodeList.ptr = cnodes().data();
|
||||
clayer().mNodeList.size = cnodes().size();
|
||||
}
|
||||
|
||||
static void updateGStops(LOTNode *n, const VGradient *grad)
|
||||
{
|
||||
if (grad->mStops.size() != n->mGradient.stopCount) {
|
||||
if (n->mGradient.stopCount) free(n->mGradient.stopPtr);
|
||||
n->mGradient.stopCount = grad->mStops.size();
|
||||
n->mGradient.stopPtr = (LOTGradientStop *)malloc(
|
||||
n->mGradient.stopCount * sizeof(LOTGradientStop));
|
||||
}
|
||||
|
||||
LOTGradientStop *ptr = n->mGradient.stopPtr;
|
||||
for (const auto &i : grad->mStops) {
|
||||
ptr->pos = i.first;
|
||||
ptr->a = uchar(i.second.alpha() * grad->alpha());
|
||||
ptr->r = i.second.red();
|
||||
ptr->g = i.second.green();
|
||||
ptr->b = i.second.blue();
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
void renderer::Drawable::sync()
|
||||
{
|
||||
if (!mCNode) {
|
||||
mCNode = std::make_unique<LOTNode>();
|
||||
mCNode->mGradient.stopPtr = nullptr;
|
||||
mCNode->mGradient.stopCount = 0;
|
||||
}
|
||||
|
||||
mCNode->mFlag = ChangeFlagNone;
|
||||
if (mFlag & DirtyState::None) return;
|
||||
|
||||
if (mFlag & DirtyState::Path) {
|
||||
applyDashOp();
|
||||
const std::vector<VPath::Element> &elm = mPath.elements();
|
||||
const std::vector<VPointF> & pts = mPath.points();
|
||||
const float *ptPtr = reinterpret_cast<const float *>(pts.data());
|
||||
const char * elmPtr = reinterpret_cast<const char *>(elm.data());
|
||||
mCNode->mPath.elmPtr = elmPtr;
|
||||
mCNode->mPath.elmCount = elm.size();
|
||||
mCNode->mPath.ptPtr = ptPtr;
|
||||
mCNode->mPath.ptCount = 2 * pts.size();
|
||||
mCNode->mFlag |= ChangeFlagPath;
|
||||
mCNode->keypath = name();
|
||||
}
|
||||
|
||||
if (mStrokeInfo) {
|
||||
mCNode->mStroke.width = mStrokeInfo->width;
|
||||
mCNode->mStroke.miterLimit = mStrokeInfo->miterLimit;
|
||||
mCNode->mStroke.enable = 1;
|
||||
|
||||
switch (mStrokeInfo->cap) {
|
||||
case CapStyle::Flat:
|
||||
mCNode->mStroke.cap = LOTCapStyle::CapFlat;
|
||||
break;
|
||||
case CapStyle::Square:
|
||||
mCNode->mStroke.cap = LOTCapStyle::CapSquare;
|
||||
break;
|
||||
case CapStyle::Round:
|
||||
mCNode->mStroke.cap = LOTCapStyle::CapRound;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (mStrokeInfo->join) {
|
||||
case JoinStyle::Miter:
|
||||
mCNode->mStroke.join = LOTJoinStyle::JoinMiter;
|
||||
break;
|
||||
case JoinStyle::Bevel:
|
||||
mCNode->mStroke.join = LOTJoinStyle::JoinBevel;
|
||||
break;
|
||||
case JoinStyle::Round:
|
||||
mCNode->mStroke.join = LOTJoinStyle::JoinRound;
|
||||
break;
|
||||
default:
|
||||
mCNode->mStroke.join = LOTJoinStyle::JoinMiter;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
mCNode->mStroke.enable = 0;
|
||||
}
|
||||
|
||||
switch (mFillRule) {
|
||||
case FillRule::EvenOdd:
|
||||
mCNode->mFillRule = LOTFillRule::FillEvenOdd;
|
||||
break;
|
||||
default:
|
||||
mCNode->mFillRule = LOTFillRule::FillWinding;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (mBrush.type()) {
|
||||
case VBrush::Type::Solid:
|
||||
mCNode->mBrushType = LOTBrushType::BrushSolid;
|
||||
mCNode->mColor.r = mBrush.mColor.r;
|
||||
mCNode->mColor.g = mBrush.mColor.g;
|
||||
mCNode->mColor.b = mBrush.mColor.b;
|
||||
mCNode->mColor.a = mBrush.mColor.a;
|
||||
break;
|
||||
case VBrush::Type::LinearGradient: {
|
||||
mCNode->mBrushType = LOTBrushType::BrushGradient;
|
||||
mCNode->mGradient.type = LOTGradientType::GradientLinear;
|
||||
VPointF s = mBrush.mGradient->mMatrix.map(
|
||||
{mBrush.mGradient->linear.x1, mBrush.mGradient->linear.y1});
|
||||
VPointF e = mBrush.mGradient->mMatrix.map(
|
||||
{mBrush.mGradient->linear.x2, mBrush.mGradient->linear.y2});
|
||||
mCNode->mGradient.start.x = s.x();
|
||||
mCNode->mGradient.start.y = s.y();
|
||||
mCNode->mGradient.end.x = e.x();
|
||||
mCNode->mGradient.end.y = e.y();
|
||||
updateGStops(mCNode.get(), mBrush.mGradient);
|
||||
break;
|
||||
}
|
||||
case VBrush::Type::RadialGradient: {
|
||||
mCNode->mBrushType = LOTBrushType::BrushGradient;
|
||||
mCNode->mGradient.type = LOTGradientType::GradientRadial;
|
||||
VPointF c = mBrush.mGradient->mMatrix.map(
|
||||
{mBrush.mGradient->radial.cx, mBrush.mGradient->radial.cy});
|
||||
VPointF f = mBrush.mGradient->mMatrix.map(
|
||||
{mBrush.mGradient->radial.fx, mBrush.mGradient->radial.fy});
|
||||
mCNode->mGradient.center.x = c.x();
|
||||
mCNode->mGradient.center.y = c.y();
|
||||
mCNode->mGradient.focal.x = f.x();
|
||||
mCNode->mGradient.focal.y = f.y();
|
||||
|
||||
float scale = mBrush.mGradient->mMatrix.scale();
|
||||
mCNode->mGradient.cradius = mBrush.mGradient->radial.cradius * scale;
|
||||
mCNode->mGradient.fradius = mBrush.mGradient->radial.fradius * scale;
|
||||
updateGStops(mCNode.get(), mBrush.mGradient);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
86
vendor/github.com/Benau/go_rlottie/lottie_lottiekeypath.cpp
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
#include "lottie_lottiekeypath.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
LOTKeyPath::LOTKeyPath(const std::string &keyPath)
|
||||
{
|
||||
std::stringstream ss(keyPath);
|
||||
std::string item;
|
||||
|
||||
while (getline(ss, item, '.')) {
|
||||
mKeys.push_back(item);
|
||||
}
|
||||
}
|
||||
|
||||
bool LOTKeyPath::matches(const std::string &key, uint depth)
|
||||
{
|
||||
if (skip(key)) {
|
||||
// This is an object we programatically create.
|
||||
return true;
|
||||
}
|
||||
if (depth > size()) {
|
||||
return false;
|
||||
}
|
||||
if ((mKeys[depth] == key) || (mKeys[depth] == "*") ||
|
||||
(mKeys[depth] == "**")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint LOTKeyPath::nextDepth(const std::string key, uint depth)
|
||||
{
|
||||
if (skip(key)) {
|
||||
// If it's a container then we added programatically and it isn't a part
|
||||
// of the keypath.
|
||||
return depth;
|
||||
}
|
||||
if (mKeys[depth] != "**") {
|
||||
// If it's not a globstar then it is part of the keypath.
|
||||
return depth + 1;
|
||||
}
|
||||
if (depth == size()) {
|
||||
// The last key is a globstar.
|
||||
return depth;
|
||||
}
|
||||
if (mKeys[depth + 1] == key) {
|
||||
// We are a globstar and the next key is our current key so consume
|
||||
// both.
|
||||
return depth + 2;
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
bool LOTKeyPath::fullyResolvesTo(const std::string key, uint depth)
|
||||
{
|
||||
if (depth > mKeys.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isLastDepth = (depth == size());
|
||||
|
||||
if (!isGlobstar(depth)) {
|
||||
bool matches = (mKeys[depth] == key) || isGlob(depth);
|
||||
return (isLastDepth || (depth == size() - 1 && endsWithGlobstar())) &&
|
||||
matches;
|
||||
}
|
||||
|
||||
bool isGlobstarButNextKeyMatches = !isLastDepth && mKeys[depth + 1] == key;
|
||||
if (isGlobstarButNextKeyMatches) {
|
||||
return depth == size() - 1 ||
|
||||
(depth == size() - 2 && endsWithGlobstar());
|
||||
}
|
||||
|
||||
if (isLastDepth) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (depth + 1 < size()) {
|
||||
// We are a globstar but there is more than 1 key after the globstar we
|
||||
// we can't fully match.
|
||||
return false;
|
||||
}
|
||||
// Return whether the next key (which we now know is the last one) is the
|
||||
// same as the current key.
|
||||
return mKeys[depth + 1] == key;
|
||||
}
|
||||
53
vendor/github.com/Benau/go_rlottie/lottie_lottiekeypath.h
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef LOTTIEKEYPATH_H
|
||||
#define LOTTIEKEYPATH_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "vector_vglobal.h"
|
||||
|
||||
class LOTKeyPath {
|
||||
public:
|
||||
LOTKeyPath(const std::string &keyPath);
|
||||
bool matches(const std::string &key, uint depth);
|
||||
uint nextDepth(const std::string key, uint depth);
|
||||
bool fullyResolvesTo(const std::string key, uint depth);
|
||||
|
||||
bool propagate(const std::string key, uint depth)
|
||||
{
|
||||
return skip(key) ? true : (depth < size()) || (mKeys[depth] == "**");
|
||||
}
|
||||
bool skip(const std::string &key) const { return key == "__"; }
|
||||
|
||||
private:
|
||||
bool isGlobstar(uint depth) const { return mKeys[depth] == "**"; }
|
||||
bool isGlob(uint depth) const { return mKeys[depth] == "*"; }
|
||||
bool endsWithGlobstar() const { return mKeys.back() == "**"; }
|
||||
size_t size() const { return mKeys.size() - 1; }
|
||||
|
||||
private:
|
||||
std::vector<std::string> mKeys;
|
||||
};
|
||||
|
||||
#endif // LOTTIEKEYPATH_H
|
||||
169
vendor/github.com/Benau/go_rlottie/lottie_lottieloader.cpp
generated
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include "lottie_lottiemodel.h"
|
||||
|
||||
using namespace rlottie::internal;
|
||||
|
||||
#ifdef LOTTIE_CACHE_SUPPORT
|
||||
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
class ModelCache {
|
||||
public:
|
||||
static ModelCache &instance()
|
||||
{
|
||||
static ModelCache singleton;
|
||||
return singleton;
|
||||
}
|
||||
std::shared_ptr<model::Composition> find(const std::string &key)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(mMutex);
|
||||
|
||||
if (!mcacheSize) return nullptr;
|
||||
|
||||
auto search = mHash.find(key);
|
||||
|
||||
return (search != mHash.end()) ? search->second : nullptr;
|
||||
}
|
||||
void add(const std::string &key, std::shared_ptr<model::Composition> value)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(mMutex);
|
||||
|
||||
if (!mcacheSize) return;
|
||||
|
||||
//@TODO just remove the 1st element
|
||||
// not the best of LRU logic
|
||||
if (mcacheSize == mHash.size()) mHash.erase(mHash.cbegin());
|
||||
|
||||
mHash[key] = std::move(value);
|
||||
}
|
||||
|
||||
void configureCacheSize(size_t cacheSize)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(mMutex);
|
||||
mcacheSize = cacheSize;
|
||||
|
||||
if (!mcacheSize) mHash.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
ModelCache() = default;
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<model::Composition>> mHash;
|
||||
std::mutex mMutex;
|
||||
size_t mcacheSize{10};
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
class ModelCache {
|
||||
public:
|
||||
static ModelCache &instance()
|
||||
{
|
||||
static ModelCache singleton;
|
||||
return singleton;
|
||||
}
|
||||
std::shared_ptr<model::Composition> find(const std::string &)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
void add(const std::string &, std::shared_ptr<model::Composition>) {}
|
||||
void configureCacheSize(size_t) {}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
static std::string dirname(const std::string &path)
|
||||
{
|
||||
const char *ptr = strrchr(path.c_str(), '/');
|
||||
#ifdef _WIN32
|
||||
if (ptr) ptr = strrchr(ptr + 1, '\\');
|
||||
#endif
|
||||
int len = int(ptr + 1 - path.c_str()); // +1 to include '/'
|
||||
return std::string(path, 0, len);
|
||||
}
|
||||
|
||||
void model::configureModelCacheSize(size_t cacheSize)
|
||||
{
|
||||
ModelCache::instance().configureCacheSize(cacheSize);
|
||||
}
|
||||
|
||||
std::shared_ptr<model::Composition> model::loadFromFile(const std::string &path,
|
||||
bool cachePolicy)
|
||||
{
|
||||
if (cachePolicy) {
|
||||
auto obj = ModelCache::instance().find(path);
|
||||
if (obj) return obj;
|
||||
}
|
||||
|
||||
std::ifstream f;
|
||||
f.open(path);
|
||||
|
||||
if (!f.is_open()) {
|
||||
vCritical << "failed to open file = " << path.c_str();
|
||||
return {};
|
||||
} else {
|
||||
std::string content;
|
||||
|
||||
std::getline(f, content, '\0');
|
||||
f.close();
|
||||
|
||||
if (content.empty()) return {};
|
||||
|
||||
auto obj = internal::model::parse(const_cast<char *>(content.c_str()),
|
||||
dirname(path));
|
||||
|
||||
if (obj && cachePolicy) ModelCache::instance().add(path, obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<model::Composition> model::loadFromData(
|
||||
std::string jsonData, const std::string &key, std::string resourcePath,
|
||||
bool cachePolicy)
|
||||
{
|
||||
if (cachePolicy) {
|
||||
auto obj = ModelCache::instance().find(key);
|
||||
if (obj) return obj;
|
||||
}
|
||||
|
||||
auto obj = internal::model::parse(const_cast<char *>(jsonData.c_str()),
|
||||
std::move(resourcePath));
|
||||
|
||||
if (obj && cachePolicy) ModelCache::instance().add(key, obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
std::shared_ptr<model::Composition> model::loadFromData(
|
||||
std::string jsonData, std::string resourcePath, model::ColorFilter filter)
|
||||
{
|
||||
return internal::model::parse(const_cast<char *>(jsonData.c_str()),
|
||||
std::move(resourcePath), std::move(filter));
|
||||
}
|
||||
390
vendor/github.com/Benau/go_rlottie/lottie_lottiemodel.cpp
generated
vendored
Normal file
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "lottie_lottiemodel.h"
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
#include <stack>
|
||||
#include "vector_vimageloader.h"
|
||||
#include "vector_vline.h"
|
||||
|
||||
using namespace rlottie::internal;
|
||||
|
||||
/*
|
||||
* We process the iterator objects in the children list
|
||||
* by iterating from back to front. when we find a repeater object
|
||||
* we remove the objects from satrt till repeater object and then place
|
||||
* under a new shape group object which we add it as children to the repeater
|
||||
* object.
|
||||
* Then we visit the childrens of the newly created shape group object to
|
||||
* process the remaining repeater object(when children list contains more than
|
||||
* one repeater).
|
||||
*
|
||||
*/
|
||||
class LottieRepeaterProcesser {
|
||||
public:
|
||||
void visitChildren(model::Group *obj)
|
||||
{
|
||||
for (auto i = obj->mChildren.rbegin(); i != obj->mChildren.rend();
|
||||
++i) {
|
||||
auto child = (*i);
|
||||
if (child->type() == model::Object::Type::Repeater) {
|
||||
model::Repeater *repeater =
|
||||
static_cast<model::Repeater *>(child);
|
||||
// check if this repeater is already processed
|
||||
// can happen if the layer is an asset and referenced by
|
||||
// multiple layer.
|
||||
if (repeater->processed()) continue;
|
||||
|
||||
repeater->markProcessed();
|
||||
|
||||
auto content = repeater->content();
|
||||
// 1. increment the reverse iterator to point to the
|
||||
// object before the repeater
|
||||
++i;
|
||||
// 2. move all the children till repater to the group
|
||||
std::move(obj->mChildren.begin(), i.base(),
|
||||
back_inserter(content->mChildren));
|
||||
// 3. erase the objects from the original children list
|
||||
obj->mChildren.erase(obj->mChildren.begin(), i.base());
|
||||
|
||||
// 5. visit newly created group to process remaining repeater
|
||||
// object.
|
||||
visitChildren(content);
|
||||
// 6. exit the loop as the current iterators are invalid
|
||||
break;
|
||||
}
|
||||
visit(child);
|
||||
}
|
||||
}
|
||||
|
||||
void visit(model::Object *obj)
|
||||
{
|
||||
switch (obj->type()) {
|
||||
case model::Object::Type::Group:
|
||||
case model::Object::Type::Layer: {
|
||||
visitChildren(static_cast<model::Group *>(obj));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class LottieUpdateStatVisitor {
|
||||
model::Composition::Stats *stat;
|
||||
|
||||
public:
|
||||
explicit LottieUpdateStatVisitor(model::Composition::Stats *s) : stat(s) {}
|
||||
void visitChildren(model::Group *obj)
|
||||
{
|
||||
for (const auto &child : obj->mChildren) {
|
||||
if (child) visit(child);
|
||||
}
|
||||
}
|
||||
void visitLayer(model::Layer *layer)
|
||||
{
|
||||
switch (layer->mLayerType) {
|
||||
case model::Layer::Type::Precomp:
|
||||
stat->precompLayerCount++;
|
||||
break;
|
||||
case model::Layer::Type::Null:
|
||||
stat->nullLayerCount++;
|
||||
break;
|
||||
case model::Layer::Type::Shape:
|
||||
stat->shapeLayerCount++;
|
||||
break;
|
||||
case model::Layer::Type::Solid:
|
||||
stat->solidLayerCount++;
|
||||
break;
|
||||
case model::Layer::Type::Image:
|
||||
stat->imageLayerCount++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
visitChildren(layer);
|
||||
}
|
||||
void visit(model::Object *obj)
|
||||
{
|
||||
switch (obj->type()) {
|
||||
case model::Object::Type::Layer: {
|
||||
visitLayer(static_cast<model::Layer *>(obj));
|
||||
break;
|
||||
}
|
||||
case model::Object::Type::Repeater: {
|
||||
visitChildren(static_cast<model::Repeater *>(obj)->content());
|
||||
break;
|
||||
}
|
||||
case model::Object::Type::Group: {
|
||||
visitChildren(static_cast<model::Group *>(obj));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void model::Composition::processRepeaterObjects()
|
||||
{
|
||||
LottieRepeaterProcesser visitor;
|
||||
visitor.visit(mRootLayer);
|
||||
}
|
||||
|
||||
void model::Composition::updateStats()
|
||||
{
|
||||
LottieUpdateStatVisitor visitor(&mStats);
|
||||
visitor.visit(mRootLayer);
|
||||
}
|
||||
|
||||
VMatrix model::Repeater::Transform::matrix(int frameNo, float multiplier) const
|
||||
{
|
||||
VPointF scale = mScale.value(frameNo) / 100.f;
|
||||
scale.setX(std::pow(scale.x(), multiplier));
|
||||
scale.setY(std::pow(scale.y(), multiplier));
|
||||
VMatrix m;
|
||||
m.translate(mPosition.value(frameNo) * multiplier)
|
||||
.translate(mAnchor.value(frameNo))
|
||||
.scale(scale)
|
||||
.rotate(mRotation.value(frameNo) * multiplier)
|
||||
.translate(-mAnchor.value(frameNo));
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
VMatrix model::Transform::Data::matrix(int frameNo, bool autoOrient) const
|
||||
{
|
||||
VMatrix m;
|
||||
VPointF position;
|
||||
if (mExtra && mExtra->mSeparate) {
|
||||
position.setX(mExtra->mSeparateX.value(frameNo));
|
||||
position.setY(mExtra->mSeparateY.value(frameNo));
|
||||
} else {
|
||||
position = mPosition.value(frameNo);
|
||||
}
|
||||
|
||||
float angle = autoOrient ? mPosition.angle(frameNo) : 0;
|
||||
if (mExtra && mExtra->m3DData) {
|
||||
m.translate(position)
|
||||
.rotate(mExtra->m3DRz.value(frameNo) + angle)
|
||||
.rotate(mExtra->m3DRy.value(frameNo), VMatrix::Axis::Y)
|
||||
.rotate(mExtra->m3DRx.value(frameNo), VMatrix::Axis::X)
|
||||
.scale(mScale.value(frameNo) / 100.f)
|
||||
.translate(-mAnchor.value(frameNo));
|
||||
} else {
|
||||
m.translate(position)
|
||||
.rotate(mRotation.value(frameNo) + angle)
|
||||
.scale(mScale.value(frameNo) / 100.f)
|
||||
.translate(-mAnchor.value(frameNo));
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
void model::Dash::getDashInfo(int frameNo, std::vector<float> &result) const
|
||||
{
|
||||
result.clear();
|
||||
|
||||
if (mData.size() <= 1) return;
|
||||
|
||||
if (result.capacity() < mData.size()) result.reserve(mData.size() + 1);
|
||||
|
||||
for (const auto &elm : mData) result.push_back(elm.value(frameNo));
|
||||
|
||||
// if the size is even then we are missing last
|
||||
// gap information which is same as the last dash value
|
||||
// copy it from the last dash value.
|
||||
// NOTE: last value is the offset and last-1 is the last dash value.
|
||||
auto size = result.size();
|
||||
if ((size % 2) == 0) {
|
||||
// copy offset value to end.
|
||||
result.push_back(result.back());
|
||||
// copy dash value to gap.
|
||||
result[size - 1] = result[size - 2];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Both the color stops and opacity stops are in the same array.
|
||||
* There are {@link #colorPoints} colors sequentially as:
|
||||
* [
|
||||
* ...,
|
||||
* position,
|
||||
* red,
|
||||
* green,
|
||||
* blue,
|
||||
* ...
|
||||
* ]
|
||||
*
|
||||
* The remainder of the array is the opacity stops sequentially as:
|
||||
* [
|
||||
* ...,
|
||||
* position,
|
||||
* opacity,
|
||||
* ...
|
||||
* ]
|
||||
*/
|
||||
void model::Gradient::populate(VGradientStops &stops, int frameNo)
|
||||
{
|
||||
model::Gradient::Data gradData = mGradient.value(frameNo);
|
||||
auto size = gradData.mGradient.size();
|
||||
float * ptr = gradData.mGradient.data();
|
||||
int colorPoints = mColorPoints;
|
||||
if (colorPoints == -1) { // for legacy bodymovin (ref: lottie-android)
|
||||
colorPoints = int(size / 4);
|
||||
}
|
||||
auto opacityArraySize = size - colorPoints * 4;
|
||||
float *opacityPtr = ptr + (colorPoints * 4);
|
||||
stops.clear();
|
||||
size_t j = 0;
|
||||
for (int i = 0; i < colorPoints; i++) {
|
||||
float colorStop = ptr[0];
|
||||
model::Color color = model::Color(ptr[1], ptr[2], ptr[3]);
|
||||
if (opacityArraySize) {
|
||||
if (j == opacityArraySize) {
|
||||
// already reached the end
|
||||
float stop1 = opacityPtr[j - 4];
|
||||
float op1 = opacityPtr[j - 3];
|
||||
float stop2 = opacityPtr[j - 2];
|
||||
float op2 = opacityPtr[j - 1];
|
||||
if (colorStop > stop2) {
|
||||
stops.push_back(
|
||||
std::make_pair(colorStop, color.toColor(op2)));
|
||||
} else {
|
||||
float progress = (colorStop - stop1) / (stop2 - stop1);
|
||||
float opacity = op1 + progress * (op2 - op1);
|
||||
stops.push_back(
|
||||
std::make_pair(colorStop, color.toColor(opacity)));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for (; j < opacityArraySize; j += 2) {
|
||||
float opacityStop = opacityPtr[j];
|
||||
if (opacityStop < colorStop) {
|
||||
// add a color using opacity stop
|
||||
stops.push_back(std::make_pair(
|
||||
opacityStop, color.toColor(opacityPtr[j + 1])));
|
||||
continue;
|
||||
}
|
||||
// add a color using color stop
|
||||
if (j == 0) {
|
||||
stops.push_back(std::make_pair(
|
||||
colorStop, color.toColor(opacityPtr[j + 1])));
|
||||
} else {
|
||||
float progress = (colorStop - opacityPtr[j - 2]) /
|
||||
(opacityPtr[j] - opacityPtr[j - 2]);
|
||||
float opacity =
|
||||
opacityPtr[j - 1] +
|
||||
progress * (opacityPtr[j + 1] - opacityPtr[j - 1]);
|
||||
stops.push_back(
|
||||
std::make_pair(colorStop, color.toColor(opacity)));
|
||||
}
|
||||
j += 2;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
stops.push_back(std::make_pair(colorStop, color.toColor()));
|
||||
}
|
||||
ptr += 4;
|
||||
}
|
||||
}
|
||||
|
||||
void model::Gradient::update(std::unique_ptr<VGradient> &grad, int frameNo)
|
||||
{
|
||||
bool init = false;
|
||||
if (!grad) {
|
||||
if (mGradientType == 1)
|
||||
grad = std::make_unique<VGradient>(VGradient::Type::Linear);
|
||||
else
|
||||
grad = std::make_unique<VGradient>(VGradient::Type::Radial);
|
||||
grad->mSpread = VGradient::Spread::Pad;
|
||||
init = true;
|
||||
}
|
||||
|
||||
if (!mGradient.isStatic() || init) {
|
||||
populate(grad->mStops, frameNo);
|
||||
}
|
||||
|
||||
if (mGradientType == 1) { // linear gradient
|
||||
VPointF start = mStartPoint.value(frameNo);
|
||||
VPointF end = mEndPoint.value(frameNo);
|
||||
grad->linear.x1 = start.x();
|
||||
grad->linear.y1 = start.y();
|
||||
grad->linear.x2 = end.x();
|
||||
grad->linear.y2 = end.y();
|
||||
} else { // radial gradient
|
||||
VPointF start = mStartPoint.value(frameNo);
|
||||
VPointF end = mEndPoint.value(frameNo);
|
||||
grad->radial.cx = start.x();
|
||||
grad->radial.cy = start.y();
|
||||
grad->radial.cradius =
|
||||
VLine::length(start.x(), start.y(), end.x(), end.y());
|
||||
/*
|
||||
* Focal point is the point lives in highlight length distance from
|
||||
* center along the line (start, end) and rotated by highlight angle.
|
||||
* below calculation first finds the quadrant(angle) on which the point
|
||||
* lives by applying inverse slope formula then adds the rotation angle
|
||||
* to find the final angle. then point is retrived using circle equation
|
||||
* of center, angle and distance.
|
||||
*/
|
||||
float progress = mHighlightLength.value(frameNo) / 100.0f;
|
||||
if (vCompare(progress, 1.0f)) progress = 0.99f;
|
||||
float startAngle = VLine(start, end).angle();
|
||||
float highlightAngle = mHighlightAngle.value(frameNo);
|
||||
static constexpr float K_PI = 3.1415926f;
|
||||
float angle = (startAngle + highlightAngle) * (K_PI / 180.0f);
|
||||
grad->radial.fx =
|
||||
grad->radial.cx + std::cos(angle) * progress * grad->radial.cradius;
|
||||
grad->radial.fy =
|
||||
grad->radial.cy + std::sin(angle) * progress * grad->radial.cradius;
|
||||
// Lottie dosen't have any focal radius concept.
|
||||
grad->radial.fradius = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void model::Asset::loadImageData(std::string data)
|
||||
{
|
||||
if (!data.empty())
|
||||
mBitmap = VImageLoader::instance().load(data.c_str(), data.length());
|
||||
}
|
||||
|
||||
void model::Asset::loadImagePath(std::string path)
|
||||
{
|
||||
if (!path.empty()) mBitmap = VImageLoader::instance().load(path.c_str());
|
||||
}
|
||||
|
||||
std::vector<LayerInfo> model::Composition::layerInfoList() const
|
||||
{
|
||||
if (!mRootLayer || mRootLayer->mChildren.empty()) return {};
|
||||
|
||||
std::vector<LayerInfo> result;
|
||||
|
||||
result.reserve(mRootLayer->mChildren.size());
|
||||
|
||||
for (auto it : mRootLayer->mChildren) {
|
||||
auto layer = static_cast<model::Layer *>(it);
|
||||
result.emplace_back(layer->name(), layer->mInFrame, layer->mOutFrame);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
1148
vendor/github.com/Benau/go_rlottie/lottie_lottiemodel.h
generated
vendored
Normal file
2390
vendor/github.com/Benau/go_rlottie/lottie_lottieparser.cpp
generated
vendored
Normal file
284
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_allocators.h
generated
vendored
Normal file
@@ -0,0 +1,284 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ALLOCATORS_H_
|
||||
#define RAPIDJSON_ALLOCATORS_H_
|
||||
|
||||
#include "lottie_rapidjson_rapidjson.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Allocator
|
||||
|
||||
/*! \class rapidjson::Allocator
|
||||
\brief Concept for allocating, resizing and freeing memory block.
|
||||
|
||||
Note that Malloc() and Realloc() are non-static but Free() is static.
|
||||
|
||||
So if an allocator need to support Free(), it needs to put its pointer in
|
||||
the header of memory block.
|
||||
|
||||
\code
|
||||
concept Allocator {
|
||||
static const bool kNeedFree; //!< Whether this allocator needs to call Free().
|
||||
|
||||
// Allocate a memory block.
|
||||
// \param size of the memory block in bytes.
|
||||
// \returns pointer to the memory block.
|
||||
void* Malloc(size_t size);
|
||||
|
||||
// Resize a memory block.
|
||||
// \param originalPtr The pointer to current memory block. Null pointer is permitted.
|
||||
// \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.)
|
||||
// \param newSize the new size in bytes.
|
||||
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize);
|
||||
|
||||
// Free a memory block.
|
||||
// \param pointer to the memory block. Null pointer is permitted.
|
||||
static void Free(void *ptr);
|
||||
};
|
||||
\endcode
|
||||
*/
|
||||
|
||||
|
||||
/*! \def RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief User-defined kDefaultChunkCapacity definition.
|
||||
|
||||
User can define this as any \c size that is a power of 2.
|
||||
*/
|
||||
|
||||
#ifndef RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY
|
||||
#define RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY (64 * 1024)
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// CrtAllocator
|
||||
|
||||
//! C-runtime library allocator.
|
||||
/*! This class is just wrapper for standard C library memory routines.
|
||||
\note implements Allocator concept
|
||||
*/
|
||||
class CrtAllocator {
|
||||
public:
|
||||
static const bool kNeedFree = true;
|
||||
void* Malloc(size_t size) {
|
||||
if (size) // behavior of malloc(0) is implementation defined.
|
||||
return RAPIDJSON_MALLOC(size);
|
||||
else
|
||||
return NULL; // standardize to returning NULL.
|
||||
}
|
||||
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
|
||||
(void)originalSize;
|
||||
if (newSize == 0) {
|
||||
RAPIDJSON_FREE(originalPtr);
|
||||
return NULL;
|
||||
}
|
||||
return RAPIDJSON_REALLOC(originalPtr, newSize);
|
||||
}
|
||||
static void Free(void *ptr) { RAPIDJSON_FREE(ptr); }
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// MemoryPoolAllocator
|
||||
|
||||
//! Default memory allocator used by the parser and DOM.
|
||||
/*! This allocator allocate memory blocks from pre-allocated memory chunks.
|
||||
|
||||
It does not free memory blocks. And Realloc() only allocate new memory.
|
||||
|
||||
The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default.
|
||||
|
||||
User may also supply a buffer as the first chunk.
|
||||
|
||||
If the user-buffer is full then additional chunks are allocated by BaseAllocator.
|
||||
|
||||
The user-buffer is not deallocated by this allocator.
|
||||
|
||||
\tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator.
|
||||
\note implements Allocator concept
|
||||
*/
|
||||
template <typename BaseAllocator = CrtAllocator>
|
||||
class MemoryPoolAllocator {
|
||||
public:
|
||||
static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator)
|
||||
|
||||
//! Constructor with chunkSize.
|
||||
/*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
|
||||
\param baseAllocator The allocator for allocating memory chunks.
|
||||
*/
|
||||
MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
|
||||
chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
|
||||
{
|
||||
}
|
||||
|
||||
//! Constructor with user-supplied buffer.
|
||||
/*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size.
|
||||
|
||||
The user buffer will not be deallocated when this allocator is destructed.
|
||||
|
||||
\param buffer User supplied buffer.
|
||||
\param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader).
|
||||
\param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
|
||||
\param baseAllocator The allocator for allocating memory chunks.
|
||||
*/
|
||||
MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
|
||||
chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
|
||||
{
|
||||
RAPIDJSON_ASSERT(buffer != 0);
|
||||
RAPIDJSON_ASSERT(size > sizeof(ChunkHeader));
|
||||
chunkHead_ = reinterpret_cast<ChunkHeader*>(buffer);
|
||||
chunkHead_->capacity = size - sizeof(ChunkHeader);
|
||||
chunkHead_->size = 0;
|
||||
chunkHead_->next = 0;
|
||||
}
|
||||
|
||||
//! Destructor.
|
||||
/*! This deallocates all memory chunks, excluding the user-supplied buffer.
|
||||
*/
|
||||
~MemoryPoolAllocator() {
|
||||
Clear();
|
||||
RAPIDJSON_DELETE(ownBaseAllocator_);
|
||||
}
|
||||
|
||||
//! Deallocates all memory chunks, excluding the user-supplied buffer.
|
||||
void Clear() {
|
||||
while (chunkHead_ && chunkHead_ != userBuffer_) {
|
||||
ChunkHeader* next = chunkHead_->next;
|
||||
baseAllocator_->Free(chunkHead_);
|
||||
chunkHead_ = next;
|
||||
}
|
||||
if (chunkHead_ && chunkHead_ == userBuffer_)
|
||||
chunkHead_->size = 0; // Clear user buffer
|
||||
}
|
||||
|
||||
//! Computes the total capacity of allocated memory chunks.
|
||||
/*! \return total capacity in bytes.
|
||||
*/
|
||||
size_t Capacity() const {
|
||||
size_t capacity = 0;
|
||||
for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
|
||||
capacity += c->capacity;
|
||||
return capacity;
|
||||
}
|
||||
|
||||
//! Computes the memory blocks allocated.
|
||||
/*! \return total used bytes.
|
||||
*/
|
||||
size_t Size() const {
|
||||
size_t size = 0;
|
||||
for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
|
||||
size += c->size;
|
||||
return size;
|
||||
}
|
||||
|
||||
//! Allocates a memory block. (concept Allocator)
|
||||
void* Malloc(size_t size) {
|
||||
if (!size)
|
||||
return NULL;
|
||||
|
||||
size = RAPIDJSON_ALIGN(size);
|
||||
if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity)
|
||||
if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size))
|
||||
return NULL;
|
||||
|
||||
void *buffer = reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size;
|
||||
chunkHead_->size += size;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
//! Resizes a memory block (concept Allocator)
|
||||
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
|
||||
if (originalPtr == 0)
|
||||
return Malloc(newSize);
|
||||
|
||||
if (newSize == 0)
|
||||
return NULL;
|
||||
|
||||
originalSize = RAPIDJSON_ALIGN(originalSize);
|
||||
newSize = RAPIDJSON_ALIGN(newSize);
|
||||
|
||||
// Do not shrink if new size is smaller than original
|
||||
if (originalSize >= newSize)
|
||||
return originalPtr;
|
||||
|
||||
// Simply expand it if it is the last allocation and there is sufficient space
|
||||
if (originalPtr == reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) {
|
||||
size_t increment = static_cast<size_t>(newSize - originalSize);
|
||||
if (chunkHead_->size + increment <= chunkHead_->capacity) {
|
||||
chunkHead_->size += increment;
|
||||
return originalPtr;
|
||||
}
|
||||
}
|
||||
|
||||
// Realloc process: allocate and copy memory, do not free original buffer.
|
||||
if (void* newBuffer = Malloc(newSize)) {
|
||||
if (originalSize)
|
||||
std::memcpy(newBuffer, originalPtr, originalSize);
|
||||
return newBuffer;
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//! Frees a memory block (concept Allocator)
|
||||
static void Free(void *ptr) { (void)ptr; } // Do nothing
|
||||
|
||||
private:
|
||||
//! Copy constructor is not permitted.
|
||||
MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */;
|
||||
//! Copy assignment operator is not permitted.
|
||||
MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */;
|
||||
|
||||
//! Creates a new chunk.
|
||||
/*! \param capacity Capacity of the chunk in bytes.
|
||||
\return true if success.
|
||||
*/
|
||||
bool AddChunk(size_t capacity) {
|
||||
if (!baseAllocator_)
|
||||
ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)();
|
||||
if (ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) {
|
||||
chunk->capacity = capacity;
|
||||
chunk->size = 0;
|
||||
chunk->next = chunkHead_;
|
||||
chunkHead_ = chunk;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static const int kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< Default chunk capacity.
|
||||
|
||||
//! Chunk header for perpending to each chunk.
|
||||
/*! Chunks are stored as a singly linked list.
|
||||
*/
|
||||
struct ChunkHeader {
|
||||
size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself).
|
||||
size_t size; //!< Current size of allocated memory in bytes.
|
||||
ChunkHeader *next; //!< Next chunk in the linked list.
|
||||
};
|
||||
|
||||
ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation.
|
||||
size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated.
|
||||
void *userBuffer_; //!< User supplied buffer.
|
||||
BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks.
|
||||
BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object.
|
||||
};
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_ENCODINGS_H_
|
||||
78
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_cursorstreamwrapper.h
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_CURSORSTREAMWRAPPER_H_
|
||||
#define RAPIDJSON_CURSORSTREAMWRAPPER_H_
|
||||
|
||||
#include "lottie_rapidjson_stream.h"
|
||||
|
||||
#if defined(__GNUC__)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1800
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(4702) // unreachable code
|
||||
RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
|
||||
//! Cursor stream wrapper for counting line and column number if error exists.
|
||||
/*!
|
||||
\tparam InputStream Any stream that implements Stream Concept
|
||||
*/
|
||||
template <typename InputStream, typename Encoding = UTF8<> >
|
||||
class CursorStreamWrapper : public GenericStreamWrapper<InputStream, Encoding> {
|
||||
public:
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
CursorStreamWrapper(InputStream& is):
|
||||
GenericStreamWrapper<InputStream, Encoding>(is), line_(1), col_(0) {}
|
||||
|
||||
// counting line and column number
|
||||
Ch Take() {
|
||||
Ch ch = this->is_.Take();
|
||||
if(ch == '\n') {
|
||||
line_ ++;
|
||||
col_ = 0;
|
||||
} else {
|
||||
col_ ++;
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
|
||||
//! Get the error line number, if error exists.
|
||||
size_t GetLine() const { return line_; }
|
||||
//! Get the error column number, if error exists.
|
||||
size_t GetColumn() const { return col_; }
|
||||
|
||||
private:
|
||||
size_t line_; //!< Current Line
|
||||
size_t col_; //!< Current Column
|
||||
};
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1800
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_CURSORSTREAMWRAPPER_H_
|
||||
2732
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_document.h
generated
vendored
Normal file
299
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_encodedstream.h
generated
vendored
Normal file
@@ -0,0 +1,299 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ENCODEDSTREAM_H_
|
||||
#define RAPIDJSON_ENCODEDSTREAM_H_
|
||||
|
||||
#include "lottie_rapidjson_stream.h"
|
||||
#include "lottie_rapidjson_memorystream.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(padded)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Input byte stream wrapper with a statically bound encoding.
|
||||
/*!
|
||||
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
|
||||
\tparam InputByteStream Type of input byte stream. For example, FileReadStream.
|
||||
*/
|
||||
template <typename Encoding, typename InputByteStream>
|
||||
class EncodedInputStream {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
public:
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
EncodedInputStream(InputByteStream& is) : is_(is) {
|
||||
current_ = Encoding::TakeBOM(is_);
|
||||
}
|
||||
|
||||
Ch Peek() const { return current_; }
|
||||
Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; }
|
||||
size_t Tell() const { return is_.Tell(); }
|
||||
|
||||
// Not implemented
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
EncodedInputStream(const EncodedInputStream&);
|
||||
EncodedInputStream& operator=(const EncodedInputStream&);
|
||||
|
||||
InputByteStream& is_;
|
||||
Ch current_;
|
||||
};
|
||||
|
||||
//! Specialized for UTF8 MemoryStream.
|
||||
template <>
|
||||
class EncodedInputStream<UTF8<>, MemoryStream> {
|
||||
public:
|
||||
typedef UTF8<>::Ch Ch;
|
||||
|
||||
EncodedInputStream(MemoryStream& is) : is_(is) {
|
||||
if (static_cast<unsigned char>(is_.Peek()) == 0xEFu) is_.Take();
|
||||
if (static_cast<unsigned char>(is_.Peek()) == 0xBBu) is_.Take();
|
||||
if (static_cast<unsigned char>(is_.Peek()) == 0xBFu) is_.Take();
|
||||
}
|
||||
Ch Peek() const { return is_.Peek(); }
|
||||
Ch Take() { return is_.Take(); }
|
||||
size_t Tell() const { return is_.Tell(); }
|
||||
|
||||
// Not implemented
|
||||
void Put(Ch) {}
|
||||
void Flush() {}
|
||||
Ch* PutBegin() { return 0; }
|
||||
size_t PutEnd(Ch*) { return 0; }
|
||||
|
||||
MemoryStream& is_;
|
||||
|
||||
private:
|
||||
EncodedInputStream(const EncodedInputStream&);
|
||||
EncodedInputStream& operator=(const EncodedInputStream&);
|
||||
};
|
||||
|
||||
//! Output byte stream wrapper with statically bound encoding.
|
||||
/*!
|
||||
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
|
||||
\tparam OutputByteStream Type of input byte stream. For example, FileWriteStream.
|
||||
*/
|
||||
template <typename Encoding, typename OutputByteStream>
|
||||
class EncodedOutputStream {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
public:
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) {
|
||||
if (putBOM)
|
||||
Encoding::PutBOM(os_);
|
||||
}
|
||||
|
||||
void Put(Ch c) { Encoding::Put(os_, c); }
|
||||
void Flush() { os_.Flush(); }
|
||||
|
||||
// Not implemented
|
||||
Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;}
|
||||
Ch Take() { RAPIDJSON_ASSERT(false); return 0;}
|
||||
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
EncodedOutputStream(const EncodedOutputStream&);
|
||||
EncodedOutputStream& operator=(const EncodedOutputStream&);
|
||||
|
||||
OutputByteStream& os_;
|
||||
};
|
||||
|
||||
#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
|
||||
|
||||
//! Input stream wrapper with dynamically bound encoding and automatic encoding detection.
|
||||
/*!
|
||||
\tparam CharType Type of character for reading.
|
||||
\tparam InputByteStream type of input byte stream to be wrapped.
|
||||
*/
|
||||
template <typename CharType, typename InputByteStream>
|
||||
class AutoUTFInputStream {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
public:
|
||||
typedef CharType Ch;
|
||||
|
||||
//! Constructor.
|
||||
/*!
|
||||
\param is input stream to be wrapped.
|
||||
\param type UTF encoding type if it is not detected from the stream.
|
||||
*/
|
||||
AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) {
|
||||
RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
|
||||
DetectType();
|
||||
static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) };
|
||||
takeFunc_ = f[type_];
|
||||
current_ = takeFunc_(*is_);
|
||||
}
|
||||
|
||||
UTFType GetType() const { return type_; }
|
||||
bool HasBOM() const { return hasBOM_; }
|
||||
|
||||
Ch Peek() const { return current_; }
|
||||
Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; }
|
||||
size_t Tell() const { return is_->Tell(); }
|
||||
|
||||
// Not implemented
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
AutoUTFInputStream(const AutoUTFInputStream&);
|
||||
AutoUTFInputStream& operator=(const AutoUTFInputStream&);
|
||||
|
||||
// Detect encoding type with BOM or RFC 4627
|
||||
void DetectType() {
|
||||
// BOM (Byte Order Mark):
|
||||
// 00 00 FE FF UTF-32BE
|
||||
// FF FE 00 00 UTF-32LE
|
||||
// FE FF UTF-16BE
|
||||
// FF FE UTF-16LE
|
||||
// EF BB BF UTF-8
|
||||
|
||||
const unsigned char* c = reinterpret_cast<const unsigned char *>(is_->Peek4());
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
unsigned bom = static_cast<unsigned>(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24));
|
||||
hasBOM_ = false;
|
||||
if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
|
||||
else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
|
||||
else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); }
|
||||
else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); }
|
||||
else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); }
|
||||
|
||||
// RFC 4627: Section 3
|
||||
// "Since the first two characters of a JSON text will always be ASCII
|
||||
// characters [RFC0020], it is possible to determine whether an octet
|
||||
// stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
|
||||
// at the pattern of nulls in the first four octets."
|
||||
// 00 00 00 xx UTF-32BE
|
||||
// 00 xx 00 xx UTF-16BE
|
||||
// xx 00 00 00 UTF-32LE
|
||||
// xx 00 xx 00 UTF-16LE
|
||||
// xx xx xx xx UTF-8
|
||||
|
||||
if (!hasBOM_) {
|
||||
int pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0);
|
||||
switch (pattern) {
|
||||
case 0x08: type_ = kUTF32BE; break;
|
||||
case 0x0A: type_ = kUTF16BE; break;
|
||||
case 0x01: type_ = kUTF32LE; break;
|
||||
case 0x05: type_ = kUTF16LE; break;
|
||||
case 0x0F: type_ = kUTF8; break;
|
||||
default: break; // Use type defined by user.
|
||||
}
|
||||
}
|
||||
|
||||
// Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
|
||||
if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
|
||||
if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
|
||||
}
|
||||
|
||||
typedef Ch (*TakeFunc)(InputByteStream& is);
|
||||
InputByteStream* is_;
|
||||
UTFType type_;
|
||||
Ch current_;
|
||||
TakeFunc takeFunc_;
|
||||
bool hasBOM_;
|
||||
};
|
||||
|
||||
//! Output stream wrapper with dynamically bound encoding and automatic encoding detection.
|
||||
/*!
|
||||
\tparam CharType Type of character for writing.
|
||||
\tparam OutputByteStream type of output byte stream to be wrapped.
|
||||
*/
|
||||
template <typename CharType, typename OutputByteStream>
|
||||
class AutoUTFOutputStream {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
public:
|
||||
typedef CharType Ch;
|
||||
|
||||
//! Constructor.
|
||||
/*!
|
||||
\param os output stream to be wrapped.
|
||||
\param type UTF encoding type.
|
||||
\param putBOM Whether to write BOM at the beginning of the stream.
|
||||
*/
|
||||
AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) {
|
||||
RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
|
||||
|
||||
// Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
|
||||
if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
|
||||
if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
|
||||
|
||||
static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) };
|
||||
putFunc_ = f[type_];
|
||||
|
||||
if (putBOM)
|
||||
PutBOM();
|
||||
}
|
||||
|
||||
UTFType GetType() const { return type_; }
|
||||
|
||||
void Put(Ch c) { putFunc_(*os_, c); }
|
||||
void Flush() { os_->Flush(); }
|
||||
|
||||
// Not implemented
|
||||
Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;}
|
||||
Ch Take() { RAPIDJSON_ASSERT(false); return 0;}
|
||||
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
AutoUTFOutputStream(const AutoUTFOutputStream&);
|
||||
AutoUTFOutputStream& operator=(const AutoUTFOutputStream&);
|
||||
|
||||
void PutBOM() {
|
||||
typedef void (*PutBOMFunc)(OutputByteStream&);
|
||||
static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) };
|
||||
f[type_](*os_);
|
||||
}
|
||||
|
||||
typedef void (*PutFunc)(OutputByteStream&, Ch);
|
||||
|
||||
OutputByteStream* os_;
|
||||
UTFType type_;
|
||||
PutFunc putFunc_;
|
||||
};
|
||||
|
||||
#undef RAPIDJSON_ENCODINGS_FUNC
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_FILESTREAM_H_
|
||||
716
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_encodings.h
generated
vendored
Normal file
@@ -0,0 +1,716 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ENCODINGS_H_
|
||||
#define RAPIDJSON_ENCODINGS_H_
|
||||
|
||||
#include "lottie_rapidjson_rapidjson.h"
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data
|
||||
RAPIDJSON_DIAG_OFF(4702) // unreachable code
|
||||
#elif defined(__GNUC__)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
RAPIDJSON_DIAG_OFF(overflow)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Encoding
|
||||
|
||||
/*! \class rapidjson::Encoding
|
||||
\brief Concept for encoding of Unicode characters.
|
||||
|
||||
\code
|
||||
concept Encoding {
|
||||
typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition.
|
||||
|
||||
enum { supportUnicode = 1 }; // or 0 if not supporting unicode
|
||||
|
||||
//! \brief Encode a Unicode codepoint to an output stream.
|
||||
//! \param os Output stream.
|
||||
//! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively.
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint);
|
||||
|
||||
//! \brief Decode a Unicode codepoint from an input stream.
|
||||
//! \param is Input stream.
|
||||
//! \param codepoint Output of the unicode codepoint.
|
||||
//! \return true if a valid codepoint can be decoded from the stream.
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint);
|
||||
|
||||
//! \brief Validate one Unicode codepoint from an encoded stream.
|
||||
//! \param is Input stream to obtain codepoint.
|
||||
//! \param os Output for copying one codepoint.
|
||||
//! \return true if it is valid.
|
||||
//! \note This function just validating and copying the codepoint without actually decode it.
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os);
|
||||
|
||||
// The following functions are deal with byte streams.
|
||||
|
||||
//! Take a character from input byte stream, skip BOM if exist.
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is);
|
||||
|
||||
//! Take a character from input byte stream.
|
||||
template <typename InputByteStream>
|
||||
static Ch Take(InputByteStream& is);
|
||||
|
||||
//! Put BOM to output byte stream.
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os);
|
||||
|
||||
//! Put a character to output byte stream.
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, Ch c);
|
||||
};
|
||||
\endcode
|
||||
*/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// UTF8
|
||||
|
||||
//! UTF-8 encoding.
|
||||
/*! http://en.wikipedia.org/wiki/UTF-8
|
||||
http://tools.ietf.org/html/rfc3629
|
||||
\tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char.
|
||||
\note implements Encoding concept
|
||||
*/
|
||||
template<typename CharType = char>
|
||||
struct UTF8 {
|
||||
typedef CharType Ch;
|
||||
|
||||
enum { supportUnicode = 1 };
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
if (codepoint <= 0x7F)
|
||||
os.Put(static_cast<Ch>(codepoint & 0xFF));
|
||||
else if (codepoint <= 0x7FF) {
|
||||
os.Put(static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
|
||||
os.Put(static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
|
||||
}
|
||||
else if (codepoint <= 0xFFFF) {
|
||||
os.Put(static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
|
||||
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||
os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
os.Put(static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
|
||||
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
|
||||
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||
os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename OutputStream>
|
||||
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
|
||||
if (codepoint <= 0x7F)
|
||||
PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF));
|
||||
else if (codepoint <= 0x7FF) {
|
||||
PutUnsafe(os, static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
|
||||
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
|
||||
}
|
||||
else if (codepoint <= 0xFFFF) {
|
||||
PutUnsafe(os, static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
|
||||
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||
PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
PutUnsafe(os, static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
|
||||
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
|
||||
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||
PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
#define RAPIDJSON_COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast<unsigned char>(c) & 0x3Fu)
|
||||
#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
|
||||
#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70)
|
||||
typename InputStream::Ch c = is.Take();
|
||||
if (!(c & 0x80)) {
|
||||
*codepoint = static_cast<unsigned char>(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char type = GetRange(static_cast<unsigned char>(c));
|
||||
if (type >= 32) {
|
||||
*codepoint = 0;
|
||||
} else {
|
||||
*codepoint = (0xFFu >> type) & static_cast<unsigned char>(c);
|
||||
}
|
||||
bool result = true;
|
||||
switch (type) {
|
||||
case 2: RAPIDJSON_TAIL(); return result;
|
||||
case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
||||
case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result;
|
||||
case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
||||
case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
||||
case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result;
|
||||
case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
||||
default: return false;
|
||||
}
|
||||
#undef RAPIDJSON_COPY
|
||||
#undef RAPIDJSON_TRANS
|
||||
#undef RAPIDJSON_TAIL
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os) {
|
||||
#define RAPIDJSON_COPY() os.Put(c = is.Take())
|
||||
#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
|
||||
#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70)
|
||||
Ch c;
|
||||
RAPIDJSON_COPY();
|
||||
if (!(c & 0x80))
|
||||
return true;
|
||||
|
||||
bool result = true;
|
||||
switch (GetRange(static_cast<unsigned char>(c))) {
|
||||
case 2: RAPIDJSON_TAIL(); return result;
|
||||
case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
||||
case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result;
|
||||
case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
||||
case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
||||
case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result;
|
||||
case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
||||
default: return false;
|
||||
}
|
||||
#undef RAPIDJSON_COPY
|
||||
#undef RAPIDJSON_TRANS
|
||||
#undef RAPIDJSON_TAIL
|
||||
}
|
||||
|
||||
static unsigned char GetRange(unsigned char c) {
|
||||
// Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
|
||||
// With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types.
|
||||
static const unsigned char type[] = {
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
|
||||
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
|
||||
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
|
||||
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
|
||||
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
||||
10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
|
||||
};
|
||||
return type[c];
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
typename InputByteStream::Ch c = Take(is);
|
||||
if (static_cast<unsigned char>(c) != 0xEFu) return c;
|
||||
c = is.Take();
|
||||
if (static_cast<unsigned char>(c) != 0xBBu) return c;
|
||||
c = is.Take();
|
||||
if (static_cast<unsigned char>(c) != 0xBFu) return c;
|
||||
c = is.Take();
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static Ch Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
return static_cast<Ch>(is.Take());
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xEFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xBBu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xBFu));
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, Ch c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(c));
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// UTF16
|
||||
|
||||
//! UTF-16 encoding.
|
||||
/*! http://en.wikipedia.org/wiki/UTF-16
|
||||
http://tools.ietf.org/html/rfc2781
|
||||
\tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead.
|
||||
\note implements Encoding concept
|
||||
|
||||
\note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
|
||||
For streaming, use UTF16LE and UTF16BE, which handle endianness.
|
||||
*/
|
||||
template<typename CharType = wchar_t>
|
||||
struct UTF16 {
|
||||
typedef CharType Ch;
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2);
|
||||
|
||||
enum { supportUnicode = 1 };
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
|
||||
if (codepoint <= 0xFFFF) {
|
||||
RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair
|
||||
os.Put(static_cast<typename OutputStream::Ch>(codepoint));
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
unsigned v = codepoint - 0x10000;
|
||||
os.Put(static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
|
||||
os.Put(static_cast<typename OutputStream::Ch>((v & 0x3FF) | 0xDC00));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename OutputStream>
|
||||
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
|
||||
if (codepoint <= 0xFFFF) {
|
||||
RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair
|
||||
PutUnsafe(os, static_cast<typename OutputStream::Ch>(codepoint));
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
unsigned v = codepoint - 0x10000;
|
||||
PutUnsafe(os, static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
|
||||
PutUnsafe(os, static_cast<typename OutputStream::Ch>((v & 0x3FF) | 0xDC00));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
|
||||
typename InputStream::Ch c = is.Take();
|
||||
if (c < 0xD800 || c > 0xDFFF) {
|
||||
*codepoint = static_cast<unsigned>(c);
|
||||
return true;
|
||||
}
|
||||
else if (c <= 0xDBFF) {
|
||||
*codepoint = (static_cast<unsigned>(c) & 0x3FF) << 10;
|
||||
c = is.Take();
|
||||
*codepoint |= (static_cast<unsigned>(c) & 0x3FF);
|
||||
*codepoint += 0x10000;
|
||||
return c >= 0xDC00 && c <= 0xDFFF;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
|
||||
typename InputStream::Ch c;
|
||||
os.Put(static_cast<typename OutputStream::Ch>(c = is.Take()));
|
||||
if (c < 0xD800 || c > 0xDFFF)
|
||||
return true;
|
||||
else if (c <= 0xDBFF) {
|
||||
os.Put(c = is.Take());
|
||||
return c >= 0xDC00 && c <= 0xDFFF;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-16 little endian encoding.
|
||||
template<typename CharType = wchar_t>
|
||||
struct UTF16LE : UTF16<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
unsigned c = static_cast<uint8_t>(is.Take());
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
|
||||
return static_cast<CharType>(c);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu));
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-16 big endian encoding.
|
||||
template<typename CharType = wchar_t>
|
||||
struct UTF16BE : UTF16<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take()));
|
||||
return static_cast<CharType>(c);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu));
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// UTF32
|
||||
|
||||
//! UTF-32 encoding.
|
||||
/*! http://en.wikipedia.org/wiki/UTF-32
|
||||
\tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead.
|
||||
\note implements Encoding concept
|
||||
|
||||
\note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
|
||||
For streaming, use UTF32LE and UTF32BE, which handle endianness.
|
||||
*/
|
||||
template<typename CharType = unsigned>
|
||||
struct UTF32 {
|
||||
typedef CharType Ch;
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4);
|
||||
|
||||
enum { supportUnicode = 1 };
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
os.Put(codepoint);
|
||||
}
|
||||
|
||||
template<typename OutputStream>
|
||||
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
PutUnsafe(os, codepoint);
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
|
||||
Ch c = is.Take();
|
||||
*codepoint = c;
|
||||
return c <= 0x10FFFF;
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
|
||||
Ch c;
|
||||
os.Put(c = is.Take());
|
||||
return c <= 0x10FFFF;
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-32 little endian enocoding.
|
||||
template<typename CharType = unsigned>
|
||||
struct UTF32LE : UTF32<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
unsigned c = static_cast<uint8_t>(is.Take());
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16;
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24;
|
||||
return static_cast<CharType>(c);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu));
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-32 big endian encoding.
|
||||
template<typename CharType = unsigned>
|
||||
struct UTF32BE : UTF32<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24;
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16;
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take()));
|
||||
return static_cast<CharType>(c);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu));
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ASCII
|
||||
|
||||
//! ASCII encoding.
|
||||
/*! http://en.wikipedia.org/wiki/ASCII
|
||||
\tparam CharType Code unit for storing 7-bit ASCII data. Default is char.
|
||||
\note implements Encoding concept
|
||||
*/
|
||||
template<typename CharType = char>
|
||||
struct ASCII {
|
||||
typedef CharType Ch;
|
||||
|
||||
enum { supportUnicode = 0 };
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x7F);
|
||||
os.Put(static_cast<Ch>(codepoint & 0xFF));
|
||||
}
|
||||
|
||||
template<typename OutputStream>
|
||||
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x7F);
|
||||
PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF));
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
uint8_t c = static_cast<uint8_t>(is.Take());
|
||||
*codepoint = c;
|
||||
return c <= 0X7F;
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os) {
|
||||
uint8_t c = static_cast<uint8_t>(is.Take());
|
||||
os.Put(static_cast<typename OutputStream::Ch>(c));
|
||||
return c <= 0x7F;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
uint8_t c = static_cast<uint8_t>(Take(is));
|
||||
return static_cast<Ch>(c);
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static Ch Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
return static_cast<Ch>(is.Take());
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
(void)os;
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, Ch c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(c));
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// AutoUTF
|
||||
|
||||
//! Runtime-specified UTF encoding type of a stream.
|
||||
enum UTFType {
|
||||
kUTF8 = 0, //!< UTF-8.
|
||||
kUTF16LE = 1, //!< UTF-16 little endian.
|
||||
kUTF16BE = 2, //!< UTF-16 big endian.
|
||||
kUTF32LE = 3, //!< UTF-32 little endian.
|
||||
kUTF32BE = 4 //!< UTF-32 big endian.
|
||||
};
|
||||
|
||||
//! Dynamically select encoding according to stream's runtime-specified UTF encoding type.
|
||||
/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType().
|
||||
*/
|
||||
template<typename CharType>
|
||||
struct AutoUTF {
|
||||
typedef CharType Ch;
|
||||
|
||||
enum { supportUnicode = 1 };
|
||||
|
||||
#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
|
||||
|
||||
template<typename OutputStream>
|
||||
static RAPIDJSON_FORCEINLINE void Encode(OutputStream& os, unsigned codepoint) {
|
||||
typedef void (*EncodeFunc)(OutputStream&, unsigned);
|
||||
static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) };
|
||||
(*f[os.GetType()])(os, codepoint);
|
||||
}
|
||||
|
||||
template<typename OutputStream>
|
||||
static RAPIDJSON_FORCEINLINE void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
|
||||
typedef void (*EncodeFunc)(OutputStream&, unsigned);
|
||||
static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) };
|
||||
(*f[os.GetType()])(os, codepoint);
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static RAPIDJSON_FORCEINLINE bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
typedef bool (*DecodeFunc)(InputStream&, unsigned*);
|
||||
static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) };
|
||||
return (*f[is.GetType()])(is, codepoint);
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) {
|
||||
typedef bool (*ValidateFunc)(InputStream&, OutputStream&);
|
||||
static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) };
|
||||
return (*f[is.GetType()])(is, os);
|
||||
}
|
||||
|
||||
#undef RAPIDJSON_ENCODINGS_FUNC
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Transcoder
|
||||
|
||||
//! Encoding conversion.
|
||||
template<typename SourceEncoding, typename TargetEncoding>
|
||||
struct Transcoder {
|
||||
//! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream.
|
||||
template<typename InputStream, typename OutputStream>
|
||||
static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) {
|
||||
unsigned codepoint;
|
||||
if (!SourceEncoding::Decode(is, &codepoint))
|
||||
return false;
|
||||
TargetEncoding::Encode(os, codepoint);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename InputStream, typename OutputStream>
|
||||
static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
|
||||
unsigned codepoint;
|
||||
if (!SourceEncoding::Decode(is, &codepoint))
|
||||
return false;
|
||||
TargetEncoding::EncodeUnsafe(os, codepoint);
|
||||
return true;
|
||||
}
|
||||
|
||||
//! Validate one Unicode codepoint from an encoded stream.
|
||||
template<typename InputStream, typename OutputStream>
|
||||
static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) {
|
||||
return Transcode(is, os); // Since source/target encoding is different, must transcode.
|
||||
}
|
||||
};
|
||||
|
||||
// Forward declaration.
|
||||
template<typename Stream>
|
||||
inline void PutUnsafe(Stream& stream, typename Stream::Ch c);
|
||||
|
||||
//! Specialization of Transcoder with same source and target encoding.
|
||||
template<typename Encoding>
|
||||
struct Transcoder<Encoding, Encoding> {
|
||||
template<typename InputStream, typename OutputStream>
|
||||
static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) {
|
||||
os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class.
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename InputStream, typename OutputStream>
|
||||
static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
|
||||
PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class.
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename InputStream, typename OutputStream>
|
||||
static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) {
|
||||
return Encoding::Validate(is, os); // source/target encoding are the same
|
||||
}
|
||||
};
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#if defined(__GNUC__) || (defined(_MSC_VER) && !defined(__clang__))
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_ENCODINGS_H_
|
||||
74
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_error_en.h
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ERROR_EN_H_
|
||||
#define RAPIDJSON_ERROR_EN_H_
|
||||
|
||||
#include "lottie_rapidjson_error_error.h"
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(switch-enum)
|
||||
RAPIDJSON_DIAG_OFF(covered-switch-default)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Maps error code of parsing into error message.
|
||||
/*!
|
||||
\ingroup RAPIDJSON_ERRORS
|
||||
\param parseErrorCode Error code obtained in parsing.
|
||||
\return the error message.
|
||||
\note User can make a copy of this function for localization.
|
||||
Using switch-case is safer for future modification of error codes.
|
||||
*/
|
||||
inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) {
|
||||
switch (parseErrorCode) {
|
||||
case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error.");
|
||||
|
||||
case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty.");
|
||||
case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values.");
|
||||
|
||||
case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value.");
|
||||
|
||||
case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member.");
|
||||
case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member.");
|
||||
case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member.");
|
||||
|
||||
case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element.");
|
||||
|
||||
case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string.");
|
||||
case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid.");
|
||||
case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string.");
|
||||
case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string.");
|
||||
case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string.");
|
||||
|
||||
case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double.");
|
||||
case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number.");
|
||||
case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number.");
|
||||
|
||||
case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error.");
|
||||
case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error.");
|
||||
|
||||
default: return RAPIDJSON_ERROR_STRING("Unknown error.");
|
||||
}
|
||||
}
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_ERROR_EN_H_
|
||||
161
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_error_error.h
generated
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ERROR_ERROR_H_
|
||||
#define RAPIDJSON_ERROR_ERROR_H_
|
||||
|
||||
#include "lottie_rapidjson_rapidjson.h"
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(padded)
|
||||
#endif
|
||||
|
||||
/*! \file error.h */
|
||||
|
||||
/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_ERROR_CHARTYPE
|
||||
|
||||
//! Character type of error messages.
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
The default character type is \c char.
|
||||
On Windows, user can define this macro as \c TCHAR for supporting both
|
||||
unicode/non-unicode settings.
|
||||
*/
|
||||
#ifndef RAPIDJSON_ERROR_CHARTYPE
|
||||
#define RAPIDJSON_ERROR_CHARTYPE char
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_ERROR_STRING
|
||||
|
||||
//! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[].
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
By default this conversion macro does nothing.
|
||||
On Windows, user can define this macro as \c _T(x) for supporting both
|
||||
unicode/non-unicode settings.
|
||||
*/
|
||||
#ifndef RAPIDJSON_ERROR_STRING
|
||||
#define RAPIDJSON_ERROR_STRING(x) x
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ParseErrorCode
|
||||
|
||||
//! Error code of parsing.
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
\see GenericReader::Parse, GenericReader::GetParseErrorCode
|
||||
*/
|
||||
enum ParseErrorCode {
|
||||
kParseErrorNone = 0, //!< No error.
|
||||
|
||||
kParseErrorDocumentEmpty, //!< The document is empty.
|
||||
kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values.
|
||||
|
||||
kParseErrorValueInvalid, //!< Invalid value.
|
||||
|
||||
kParseErrorObjectMissName, //!< Missing a name for object member.
|
||||
kParseErrorObjectMissColon, //!< Missing a colon after a name of object member.
|
||||
kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member.
|
||||
|
||||
kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element.
|
||||
|
||||
kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string.
|
||||
kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid.
|
||||
kParseErrorStringEscapeInvalid, //!< Invalid escape character in string.
|
||||
kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string.
|
||||
kParseErrorStringInvalidEncoding, //!< Invalid encoding in string.
|
||||
|
||||
kParseErrorNumberTooBig, //!< Number too big to be stored in double.
|
||||
kParseErrorNumberMissFraction, //!< Miss fraction part in number.
|
||||
kParseErrorNumberMissExponent, //!< Miss exponent in number.
|
||||
|
||||
kParseErrorTermination, //!< Parsing was terminated.
|
||||
kParseErrorUnspecificSyntaxError //!< Unspecific syntax error.
|
||||
};
|
||||
|
||||
//! Result of parsing (wraps ParseErrorCode)
|
||||
/*!
|
||||
\ingroup RAPIDJSON_ERRORS
|
||||
\code
|
||||
Document doc;
|
||||
ParseResult ok = doc.Parse("[42]");
|
||||
if (!ok) {
|
||||
fprintf(stderr, "JSON parse error: %s (%u)",
|
||||
GetParseError_En(ok.Code()), ok.Offset());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
\endcode
|
||||
\see GenericReader::Parse, GenericDocument::Parse
|
||||
*/
|
||||
struct ParseResult {
|
||||
//!! Unspecified boolean type
|
||||
typedef bool (ParseResult::*BooleanType)() const;
|
||||
public:
|
||||
//! Default constructor, no error.
|
||||
ParseResult() : code_(kParseErrorNone), offset_(0) {}
|
||||
//! Constructor to set an error.
|
||||
ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {}
|
||||
|
||||
//! Get the error code.
|
||||
ParseErrorCode Code() const { return code_; }
|
||||
//! Get the error offset, if \ref IsError(), 0 otherwise.
|
||||
size_t Offset() const { return offset_; }
|
||||
|
||||
//! Explicit conversion to \c bool, returns \c true, iff !\ref IsError().
|
||||
operator BooleanType() const { return !IsError() ? &ParseResult::IsError : NULL; }
|
||||
//! Whether the result is an error.
|
||||
bool IsError() const { return code_ != kParseErrorNone; }
|
||||
|
||||
bool operator==(const ParseResult& that) const { return code_ == that.code_; }
|
||||
bool operator==(ParseErrorCode code) const { return code_ == code; }
|
||||
friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; }
|
||||
|
||||
bool operator!=(const ParseResult& that) const { return !(*this == that); }
|
||||
bool operator!=(ParseErrorCode code) const { return !(*this == code); }
|
||||
friend bool operator!=(ParseErrorCode code, const ParseResult & err) { return err != code; }
|
||||
|
||||
//! Reset error code.
|
||||
void Clear() { Set(kParseErrorNone); }
|
||||
//! Update error code and offset.
|
||||
void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; }
|
||||
|
||||
private:
|
||||
ParseErrorCode code_;
|
||||
size_t offset_;
|
||||
};
|
||||
|
||||
//! Function pointer type of GetParseError().
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
|
||||
This is the prototype for \c GetParseError_X(), where \c X is a locale.
|
||||
User can dynamically change locale in runtime, e.g.:
|
||||
\code
|
||||
GetParseErrorFunc GetParseError = GetParseError_En; // or whatever
|
||||
const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode());
|
||||
\endcode
|
||||
*/
|
||||
typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode);
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_ERROR_ERROR_H_
|
||||
99
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_filereadstream.h
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_FILEREADSTREAM_H_
|
||||
#define RAPIDJSON_FILEREADSTREAM_H_
|
||||
|
||||
#include "lottie_rapidjson_stream.h"
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(padded)
|
||||
RAPIDJSON_DIAG_OFF(unreachable-code)
|
||||
RAPIDJSON_DIAG_OFF(missing-noreturn)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! File byte stream for input using fread().
|
||||
/*!
|
||||
\note implements Stream concept
|
||||
*/
|
||||
class FileReadStream {
|
||||
public:
|
||||
typedef char Ch; //!< Character type (byte).
|
||||
|
||||
//! Constructor.
|
||||
/*!
|
||||
\param fp File pointer opened for read.
|
||||
\param buffer user-supplied buffer.
|
||||
\param bufferSize size of buffer in bytes. Must >=4 bytes.
|
||||
*/
|
||||
FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) {
|
||||
RAPIDJSON_ASSERT(fp_ != 0);
|
||||
RAPIDJSON_ASSERT(bufferSize >= 4);
|
||||
Read();
|
||||
}
|
||||
|
||||
Ch Peek() const { return *current_; }
|
||||
Ch Take() { Ch c = *current_; Read(); return c; }
|
||||
size_t Tell() const { return count_ + static_cast<size_t>(current_ - buffer_); }
|
||||
|
||||
// Not implemented
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
// For encoding detection only.
|
||||
const Ch* Peek4() const {
|
||||
return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void Read() {
|
||||
if (current_ < bufferLast_)
|
||||
++current_;
|
||||
else if (!eof_) {
|
||||
count_ += readCount_;
|
||||
readCount_ = std::fread(buffer_, 1, bufferSize_, fp_);
|
||||
bufferLast_ = buffer_ + readCount_ - 1;
|
||||
current_ = buffer_;
|
||||
|
||||
if (readCount_ < bufferSize_) {
|
||||
buffer_[readCount_] = '\0';
|
||||
++bufferLast_;
|
||||
eof_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::FILE* fp_;
|
||||
Ch *buffer_;
|
||||
size_t bufferSize_;
|
||||
Ch *bufferLast_;
|
||||
Ch *current_;
|
||||
size_t readCount_;
|
||||
size_t count_; //!< Number of characters read
|
||||
bool eof_;
|
||||
};
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_FILESTREAM_H_
|
||||
104
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_filewritestream.h
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_FILEWRITESTREAM_H_
|
||||
#define RAPIDJSON_FILEWRITESTREAM_H_
|
||||
|
||||
#include "lottie_rapidjson_stream.h"
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(unreachable-code)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Wrapper of C file stream for output using fwrite().
|
||||
/*!
|
||||
\note implements Stream concept
|
||||
*/
|
||||
class FileWriteStream {
|
||||
public:
|
||||
typedef char Ch; //!< Character type. Only support char.
|
||||
|
||||
FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) {
|
||||
RAPIDJSON_ASSERT(fp_ != 0);
|
||||
}
|
||||
|
||||
void Put(char c) {
|
||||
if (current_ >= bufferEnd_)
|
||||
Flush();
|
||||
|
||||
*current_++ = c;
|
||||
}
|
||||
|
||||
void PutN(char c, size_t n) {
|
||||
size_t avail = static_cast<size_t>(bufferEnd_ - current_);
|
||||
while (n > avail) {
|
||||
std::memset(current_, c, avail);
|
||||
current_ += avail;
|
||||
Flush();
|
||||
n -= avail;
|
||||
avail = static_cast<size_t>(bufferEnd_ - current_);
|
||||
}
|
||||
|
||||
if (n > 0) {
|
||||
std::memset(current_, c, n);
|
||||
current_ += n;
|
||||
}
|
||||
}
|
||||
|
||||
void Flush() {
|
||||
if (current_ != buffer_) {
|
||||
size_t result = std::fwrite(buffer_, 1, static_cast<size_t>(current_ - buffer_), fp_);
|
||||
if (result < static_cast<size_t>(current_ - buffer_)) {
|
||||
// failure deliberately ignored at this time
|
||||
// added to avoid warn_unused_result build errors
|
||||
}
|
||||
current_ = buffer_;
|
||||
}
|
||||
}
|
||||
|
||||
// Not implemented
|
||||
char Peek() const { RAPIDJSON_ASSERT(false); return 0; }
|
||||
char Take() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
|
||||
char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
// Prohibit copy constructor & assignment operator.
|
||||
FileWriteStream(const FileWriteStream&);
|
||||
FileWriteStream& operator=(const FileWriteStream&);
|
||||
|
||||
std::FILE* fp_;
|
||||
char *buffer_;
|
||||
char *bufferEnd_;
|
||||
char *current_;
|
||||
};
|
||||
|
||||
//! Implement specialized version of PutN() with memset() for better performance.
|
||||
template<>
|
||||
inline void PutN(FileWriteStream& stream, char c, size_t n) {
|
||||
stream.PutN(c, n);
|
||||
}
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_FILESTREAM_H_
|
||||
151
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_fwd.h
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_FWD_H_
|
||||
#define RAPIDJSON_FWD_H_
|
||||
|
||||
#include "lottie_rapidjson_rapidjson.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
// encodings.h
|
||||
|
||||
template<typename CharType> struct UTF8;
|
||||
template<typename CharType> struct UTF16;
|
||||
template<typename CharType> struct UTF16BE;
|
||||
template<typename CharType> struct UTF16LE;
|
||||
template<typename CharType> struct UTF32;
|
||||
template<typename CharType> struct UTF32BE;
|
||||
template<typename CharType> struct UTF32LE;
|
||||
template<typename CharType> struct ASCII;
|
||||
template<typename CharType> struct AutoUTF;
|
||||
|
||||
template<typename SourceEncoding, typename TargetEncoding>
|
||||
struct Transcoder;
|
||||
|
||||
// allocators.h
|
||||
|
||||
class CrtAllocator;
|
||||
|
||||
template <typename BaseAllocator>
|
||||
class MemoryPoolAllocator;
|
||||
|
||||
// stream.h
|
||||
|
||||
template <typename Encoding>
|
||||
struct GenericStringStream;
|
||||
|
||||
typedef GenericStringStream<UTF8<char> > StringStream;
|
||||
|
||||
template <typename Encoding>
|
||||
struct GenericInsituStringStream;
|
||||
|
||||
typedef GenericInsituStringStream<UTF8<char> > InsituStringStream;
|
||||
|
||||
// stringbuffer.h
|
||||
|
||||
template <typename Encoding, typename Allocator>
|
||||
class GenericStringBuffer;
|
||||
|
||||
typedef GenericStringBuffer<UTF8<char>, CrtAllocator> StringBuffer;
|
||||
|
||||
// filereadstream.h
|
||||
|
||||
class FileReadStream;
|
||||
|
||||
// filewritestream.h
|
||||
|
||||
class FileWriteStream;
|
||||
|
||||
// memorybuffer.h
|
||||
|
||||
template <typename Allocator>
|
||||
struct GenericMemoryBuffer;
|
||||
|
||||
typedef GenericMemoryBuffer<CrtAllocator> MemoryBuffer;
|
||||
|
||||
// memorystream.h
|
||||
|
||||
struct MemoryStream;
|
||||
|
||||
// reader.h
|
||||
|
||||
template<typename Encoding, typename Derived>
|
||||
struct BaseReaderHandler;
|
||||
|
||||
template <typename SourceEncoding, typename TargetEncoding, typename StackAllocator>
|
||||
class GenericReader;
|
||||
|
||||
typedef GenericReader<UTF8<char>, UTF8<char>, CrtAllocator> Reader;
|
||||
|
||||
// writer.h
|
||||
|
||||
template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags>
|
||||
class Writer;
|
||||
|
||||
// prettywriter.h
|
||||
|
||||
template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags>
|
||||
class PrettyWriter;
|
||||
|
||||
// document.h
|
||||
|
||||
template <typename Encoding, typename Allocator>
|
||||
class GenericMember;
|
||||
|
||||
template <bool Const, typename Encoding, typename Allocator>
|
||||
class GenericMemberIterator;
|
||||
|
||||
template<typename CharType>
|
||||
struct GenericStringRef;
|
||||
|
||||
template <typename Encoding, typename Allocator>
|
||||
class GenericValue;
|
||||
|
||||
typedef GenericValue<UTF8<char>, MemoryPoolAllocator<CrtAllocator> > Value;
|
||||
|
||||
template <typename Encoding, typename Allocator, typename StackAllocator>
|
||||
class GenericDocument;
|
||||
|
||||
typedef GenericDocument<UTF8<char>, MemoryPoolAllocator<CrtAllocator>, CrtAllocator> Document;
|
||||
|
||||
// pointer.h
|
||||
|
||||
template <typename ValueType, typename Allocator>
|
||||
class GenericPointer;
|
||||
|
||||
typedef GenericPointer<Value, CrtAllocator> Pointer;
|
||||
|
||||
// schema.h
|
||||
|
||||
template <typename SchemaDocumentType>
|
||||
class IGenericRemoteSchemaDocumentProvider;
|
||||
|
||||
template <typename ValueT, typename Allocator>
|
||||
class GenericSchemaDocument;
|
||||
|
||||
typedef GenericSchemaDocument<Value, CrtAllocator> SchemaDocument;
|
||||
typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
|
||||
|
||||
template <
|
||||
typename SchemaDocumentType,
|
||||
typename OutputHandler,
|
||||
typename StateAllocator>
|
||||
class GenericSchemaValidator;
|
||||
|
||||
typedef GenericSchemaValidator<SchemaDocument, BaseReaderHandler<UTF8<char>, void>, CrtAllocator> SchemaValidator;
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_RAPIDJSONFWD_H_
|
||||
290
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_internal_biginteger.h
generated
vendored
Normal file
@@ -0,0 +1,290 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_BIGINTEGER_H_
|
||||
#define RAPIDJSON_BIGINTEGER_H_
|
||||
|
||||
#include "lottie_rapidjson_rapidjson.h"
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && defined(_M_AMD64)
|
||||
#include <intrin.h> // for _umul128
|
||||
#pragma intrinsic(_umul128)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
class BigInteger {
|
||||
public:
|
||||
typedef uint64_t Type;
|
||||
|
||||
BigInteger(const BigInteger& rhs) : count_(rhs.count_) {
|
||||
std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type));
|
||||
}
|
||||
|
||||
explicit BigInteger(uint64_t u) : count_(1) {
|
||||
digits_[0] = u;
|
||||
}
|
||||
|
||||
BigInteger(const char* decimals, size_t length) : count_(1) {
|
||||
RAPIDJSON_ASSERT(length > 0);
|
||||
digits_[0] = 0;
|
||||
size_t i = 0;
|
||||
const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19
|
||||
while (length >= kMaxDigitPerIteration) {
|
||||
AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration);
|
||||
length -= kMaxDigitPerIteration;
|
||||
i += kMaxDigitPerIteration;
|
||||
}
|
||||
|
||||
if (length > 0)
|
||||
AppendDecimal64(decimals + i, decimals + i + length);
|
||||
}
|
||||
|
||||
BigInteger& operator=(const BigInteger &rhs)
|
||||
{
|
||||
if (this != &rhs) {
|
||||
count_ = rhs.count_;
|
||||
std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator=(uint64_t u) {
|
||||
digits_[0] = u;
|
||||
count_ = 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator+=(uint64_t u) {
|
||||
Type backup = digits_[0];
|
||||
digits_[0] += u;
|
||||
for (size_t i = 0; i < count_ - 1; i++) {
|
||||
if (digits_[i] >= backup)
|
||||
return *this; // no carry
|
||||
backup = digits_[i + 1];
|
||||
digits_[i + 1] += 1;
|
||||
}
|
||||
|
||||
// Last carry
|
||||
if (digits_[count_ - 1] < backup)
|
||||
PushBack(1);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator*=(uint64_t u) {
|
||||
if (u == 0) return *this = 0;
|
||||
if (u == 1) return *this;
|
||||
if (*this == 1) return *this = u;
|
||||
|
||||
uint64_t k = 0;
|
||||
for (size_t i = 0; i < count_; i++) {
|
||||
uint64_t hi;
|
||||
digits_[i] = MulAdd64(digits_[i], u, k, &hi);
|
||||
k = hi;
|
||||
}
|
||||
|
||||
if (k > 0)
|
||||
PushBack(k);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator*=(uint32_t u) {
|
||||
if (u == 0) return *this = 0;
|
||||
if (u == 1) return *this;
|
||||
if (*this == 1) return *this = u;
|
||||
|
||||
uint64_t k = 0;
|
||||
for (size_t i = 0; i < count_; i++) {
|
||||
const uint64_t c = digits_[i] >> 32;
|
||||
const uint64_t d = digits_[i] & 0xFFFFFFFF;
|
||||
const uint64_t uc = u * c;
|
||||
const uint64_t ud = u * d;
|
||||
const uint64_t p0 = ud + k;
|
||||
const uint64_t p1 = uc + (p0 >> 32);
|
||||
digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32);
|
||||
k = p1 >> 32;
|
||||
}
|
||||
|
||||
if (k > 0)
|
||||
PushBack(k);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator<<=(size_t shift) {
|
||||
if (IsZero() || shift == 0) return *this;
|
||||
|
||||
size_t offset = shift / kTypeBit;
|
||||
size_t interShift = shift % kTypeBit;
|
||||
RAPIDJSON_ASSERT(count_ + offset <= kCapacity);
|
||||
|
||||
if (interShift == 0) {
|
||||
std::memmove(digits_ + offset, digits_, count_ * sizeof(Type));
|
||||
count_ += offset;
|
||||
}
|
||||
else {
|
||||
digits_[count_] = 0;
|
||||
for (size_t i = count_; i > 0; i--)
|
||||
digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift));
|
||||
digits_[offset] = digits_[0] << interShift;
|
||||
count_ += offset;
|
||||
if (digits_[count_])
|
||||
count_++;
|
||||
}
|
||||
|
||||
std::memset(digits_, 0, offset * sizeof(Type));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const BigInteger& rhs) const {
|
||||
return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0;
|
||||
}
|
||||
|
||||
bool operator==(const Type rhs) const {
|
||||
return count_ == 1 && digits_[0] == rhs;
|
||||
}
|
||||
|
||||
BigInteger& MultiplyPow5(unsigned exp) {
|
||||
static const uint32_t kPow5[12] = {
|
||||
5,
|
||||
5 * 5,
|
||||
5 * 5 * 5,
|
||||
5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5
|
||||
};
|
||||
if (exp == 0) return *this;
|
||||
for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27
|
||||
for (; exp >= 13; exp -= 13) *this *= static_cast<uint32_t>(1220703125u); // 5^13
|
||||
if (exp > 0) *this *= kPow5[exp - 1];
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Compute absolute difference of this and rhs.
|
||||
// Assume this != rhs
|
||||
bool Difference(const BigInteger& rhs, BigInteger* out) const {
|
||||
int cmp = Compare(rhs);
|
||||
RAPIDJSON_ASSERT(cmp != 0);
|
||||
const BigInteger *a, *b; // Makes a > b
|
||||
bool ret;
|
||||
if (cmp < 0) { a = &rhs; b = this; ret = true; }
|
||||
else { a = this; b = &rhs; ret = false; }
|
||||
|
||||
Type borrow = 0;
|
||||
for (size_t i = 0; i < a->count_; i++) {
|
||||
Type d = a->digits_[i] - borrow;
|
||||
if (i < b->count_)
|
||||
d -= b->digits_[i];
|
||||
borrow = (d > a->digits_[i]) ? 1 : 0;
|
||||
out->digits_[i] = d;
|
||||
if (d != 0)
|
||||
out->count_ = i + 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Compare(const BigInteger& rhs) const {
|
||||
if (count_ != rhs.count_)
|
||||
return count_ < rhs.count_ ? -1 : 1;
|
||||
|
||||
for (size_t i = count_; i-- > 0;)
|
||||
if (digits_[i] != rhs.digits_[i])
|
||||
return digits_[i] < rhs.digits_[i] ? -1 : 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t GetCount() const { return count_; }
|
||||
Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; }
|
||||
bool IsZero() const { return count_ == 1 && digits_[0] == 0; }
|
||||
|
||||
private:
|
||||
void AppendDecimal64(const char* begin, const char* end) {
|
||||
uint64_t u = ParseUint64(begin, end);
|
||||
if (IsZero())
|
||||
*this = u;
|
||||
else {
|
||||
unsigned exp = static_cast<unsigned>(end - begin);
|
||||
(MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u
|
||||
}
|
||||
}
|
||||
|
||||
void PushBack(Type digit) {
|
||||
RAPIDJSON_ASSERT(count_ < kCapacity);
|
||||
digits_[count_++] = digit;
|
||||
}
|
||||
|
||||
static uint64_t ParseUint64(const char* begin, const char* end) {
|
||||
uint64_t r = 0;
|
||||
for (const char* p = begin; p != end; ++p) {
|
||||
RAPIDJSON_ASSERT(*p >= '0' && *p <= '9');
|
||||
r = r * 10u + static_cast<unsigned>(*p - '0');
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// Assume a * b + k < 2^128
|
||||
static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) {
|
||||
#if defined(_MSC_VER) && defined(_M_AMD64)
|
||||
uint64_t low = _umul128(a, b, outHigh) + k;
|
||||
if (low < k)
|
||||
(*outHigh)++;
|
||||
return low;
|
||||
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
|
||||
__extension__ typedef unsigned __int128 uint128;
|
||||
uint128 p = static_cast<uint128>(a) * static_cast<uint128>(b);
|
||||
p += k;
|
||||
*outHigh = static_cast<uint64_t>(p >> 64);
|
||||
return static_cast<uint64_t>(p);
|
||||
#else
|
||||
const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32;
|
||||
uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1;
|
||||
x1 += (x0 >> 32); // can't give carry
|
||||
x1 += x2;
|
||||
if (x1 < x2)
|
||||
x3 += (static_cast<uint64_t>(1) << 32);
|
||||
uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF);
|
||||
uint64_t hi = x3 + (x1 >> 32);
|
||||
|
||||
lo += k;
|
||||
if (lo < k)
|
||||
hi++;
|
||||
*outHigh = hi;
|
||||
return lo;
|
||||
#endif
|
||||
}
|
||||
|
||||
static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000
|
||||
static const size_t kCapacity = kBitCount / sizeof(Type);
|
||||
static const size_t kTypeBit = sizeof(Type) * 8;
|
||||
|
||||
Type digits_[kCapacity];
|
||||
size_t count_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_BIGINTEGER_H_
|
||||
71
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_internal_clzll.h
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_CLZLL_H_
|
||||
#define RAPIDJSON_CLZLL_H_
|
||||
|
||||
#include "lottie_rapidjson_rapidjson.h"
|
||||
|
||||
#if defined(_MSC_VER) && !defined(UNDER_CE)
|
||||
#include <intrin.h>
|
||||
#if defined(_WIN64)
|
||||
#pragma intrinsic(_BitScanReverse64)
|
||||
#else
|
||||
#pragma intrinsic(_BitScanReverse)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
inline uint32_t clzll(uint64_t x) {
|
||||
// Passing 0 to __builtin_clzll is UB in GCC and results in an
|
||||
// infinite loop in the software implementation.
|
||||
RAPIDJSON_ASSERT(x != 0);
|
||||
|
||||
#if defined(_MSC_VER) && !defined(UNDER_CE)
|
||||
unsigned long r = 0;
|
||||
#if defined(_WIN64)
|
||||
_BitScanReverse64(&r, x);
|
||||
#else
|
||||
// Scan the high 32 bits.
|
||||
if (_BitScanReverse(&r, static_cast<uint32_t>(x >> 32)))
|
||||
return 63 - (r + 32);
|
||||
|
||||
// Scan the low 32 bits.
|
||||
_BitScanReverse(&r, static_cast<uint32_t>(x & 0xFFFFFFFF));
|
||||
#endif // _WIN64
|
||||
|
||||
return 63 - r;
|
||||
#elif (defined(__GNUC__) && __GNUC__ >= 4) || RAPIDJSON_HAS_BUILTIN(__builtin_clzll)
|
||||
// __builtin_clzll wrapper
|
||||
return static_cast<uint32_t>(__builtin_clzll(x));
|
||||
#else
|
||||
// naive version
|
||||
uint32_t r = 0;
|
||||
while (!(x & (static_cast<uint64_t>(1) << 63))) {
|
||||
x <<= 1;
|
||||
++r;
|
||||
}
|
||||
|
||||
return r;
|
||||
#endif // _MSC_VER
|
||||
}
|
||||
|
||||
#define RAPIDJSON_CLZLL RAPIDJSON_NAMESPACE::internal::clzll
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_CLZLL_H_
|
||||
257
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_internal_diyfp.h
generated
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
|
||||
// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with
|
||||
// integers." ACM Sigplan Notices 45.6 (2010): 233-243.
|
||||
|
||||
#ifndef RAPIDJSON_DIYFP_H_
|
||||
#define RAPIDJSON_DIYFP_H_
|
||||
|
||||
#include "lottie_rapidjson_rapidjson.h"
|
||||
#include "lottie_rapidjson_internal_clzll.h"
|
||||
#include <limits>
|
||||
|
||||
#if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER)
|
||||
#include <intrin.h>
|
||||
#pragma intrinsic(_umul128)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(padded)
|
||||
#endif
|
||||
|
||||
struct DiyFp {
|
||||
DiyFp() : f(), e() {}
|
||||
|
||||
DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {}
|
||||
|
||||
explicit DiyFp(double d) {
|
||||
union {
|
||||
double d;
|
||||
uint64_t u64;
|
||||
} u = { d };
|
||||
|
||||
int biased_e = static_cast<int>((u.u64 & kDpExponentMask) >> kDpSignificandSize);
|
||||
uint64_t significand = (u.u64 & kDpSignificandMask);
|
||||
if (biased_e != 0) {
|
||||
f = significand + kDpHiddenBit;
|
||||
e = biased_e - kDpExponentBias;
|
||||
}
|
||||
else {
|
||||
f = significand;
|
||||
e = kDpMinExponent + 1;
|
||||
}
|
||||
}
|
||||
|
||||
DiyFp operator-(const DiyFp& rhs) const {
|
||||
return DiyFp(f - rhs.f, e);
|
||||
}
|
||||
|
||||
DiyFp operator*(const DiyFp& rhs) const {
|
||||
#if defined(_MSC_VER) && defined(_M_AMD64)
|
||||
uint64_t h;
|
||||
uint64_t l = _umul128(f, rhs.f, &h);
|
||||
if (l & (uint64_t(1) << 63)) // rounding
|
||||
h++;
|
||||
return DiyFp(h, e + rhs.e + 64);
|
||||
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
|
||||
__extension__ typedef unsigned __int128 uint128;
|
||||
uint128 p = static_cast<uint128>(f) * static_cast<uint128>(rhs.f);
|
||||
uint64_t h = static_cast<uint64_t>(p >> 64);
|
||||
uint64_t l = static_cast<uint64_t>(p);
|
||||
if (l & (uint64_t(1) << 63)) // rounding
|
||||
h++;
|
||||
return DiyFp(h, e + rhs.e + 64);
|
||||
#else
|
||||
const uint64_t M32 = 0xFFFFFFFF;
|
||||
const uint64_t a = f >> 32;
|
||||
const uint64_t b = f & M32;
|
||||
const uint64_t c = rhs.f >> 32;
|
||||
const uint64_t d = rhs.f & M32;
|
||||
const uint64_t ac = a * c;
|
||||
const uint64_t bc = b * c;
|
||||
const uint64_t ad = a * d;
|
||||
const uint64_t bd = b * d;
|
||||
uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32);
|
||||
tmp += 1U << 31; /// mult_round
|
||||
return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64);
|
||||
#endif
|
||||
}
|
||||
|
||||
DiyFp Normalize() const {
|
||||
int s = static_cast<int>(clzll(f));
|
||||
return DiyFp(f << s, e - s);
|
||||
}
|
||||
|
||||
DiyFp NormalizeBoundary() const {
|
||||
DiyFp res = *this;
|
||||
while (!(res.f & (kDpHiddenBit << 1))) {
|
||||
res.f <<= 1;
|
||||
res.e--;
|
||||
}
|
||||
res.f <<= (kDiySignificandSize - kDpSignificandSize - 2);
|
||||
res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2);
|
||||
return res;
|
||||
}
|
||||
|
||||
void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const {
|
||||
DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary();
|
||||
DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1);
|
||||
mi.f <<= mi.e - pl.e;
|
||||
mi.e = pl.e;
|
||||
*plus = pl;
|
||||
*minus = mi;
|
||||
}
|
||||
|
||||
double ToDouble() const {
|
||||
union {
|
||||
double d;
|
||||
uint64_t u64;
|
||||
}u;
|
||||
RAPIDJSON_ASSERT(f <= kDpHiddenBit + kDpSignificandMask);
|
||||
if (e < kDpDenormalExponent) {
|
||||
// Underflow.
|
||||
return 0.0;
|
||||
}
|
||||
if (e >= kDpMaxExponent) {
|
||||
// Overflow.
|
||||
return std::numeric_limits<double>::infinity();
|
||||
}
|
||||
const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 :
|
||||
static_cast<uint64_t>(e + kDpExponentBias);
|
||||
u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize);
|
||||
return u.d;
|
||||
}
|
||||
|
||||
static const int kDiySignificandSize = 64;
|
||||
static const int kDpSignificandSize = 52;
|
||||
static const int kDpExponentBias = 0x3FF + kDpSignificandSize;
|
||||
static const int kDpMaxExponent = 0x7FF - kDpExponentBias;
|
||||
static const int kDpMinExponent = -kDpExponentBias;
|
||||
static const int kDpDenormalExponent = -kDpExponentBias + 1;
|
||||
static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
|
||||
static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
|
||||
static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
|
||||
|
||||
uint64_t f;
|
||||
int e;
|
||||
};
|
||||
|
||||
inline DiyFp GetCachedPowerByIndex(size_t index) {
|
||||
// 10^-348, 10^-340, ..., 10^340
|
||||
static const uint64_t kCachedPowers_F[] = {
|
||||
RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76),
|
||||
RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea),
|
||||
RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df),
|
||||
RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f),
|
||||
RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c),
|
||||
RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5),
|
||||
RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d),
|
||||
RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637),
|
||||
RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7),
|
||||
RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5),
|
||||
RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b),
|
||||
RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996),
|
||||
RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6),
|
||||
RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8),
|
||||
RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053),
|
||||
RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd),
|
||||
RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94),
|
||||
RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b),
|
||||
RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac),
|
||||
RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3),
|
||||
RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb),
|
||||
RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c),
|
||||
RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000),
|
||||
RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984),
|
||||
RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70),
|
||||
RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245),
|
||||
RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8),
|
||||
RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a),
|
||||
RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea),
|
||||
RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85),
|
||||
RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2),
|
||||
RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3),
|
||||
RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25),
|
||||
RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece),
|
||||
RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5),
|
||||
RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a),
|
||||
RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c),
|
||||
RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a),
|
||||
RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129),
|
||||
RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429),
|
||||
RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d),
|
||||
RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841),
|
||||
RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9),
|
||||
RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b)
|
||||
};
|
||||
static const int16_t kCachedPowers_E[] = {
|
||||
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980,
|
||||
-954, -927, -901, -874, -847, -821, -794, -768, -741, -715,
|
||||
-688, -661, -635, -608, -582, -555, -529, -502, -475, -449,
|
||||
-422, -396, -369, -343, -316, -289, -263, -236, -210, -183,
|
||||
-157, -130, -103, -77, -50, -24, 3, 30, 56, 83,
|
||||
109, 136, 162, 189, 216, 242, 269, 295, 322, 348,
|
||||
375, 402, 428, 455, 481, 508, 534, 561, 588, 614,
|
||||
641, 667, 694, 720, 747, 774, 800, 827, 853, 880,
|
||||
907, 933, 960, 986, 1013, 1039, 1066
|
||||
};
|
||||
RAPIDJSON_ASSERT(index < 87);
|
||||
return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]);
|
||||
}
|
||||
|
||||
inline DiyFp GetCachedPower(int e, int* K) {
|
||||
|
||||
//int k = static_cast<int>(ceil((-61 - e) * 0.30102999566398114)) + 374;
|
||||
double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive
|
||||
int k = static_cast<int>(dk);
|
||||
if (dk - k > 0.0)
|
||||
k++;
|
||||
|
||||
unsigned index = static_cast<unsigned>((k >> 3) + 1);
|
||||
*K = -(-348 + static_cast<int>(index << 3)); // decimal exponent no need lookup table
|
||||
|
||||
return GetCachedPowerByIndex(index);
|
||||
}
|
||||
|
||||
inline DiyFp GetCachedPower10(int exp, int *outExp) {
|
||||
RAPIDJSON_ASSERT(exp >= -348);
|
||||
unsigned index = static_cast<unsigned>(exp + 348) / 8u;
|
||||
*outExp = -348 + static_cast<int>(index) * 8;
|
||||
return GetCachedPowerByIndex(index);
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_POP
|
||||
RAPIDJSON_DIAG_OFF(padded)
|
||||
#endif
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_DIYFP_H_
|
||||
245
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_internal_dtoa.h
generated
vendored
Normal file
@@ -0,0 +1,245 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
|
||||
// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with
|
||||
// integers." ACM Sigplan Notices 45.6 (2010): 233-243.
|
||||
|
||||
#ifndef RAPIDJSON_DTOA_
|
||||
#define RAPIDJSON_DTOA_
|
||||
|
||||
#include "lottie_rapidjson_internal_itoa.h"
|
||||
#include "lottie_rapidjson_internal_diyfp.h"
|
||||
#include "lottie_rapidjson_internal_ieee754.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124
|
||||
#endif
|
||||
|
||||
inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) {
|
||||
while (rest < wp_w && delta - rest >= ten_kappa &&
|
||||
(rest + ten_kappa < wp_w || /// closer
|
||||
wp_w - rest > rest + ten_kappa - wp_w)) {
|
||||
buffer[len - 1]--;
|
||||
rest += ten_kappa;
|
||||
}
|
||||
}
|
||||
|
||||
inline int CountDecimalDigit32(uint32_t n) {
|
||||
// Simple pure C++ implementation was faster than __builtin_clz version in this situation.
|
||||
if (n < 10) return 1;
|
||||
if (n < 100) return 2;
|
||||
if (n < 1000) return 3;
|
||||
if (n < 10000) return 4;
|
||||
if (n < 100000) return 5;
|
||||
if (n < 1000000) return 6;
|
||||
if (n < 10000000) return 7;
|
||||
if (n < 100000000) return 8;
|
||||
// Will not reach 10 digits in DigitGen()
|
||||
//if (n < 1000000000) return 9;
|
||||
//return 10;
|
||||
return 9;
|
||||
}
|
||||
|
||||
inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) {
|
||||
static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
|
||||
const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
|
||||
const DiyFp wp_w = Mp - W;
|
||||
uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
|
||||
uint64_t p2 = Mp.f & (one.f - 1);
|
||||
int kappa = CountDecimalDigit32(p1); // kappa in [0, 9]
|
||||
*len = 0;
|
||||
|
||||
while (kappa > 0) {
|
||||
uint32_t d = 0;
|
||||
switch (kappa) {
|
||||
case 9: d = p1 / 100000000; p1 %= 100000000; break;
|
||||
case 8: d = p1 / 10000000; p1 %= 10000000; break;
|
||||
case 7: d = p1 / 1000000; p1 %= 1000000; break;
|
||||
case 6: d = p1 / 100000; p1 %= 100000; break;
|
||||
case 5: d = p1 / 10000; p1 %= 10000; break;
|
||||
case 4: d = p1 / 1000; p1 %= 1000; break;
|
||||
case 3: d = p1 / 100; p1 %= 100; break;
|
||||
case 2: d = p1 / 10; p1 %= 10; break;
|
||||
case 1: d = p1; p1 = 0; break;
|
||||
default:;
|
||||
}
|
||||
if (d || *len)
|
||||
buffer[(*len)++] = static_cast<char>('0' + static_cast<char>(d));
|
||||
kappa--;
|
||||
uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
|
||||
if (tmp <= delta) {
|
||||
*K += kappa;
|
||||
GrisuRound(buffer, *len, delta, tmp, static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// kappa = 0
|
||||
for (;;) {
|
||||
p2 *= 10;
|
||||
delta *= 10;
|
||||
char d = static_cast<char>(p2 >> -one.e);
|
||||
if (d || *len)
|
||||
buffer[(*len)++] = static_cast<char>('0' + d);
|
||||
p2 &= one.f - 1;
|
||||
kappa--;
|
||||
if (p2 < delta) {
|
||||
*K += kappa;
|
||||
int index = -kappa;
|
||||
GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[index] : 0));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void Grisu2(double value, char* buffer, int* length, int* K) {
|
||||
const DiyFp v(value);
|
||||
DiyFp w_m, w_p;
|
||||
v.NormalizedBoundaries(&w_m, &w_p);
|
||||
|
||||
const DiyFp c_mk = GetCachedPower(w_p.e, K);
|
||||
const DiyFp W = v.Normalize() * c_mk;
|
||||
DiyFp Wp = w_p * c_mk;
|
||||
DiyFp Wm = w_m * c_mk;
|
||||
Wm.f++;
|
||||
Wp.f--;
|
||||
DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K);
|
||||
}
|
||||
|
||||
inline char* WriteExponent(int K, char* buffer) {
|
||||
if (K < 0) {
|
||||
*buffer++ = '-';
|
||||
K = -K;
|
||||
}
|
||||
|
||||
if (K >= 100) {
|
||||
*buffer++ = static_cast<char>('0' + static_cast<char>(K / 100));
|
||||
K %= 100;
|
||||
const char* d = GetDigitsLut() + K * 2;
|
||||
*buffer++ = d[0];
|
||||
*buffer++ = d[1];
|
||||
}
|
||||
else if (K >= 10) {
|
||||
const char* d = GetDigitsLut() + K * 2;
|
||||
*buffer++ = d[0];
|
||||
*buffer++ = d[1];
|
||||
}
|
||||
else
|
||||
*buffer++ = static_cast<char>('0' + static_cast<char>(K));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) {
|
||||
const int kk = length + k; // 10^(kk-1) <= v < 10^kk
|
||||
|
||||
if (0 <= k && kk <= 21) {
|
||||
// 1234e7 -> 12340000000
|
||||
for (int i = length; i < kk; i++)
|
||||
buffer[i] = '0';
|
||||
buffer[kk] = '.';
|
||||
buffer[kk + 1] = '0';
|
||||
return &buffer[kk + 2];
|
||||
}
|
||||
else if (0 < kk && kk <= 21) {
|
||||
// 1234e-2 -> 12.34
|
||||
std::memmove(&buffer[kk + 1], &buffer[kk], static_cast<size_t>(length - kk));
|
||||
buffer[kk] = '.';
|
||||
if (0 > k + maxDecimalPlaces) {
|
||||
// When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1
|
||||
// Remove extra trailing zeros (at least one) after truncation.
|
||||
for (int i = kk + maxDecimalPlaces; i > kk + 1; i--)
|
||||
if (buffer[i] != '0')
|
||||
return &buffer[i + 1];
|
||||
return &buffer[kk + 2]; // Reserve one zero
|
||||
}
|
||||
else
|
||||
return &buffer[length + 1];
|
||||
}
|
||||
else if (-6 < kk && kk <= 0) {
|
||||
// 1234e-6 -> 0.001234
|
||||
const int offset = 2 - kk;
|
||||
std::memmove(&buffer[offset], &buffer[0], static_cast<size_t>(length));
|
||||
buffer[0] = '0';
|
||||
buffer[1] = '.';
|
||||
for (int i = 2; i < offset; i++)
|
||||
buffer[i] = '0';
|
||||
if (length - kk > maxDecimalPlaces) {
|
||||
// When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1
|
||||
// Remove extra trailing zeros (at least one) after truncation.
|
||||
for (int i = maxDecimalPlaces + 1; i > 2; i--)
|
||||
if (buffer[i] != '0')
|
||||
return &buffer[i + 1];
|
||||
return &buffer[3]; // Reserve one zero
|
||||
}
|
||||
else
|
||||
return &buffer[length + offset];
|
||||
}
|
||||
else if (kk < -maxDecimalPlaces) {
|
||||
// Truncate to zero
|
||||
buffer[0] = '0';
|
||||
buffer[1] = '.';
|
||||
buffer[2] = '0';
|
||||
return &buffer[3];
|
||||
}
|
||||
else if (length == 1) {
|
||||
// 1e30
|
||||
buffer[1] = 'e';
|
||||
return WriteExponent(kk - 1, &buffer[2]);
|
||||
}
|
||||
else {
|
||||
// 1234e30 -> 1.234e33
|
||||
std::memmove(&buffer[2], &buffer[1], static_cast<size_t>(length - 1));
|
||||
buffer[1] = '.';
|
||||
buffer[length + 1] = 'e';
|
||||
return WriteExponent(kk - 1, &buffer[0 + length + 2]);
|
||||
}
|
||||
}
|
||||
|
||||
inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) {
|
||||
RAPIDJSON_ASSERT(maxDecimalPlaces >= 1);
|
||||
Double d(value);
|
||||
if (d.IsZero()) {
|
||||
if (d.Sign())
|
||||
*buffer++ = '-'; // -0.0, Issue #289
|
||||
buffer[0] = '0';
|
||||
buffer[1] = '.';
|
||||
buffer[2] = '0';
|
||||
return &buffer[3];
|
||||
}
|
||||
else {
|
||||
if (value < 0) {
|
||||
*buffer++ = '-';
|
||||
value = -value;
|
||||
}
|
||||
int length, K;
|
||||
Grisu2(value, buffer, &length, &K);
|
||||
return Prettify(buffer, length, K, maxDecimalPlaces);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_DTOA_
|
||||
78
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_internal_ieee754.h
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_IEEE754_
|
||||
#define RAPIDJSON_IEEE754_
|
||||
|
||||
#include "lottie_rapidjson_rapidjson.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
class Double {
|
||||
public:
|
||||
Double() {}
|
||||
Double(double d) : d_(d) {}
|
||||
Double(uint64_t u) : u_(u) {}
|
||||
|
||||
double Value() const { return d_; }
|
||||
uint64_t Uint64Value() const { return u_; }
|
||||
|
||||
double NextPositiveDouble() const {
|
||||
RAPIDJSON_ASSERT(!Sign());
|
||||
return Double(u_ + 1).Value();
|
||||
}
|
||||
|
||||
bool Sign() const { return (u_ & kSignMask) != 0; }
|
||||
uint64_t Significand() const { return u_ & kSignificandMask; }
|
||||
int Exponent() const { return static_cast<int>(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); }
|
||||
|
||||
bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; }
|
||||
bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; }
|
||||
bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; }
|
||||
bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; }
|
||||
bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; }
|
||||
|
||||
uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); }
|
||||
int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; }
|
||||
uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; }
|
||||
|
||||
static int EffectiveSignificandSize(int order) {
|
||||
if (order >= -1021)
|
||||
return 53;
|
||||
else if (order <= -1074)
|
||||
return 0;
|
||||
else
|
||||
return order + 1074;
|
||||
}
|
||||
|
||||
private:
|
||||
static const int kSignificandSize = 52;
|
||||
static const int kExponentBias = 0x3FF;
|
||||
static const int kDenormalExponent = 1 - kExponentBias;
|
||||
static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000);
|
||||
static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
|
||||
static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
|
||||
static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
|
||||
|
||||
union {
|
||||
double d_;
|
||||
uint64_t u_;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_IEEE754_
|
||||
308
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_internal_itoa.h
generated
vendored
Normal file
@@ -0,0 +1,308 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ITOA_
|
||||
#define RAPIDJSON_ITOA_
|
||||
|
||||
#include "lottie_rapidjson_rapidjson.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
inline const char* GetDigitsLut() {
|
||||
static const char cDigitsLut[200] = {
|
||||
'0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9',
|
||||
'1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9',
|
||||
'2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9',
|
||||
'3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9',
|
||||
'4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9',
|
||||
'5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9',
|
||||
'6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9',
|
||||
'7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9',
|
||||
'8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9',
|
||||
'9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9'
|
||||
};
|
||||
return cDigitsLut;
|
||||
}
|
||||
|
||||
inline char* u32toa(uint32_t value, char* buffer) {
|
||||
RAPIDJSON_ASSERT(buffer != 0);
|
||||
|
||||
const char* cDigitsLut = GetDigitsLut();
|
||||
|
||||
if (value < 10000) {
|
||||
const uint32_t d1 = (value / 100) << 1;
|
||||
const uint32_t d2 = (value % 100) << 1;
|
||||
|
||||
if (value >= 1000)
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
if (value >= 100)
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
if (value >= 10)
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
}
|
||||
else if (value < 100000000) {
|
||||
// value = bbbbcccc
|
||||
const uint32_t b = value / 10000;
|
||||
const uint32_t c = value % 10000;
|
||||
|
||||
const uint32_t d1 = (b / 100) << 1;
|
||||
const uint32_t d2 = (b % 100) << 1;
|
||||
|
||||
const uint32_t d3 = (c / 100) << 1;
|
||||
const uint32_t d4 = (c % 100) << 1;
|
||||
|
||||
if (value >= 10000000)
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
if (value >= 1000000)
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
if (value >= 100000)
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
|
||||
*buffer++ = cDigitsLut[d3];
|
||||
*buffer++ = cDigitsLut[d3 + 1];
|
||||
*buffer++ = cDigitsLut[d4];
|
||||
*buffer++ = cDigitsLut[d4 + 1];
|
||||
}
|
||||
else {
|
||||
// value = aabbbbcccc in decimal
|
||||
|
||||
const uint32_t a = value / 100000000; // 1 to 42
|
||||
value %= 100000000;
|
||||
|
||||
if (a >= 10) {
|
||||
const unsigned i = a << 1;
|
||||
*buffer++ = cDigitsLut[i];
|
||||
*buffer++ = cDigitsLut[i + 1];
|
||||
}
|
||||
else
|
||||
*buffer++ = static_cast<char>('0' + static_cast<char>(a));
|
||||
|
||||
const uint32_t b = value / 10000; // 0 to 9999
|
||||
const uint32_t c = value % 10000; // 0 to 9999
|
||||
|
||||
const uint32_t d1 = (b / 100) << 1;
|
||||
const uint32_t d2 = (b % 100) << 1;
|
||||
|
||||
const uint32_t d3 = (c / 100) << 1;
|
||||
const uint32_t d4 = (c % 100) << 1;
|
||||
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
*buffer++ = cDigitsLut[d3];
|
||||
*buffer++ = cDigitsLut[d3 + 1];
|
||||
*buffer++ = cDigitsLut[d4];
|
||||
*buffer++ = cDigitsLut[d4 + 1];
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
inline char* i32toa(int32_t value, char* buffer) {
|
||||
RAPIDJSON_ASSERT(buffer != 0);
|
||||
uint32_t u = static_cast<uint32_t>(value);
|
||||
if (value < 0) {
|
||||
*buffer++ = '-';
|
||||
u = ~u + 1;
|
||||
}
|
||||
|
||||
return u32toa(u, buffer);
|
||||
}
|
||||
|
||||
inline char* u64toa(uint64_t value, char* buffer) {
|
||||
RAPIDJSON_ASSERT(buffer != 0);
|
||||
const char* cDigitsLut = GetDigitsLut();
|
||||
const uint64_t kTen8 = 100000000;
|
||||
const uint64_t kTen9 = kTen8 * 10;
|
||||
const uint64_t kTen10 = kTen8 * 100;
|
||||
const uint64_t kTen11 = kTen8 * 1000;
|
||||
const uint64_t kTen12 = kTen8 * 10000;
|
||||
const uint64_t kTen13 = kTen8 * 100000;
|
||||
const uint64_t kTen14 = kTen8 * 1000000;
|
||||
const uint64_t kTen15 = kTen8 * 10000000;
|
||||
const uint64_t kTen16 = kTen8 * kTen8;
|
||||
|
||||
if (value < kTen8) {
|
||||
uint32_t v = static_cast<uint32_t>(value);
|
||||
if (v < 10000) {
|
||||
const uint32_t d1 = (v / 100) << 1;
|
||||
const uint32_t d2 = (v % 100) << 1;
|
||||
|
||||
if (v >= 1000)
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
if (v >= 100)
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
if (v >= 10)
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
}
|
||||
else {
|
||||
// value = bbbbcccc
|
||||
const uint32_t b = v / 10000;
|
||||
const uint32_t c = v % 10000;
|
||||
|
||||
const uint32_t d1 = (b / 100) << 1;
|
||||
const uint32_t d2 = (b % 100) << 1;
|
||||
|
||||
const uint32_t d3 = (c / 100) << 1;
|
||||
const uint32_t d4 = (c % 100) << 1;
|
||||
|
||||
if (value >= 10000000)
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
if (value >= 1000000)
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
if (value >= 100000)
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
|
||||
*buffer++ = cDigitsLut[d3];
|
||||
*buffer++ = cDigitsLut[d3 + 1];
|
||||
*buffer++ = cDigitsLut[d4];
|
||||
*buffer++ = cDigitsLut[d4 + 1];
|
||||
}
|
||||
}
|
||||
else if (value < kTen16) {
|
||||
const uint32_t v0 = static_cast<uint32_t>(value / kTen8);
|
||||
const uint32_t v1 = static_cast<uint32_t>(value % kTen8);
|
||||
|
||||
const uint32_t b0 = v0 / 10000;
|
||||
const uint32_t c0 = v0 % 10000;
|
||||
|
||||
const uint32_t d1 = (b0 / 100) << 1;
|
||||
const uint32_t d2 = (b0 % 100) << 1;
|
||||
|
||||
const uint32_t d3 = (c0 / 100) << 1;
|
||||
const uint32_t d4 = (c0 % 100) << 1;
|
||||
|
||||
const uint32_t b1 = v1 / 10000;
|
||||
const uint32_t c1 = v1 % 10000;
|
||||
|
||||
const uint32_t d5 = (b1 / 100) << 1;
|
||||
const uint32_t d6 = (b1 % 100) << 1;
|
||||
|
||||
const uint32_t d7 = (c1 / 100) << 1;
|
||||
const uint32_t d8 = (c1 % 100) << 1;
|
||||
|
||||
if (value >= kTen15)
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
if (value >= kTen14)
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
if (value >= kTen13)
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
if (value >= kTen12)
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
if (value >= kTen11)
|
||||
*buffer++ = cDigitsLut[d3];
|
||||
if (value >= kTen10)
|
||||
*buffer++ = cDigitsLut[d3 + 1];
|
||||
if (value >= kTen9)
|
||||
*buffer++ = cDigitsLut[d4];
|
||||
|
||||
*buffer++ = cDigitsLut[d4 + 1];
|
||||
*buffer++ = cDigitsLut[d5];
|
||||
*buffer++ = cDigitsLut[d5 + 1];
|
||||
*buffer++ = cDigitsLut[d6];
|
||||
*buffer++ = cDigitsLut[d6 + 1];
|
||||
*buffer++ = cDigitsLut[d7];
|
||||
*buffer++ = cDigitsLut[d7 + 1];
|
||||
*buffer++ = cDigitsLut[d8];
|
||||
*buffer++ = cDigitsLut[d8 + 1];
|
||||
}
|
||||
else {
|
||||
const uint32_t a = static_cast<uint32_t>(value / kTen16); // 1 to 1844
|
||||
value %= kTen16;
|
||||
|
||||
if (a < 10)
|
||||
*buffer++ = static_cast<char>('0' + static_cast<char>(a));
|
||||
else if (a < 100) {
|
||||
const uint32_t i = a << 1;
|
||||
*buffer++ = cDigitsLut[i];
|
||||
*buffer++ = cDigitsLut[i + 1];
|
||||
}
|
||||
else if (a < 1000) {
|
||||
*buffer++ = static_cast<char>('0' + static_cast<char>(a / 100));
|
||||
|
||||
const uint32_t i = (a % 100) << 1;
|
||||
*buffer++ = cDigitsLut[i];
|
||||
*buffer++ = cDigitsLut[i + 1];
|
||||
}
|
||||
else {
|
||||
const uint32_t i = (a / 100) << 1;
|
||||
const uint32_t j = (a % 100) << 1;
|
||||
*buffer++ = cDigitsLut[i];
|
||||
*buffer++ = cDigitsLut[i + 1];
|
||||
*buffer++ = cDigitsLut[j];
|
||||
*buffer++ = cDigitsLut[j + 1];
|
||||
}
|
||||
|
||||
const uint32_t v0 = static_cast<uint32_t>(value / kTen8);
|
||||
const uint32_t v1 = static_cast<uint32_t>(value % kTen8);
|
||||
|
||||
const uint32_t b0 = v0 / 10000;
|
||||
const uint32_t c0 = v0 % 10000;
|
||||
|
||||
const uint32_t d1 = (b0 / 100) << 1;
|
||||
const uint32_t d2 = (b0 % 100) << 1;
|
||||
|
||||
const uint32_t d3 = (c0 / 100) << 1;
|
||||
const uint32_t d4 = (c0 % 100) << 1;
|
||||
|
||||
const uint32_t b1 = v1 / 10000;
|
||||
const uint32_t c1 = v1 % 10000;
|
||||
|
||||
const uint32_t d5 = (b1 / 100) << 1;
|
||||
const uint32_t d6 = (b1 % 100) << 1;
|
||||
|
||||
const uint32_t d7 = (c1 / 100) << 1;
|
||||
const uint32_t d8 = (c1 % 100) << 1;
|
||||
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
*buffer++ = cDigitsLut[d3];
|
||||
*buffer++ = cDigitsLut[d3 + 1];
|
||||
*buffer++ = cDigitsLut[d4];
|
||||
*buffer++ = cDigitsLut[d4 + 1];
|
||||
*buffer++ = cDigitsLut[d5];
|
||||
*buffer++ = cDigitsLut[d5 + 1];
|
||||
*buffer++ = cDigitsLut[d6];
|
||||
*buffer++ = cDigitsLut[d6 + 1];
|
||||
*buffer++ = cDigitsLut[d7];
|
||||
*buffer++ = cDigitsLut[d7 + 1];
|
||||
*buffer++ = cDigitsLut[d8];
|
||||
*buffer++ = cDigitsLut[d8 + 1];
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
inline char* i64toa(int64_t value, char* buffer) {
|
||||
RAPIDJSON_ASSERT(buffer != 0);
|
||||
uint64_t u = static_cast<uint64_t>(value);
|
||||
if (value < 0) {
|
||||
*buffer++ = '-';
|
||||
u = ~u + 1;
|
||||
}
|
||||
|
||||
return u64toa(u, buffer);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_ITOA_
|
||||
186
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_internal_meta.h
generated
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_INTERNAL_META_H_
|
||||
#define RAPIDJSON_INTERNAL_META_H_
|
||||
|
||||
#include "lottie_rapidjson_rapidjson.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(6334)
|
||||
#endif
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_TYPETRAITS
|
||||
#include <type_traits>
|
||||
#endif
|
||||
|
||||
//@cond RAPIDJSON_INTERNAL
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching
|
||||
template <typename T> struct Void { typedef void Type; };
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// BoolType, TrueType, FalseType
|
||||
//
|
||||
template <bool Cond> struct BoolType {
|
||||
static const bool Value = Cond;
|
||||
typedef BoolType Type;
|
||||
};
|
||||
typedef BoolType<true> TrueType;
|
||||
typedef BoolType<false> FalseType;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr
|
||||
//
|
||||
|
||||
template <bool C> struct SelectIfImpl { template <typename T1, typename T2> struct Apply { typedef T1 Type; }; };
|
||||
template <> struct SelectIfImpl<false> { template <typename T1, typename T2> struct Apply { typedef T2 Type; }; };
|
||||
template <bool C, typename T1, typename T2> struct SelectIfCond : SelectIfImpl<C>::template Apply<T1,T2> {};
|
||||
template <typename C, typename T1, typename T2> struct SelectIf : SelectIfCond<C::Value, T1, T2> {};
|
||||
|
||||
template <bool Cond1, bool Cond2> struct AndExprCond : FalseType {};
|
||||
template <> struct AndExprCond<true, true> : TrueType {};
|
||||
template <bool Cond1, bool Cond2> struct OrExprCond : TrueType {};
|
||||
template <> struct OrExprCond<false, false> : FalseType {};
|
||||
|
||||
template <typename C> struct BoolExpr : SelectIf<C,TrueType,FalseType>::Type {};
|
||||
template <typename C> struct NotExpr : SelectIf<C,FalseType,TrueType>::Type {};
|
||||
template <typename C1, typename C2> struct AndExpr : AndExprCond<C1::Value, C2::Value>::Type {};
|
||||
template <typename C1, typename C2> struct OrExpr : OrExprCond<C1::Value, C2::Value>::Type {};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// AddConst, MaybeAddConst, RemoveConst
|
||||
template <typename T> struct AddConst { typedef const T Type; };
|
||||
template <bool Constify, typename T> struct MaybeAddConst : SelectIfCond<Constify, const T, T> {};
|
||||
template <typename T> struct RemoveConst { typedef T Type; };
|
||||
template <typename T> struct RemoveConst<const T> { typedef T Type; };
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// IsSame, IsConst, IsMoreConst, IsPointer
|
||||
//
|
||||
template <typename T, typename U> struct IsSame : FalseType {};
|
||||
template <typename T> struct IsSame<T, T> : TrueType {};
|
||||
|
||||
template <typename T> struct IsConst : FalseType {};
|
||||
template <typename T> struct IsConst<const T> : TrueType {};
|
||||
|
||||
template <typename CT, typename T>
|
||||
struct IsMoreConst
|
||||
: AndExpr<IsSame<typename RemoveConst<CT>::Type, typename RemoveConst<T>::Type>,
|
||||
BoolType<IsConst<CT>::Value >= IsConst<T>::Value> >::Type {};
|
||||
|
||||
template <typename T> struct IsPointer : FalseType {};
|
||||
template <typename T> struct IsPointer<T*> : TrueType {};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// IsBaseOf
|
||||
//
|
||||
#if RAPIDJSON_HAS_CXX11_TYPETRAITS
|
||||
|
||||
template <typename B, typename D> struct IsBaseOf
|
||||
: BoolType< ::std::is_base_of<B,D>::value> {};
|
||||
|
||||
#else // simplified version adopted from Boost
|
||||
|
||||
template<typename B, typename D> struct IsBaseOfImpl {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0);
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0);
|
||||
|
||||
typedef char (&Yes)[1];
|
||||
typedef char (&No) [2];
|
||||
|
||||
template <typename T>
|
||||
static Yes Check(const D*, T);
|
||||
static No Check(const B*, int);
|
||||
|
||||
struct Host {
|
||||
operator const B*() const;
|
||||
operator const D*();
|
||||
};
|
||||
|
||||
enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) };
|
||||
};
|
||||
|
||||
template <typename B, typename D> struct IsBaseOf
|
||||
: OrExpr<IsSame<B, D>, BoolExpr<IsBaseOfImpl<B, D> > >::Type {};
|
||||
|
||||
#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EnableIf / DisableIf
|
||||
//
|
||||
template <bool Condition, typename T = void> struct EnableIfCond { typedef T Type; };
|
||||
template <typename T> struct EnableIfCond<false, T> { /* empty */ };
|
||||
|
||||
template <bool Condition, typename T = void> struct DisableIfCond { typedef T Type; };
|
||||
template <typename T> struct DisableIfCond<true, T> { /* empty */ };
|
||||
|
||||
template <typename Condition, typename T = void>
|
||||
struct EnableIf : EnableIfCond<Condition::Value, T> {};
|
||||
|
||||
template <typename Condition, typename T = void>
|
||||
struct DisableIf : DisableIfCond<Condition::Value, T> {};
|
||||
|
||||
// SFINAE helpers
|
||||
struct SfinaeTag {};
|
||||
template <typename T> struct RemoveSfinaeTag;
|
||||
template <typename T> struct RemoveSfinaeTag<SfinaeTag&(*)(T)> { typedef T Type; };
|
||||
|
||||
#define RAPIDJSON_REMOVEFPTR_(type) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \
|
||||
< ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type
|
||||
|
||||
#define RAPIDJSON_ENABLEIF(cond) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
|
||||
<RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
|
||||
|
||||
#define RAPIDJSON_DISABLEIF(cond) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
|
||||
<RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
|
||||
|
||||
#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
|
||||
<RAPIDJSON_REMOVEFPTR_(cond), \
|
||||
RAPIDJSON_REMOVEFPTR_(returntype)>::Type
|
||||
|
||||
#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
|
||||
<RAPIDJSON_REMOVEFPTR_(cond), \
|
||||
RAPIDJSON_REMOVEFPTR_(returntype)>::Type
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
//@endcond
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_INTERNAL_META_H_
|
||||
55
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_internal_pow10.h
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_POW10_
|
||||
#define RAPIDJSON_POW10_
|
||||
|
||||
#include "lottie_rapidjson_rapidjson.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
//! Computes integer powers of 10 in double (10.0^n).
|
||||
/*! This function uses lookup table for fast and accurate results.
|
||||
\param n non-negative exponent. Must <= 308.
|
||||
\return 10.0^n
|
||||
*/
|
||||
inline double Pow10(int n) {
|
||||
static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes
|
||||
1e+0,
|
||||
1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20,
|
||||
1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40,
|
||||
1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60,
|
||||
1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80,
|
||||
1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100,
|
||||
1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120,
|
||||
1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140,
|
||||
1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160,
|
||||
1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180,
|
||||
1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200,
|
||||
1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220,
|
||||
1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240,
|
||||
1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260,
|
||||
1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280,
|
||||
1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300,
|
||||
1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308
|
||||
};
|
||||
RAPIDJSON_ASSERT(n >= 0 && n <= 308);
|
||||
return e[n];
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_POW10_
|
||||
739
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_internal_regex.h
generated
vendored
Normal file
@@ -0,0 +1,739 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_INTERNAL_REGEX_H_
|
||||
#define RAPIDJSON_INTERNAL_REGEX_H_
|
||||
|
||||
#include "lottie_rapidjson_allocators.h"
|
||||
#include "lottie_rapidjson_stream.h"
|
||||
#include "lottie_rapidjson_internal_stack.h"
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(padded)
|
||||
RAPIDJSON_DIAG_OFF(switch-enum)
|
||||
#elif defined(_MSC_VER)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
|
||||
#ifndef RAPIDJSON_REGEX_VERBOSE
|
||||
#define RAPIDJSON_REGEX_VERBOSE 0
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// DecodedStream
|
||||
|
||||
template <typename SourceStream, typename Encoding>
|
||||
class DecodedStream {
|
||||
public:
|
||||
DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); }
|
||||
unsigned Peek() { return codepoint_; }
|
||||
unsigned Take() {
|
||||
unsigned c = codepoint_;
|
||||
if (c) // No further decoding when '\0'
|
||||
Decode();
|
||||
return c;
|
||||
}
|
||||
|
||||
private:
|
||||
void Decode() {
|
||||
if (!Encoding::Decode(ss_, &codepoint_))
|
||||
codepoint_ = 0;
|
||||
}
|
||||
|
||||
SourceStream& ss_;
|
||||
unsigned codepoint_;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// GenericRegex
|
||||
|
||||
static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1
|
||||
static const SizeType kRegexInvalidRange = ~SizeType(0);
|
||||
|
||||
template <typename Encoding, typename Allocator>
|
||||
class GenericRegexSearch;
|
||||
|
||||
//! Regular expression engine with subset of ECMAscript grammar.
|
||||
/*!
|
||||
Supported regular expression syntax:
|
||||
- \c ab Concatenation
|
||||
- \c a|b Alternation
|
||||
- \c a? Zero or one
|
||||
- \c a* Zero or more
|
||||
- \c a+ One or more
|
||||
- \c a{3} Exactly 3 times
|
||||
- \c a{3,} At least 3 times
|
||||
- \c a{3,5} 3 to 5 times
|
||||
- \c (ab) Grouping
|
||||
- \c ^a At the beginning
|
||||
- \c a$ At the end
|
||||
- \c . Any character
|
||||
- \c [abc] Character classes
|
||||
- \c [a-c] Character class range
|
||||
- \c [a-z0-9_] Character class combination
|
||||
- \c [^abc] Negated character classes
|
||||
- \c [^a-c] Negated character class range
|
||||
- \c [\b] Backspace (U+0008)
|
||||
- \c \\| \\\\ ... Escape characters
|
||||
- \c \\f Form feed (U+000C)
|
||||
- \c \\n Line feed (U+000A)
|
||||
- \c \\r Carriage return (U+000D)
|
||||
- \c \\t Tab (U+0009)
|
||||
- \c \\v Vertical tab (U+000B)
|
||||
|
||||
\note This is a Thompson NFA engine, implemented with reference to
|
||||
Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).",
|
||||
https://swtch.com/~rsc/regexp/regexp1.html
|
||||
*/
|
||||
template <typename Encoding, typename Allocator = CrtAllocator>
|
||||
class GenericRegex {
|
||||
public:
|
||||
typedef Encoding EncodingType;
|
||||
typedef typename Encoding::Ch Ch;
|
||||
template <typename, typename> friend class GenericRegexSearch;
|
||||
|
||||
GenericRegex(const Ch* source, Allocator* allocator = 0) :
|
||||
ownAllocator_(allocator ? 0 : RAPIDJSON_NEW(Allocator)()), allocator_(allocator ? allocator : ownAllocator_),
|
||||
states_(allocator_, 256), ranges_(allocator_, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(),
|
||||
anchorBegin_(), anchorEnd_()
|
||||
{
|
||||
GenericStringStream<Encoding> ss(source);
|
||||
DecodedStream<GenericStringStream<Encoding>, Encoding> ds(ss);
|
||||
Parse(ds);
|
||||
}
|
||||
|
||||
~GenericRegex()
|
||||
{
|
||||
RAPIDJSON_DELETE(ownAllocator_);
|
||||
}
|
||||
|
||||
bool IsValid() const {
|
||||
return root_ != kRegexInvalidState;
|
||||
}
|
||||
|
||||
private:
|
||||
enum Operator {
|
||||
kZeroOrOne,
|
||||
kZeroOrMore,
|
||||
kOneOrMore,
|
||||
kConcatenation,
|
||||
kAlternation,
|
||||
kLeftParenthesis
|
||||
};
|
||||
|
||||
static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.'
|
||||
static const unsigned kRangeCharacterClass = 0xFFFFFFFE;
|
||||
static const unsigned kRangeNegationFlag = 0x80000000;
|
||||
|
||||
struct Range {
|
||||
unsigned start; //
|
||||
unsigned end;
|
||||
SizeType next;
|
||||
};
|
||||
|
||||
struct State {
|
||||
SizeType out; //!< Equals to kInvalid for matching state
|
||||
SizeType out1; //!< Equals to non-kInvalid for split
|
||||
SizeType rangeStart;
|
||||
unsigned codepoint;
|
||||
};
|
||||
|
||||
struct Frag {
|
||||
Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {}
|
||||
SizeType start;
|
||||
SizeType out; //!< link-list of all output states
|
||||
SizeType minIndex;
|
||||
};
|
||||
|
||||
State& GetState(SizeType index) {
|
||||
RAPIDJSON_ASSERT(index < stateCount_);
|
||||
return states_.template Bottom<State>()[index];
|
||||
}
|
||||
|
||||
const State& GetState(SizeType index) const {
|
||||
RAPIDJSON_ASSERT(index < stateCount_);
|
||||
return states_.template Bottom<State>()[index];
|
||||
}
|
||||
|
||||
Range& GetRange(SizeType index) {
|
||||
RAPIDJSON_ASSERT(index < rangeCount_);
|
||||
return ranges_.template Bottom<Range>()[index];
|
||||
}
|
||||
|
||||
const Range& GetRange(SizeType index) const {
|
||||
RAPIDJSON_ASSERT(index < rangeCount_);
|
||||
return ranges_.template Bottom<Range>()[index];
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
void Parse(DecodedStream<InputStream, Encoding>& ds) {
|
||||
Stack<Allocator> operandStack(allocator_, 256); // Frag
|
||||
Stack<Allocator> operatorStack(allocator_, 256); // Operator
|
||||
Stack<Allocator> atomCountStack(allocator_, 256); // unsigned (Atom per parenthesis)
|
||||
|
||||
*atomCountStack.template Push<unsigned>() = 0;
|
||||
|
||||
unsigned codepoint;
|
||||
while (ds.Peek() != 0) {
|
||||
switch (codepoint = ds.Take()) {
|
||||
case '^':
|
||||
anchorBegin_ = true;
|
||||
break;
|
||||
|
||||
case '$':
|
||||
anchorEnd_ = true;
|
||||
break;
|
||||
|
||||
case '|':
|
||||
while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() < kAlternation)
|
||||
if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
|
||||
return;
|
||||
*operatorStack.template Push<Operator>() = kAlternation;
|
||||
*atomCountStack.template Top<unsigned>() = 0;
|
||||
break;
|
||||
|
||||
case '(':
|
||||
*operatorStack.template Push<Operator>() = kLeftParenthesis;
|
||||
*atomCountStack.template Push<unsigned>() = 0;
|
||||
break;
|
||||
|
||||
case ')':
|
||||
while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() != kLeftParenthesis)
|
||||
if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
|
||||
return;
|
||||
if (operatorStack.Empty())
|
||||
return;
|
||||
operatorStack.template Pop<Operator>(1);
|
||||
atomCountStack.template Pop<unsigned>(1);
|
||||
ImplicitConcatenation(atomCountStack, operatorStack);
|
||||
break;
|
||||
|
||||
case '?':
|
||||
if (!Eval(operandStack, kZeroOrOne))
|
||||
return;
|
||||
break;
|
||||
|
||||
case '*':
|
||||
if (!Eval(operandStack, kZeroOrMore))
|
||||
return;
|
||||
break;
|
||||
|
||||
case '+':
|
||||
if (!Eval(operandStack, kOneOrMore))
|
||||
return;
|
||||
break;
|
||||
|
||||
case '{':
|
||||
{
|
||||
unsigned n, m;
|
||||
if (!ParseUnsigned(ds, &n))
|
||||
return;
|
||||
|
||||
if (ds.Peek() == ',') {
|
||||
ds.Take();
|
||||
if (ds.Peek() == '}')
|
||||
m = kInfinityQuantifier;
|
||||
else if (!ParseUnsigned(ds, &m) || m < n)
|
||||
return;
|
||||
}
|
||||
else
|
||||
m = n;
|
||||
|
||||
if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}')
|
||||
return;
|
||||
ds.Take();
|
||||
}
|
||||
break;
|
||||
|
||||
case '.':
|
||||
PushOperand(operandStack, kAnyCharacterClass);
|
||||
ImplicitConcatenation(atomCountStack, operatorStack);
|
||||
break;
|
||||
|
||||
case '[':
|
||||
{
|
||||
SizeType range;
|
||||
if (!ParseRange(ds, &range))
|
||||
return;
|
||||
SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass);
|
||||
GetState(s).rangeStart = range;
|
||||
*operandStack.template Push<Frag>() = Frag(s, s, s);
|
||||
}
|
||||
ImplicitConcatenation(atomCountStack, operatorStack);
|
||||
break;
|
||||
|
||||
case '\\': // Escape character
|
||||
if (!CharacterEscape(ds, &codepoint))
|
||||
return; // Unsupported escape character
|
||||
// fall through to default
|
||||
RAPIDJSON_DELIBERATE_FALLTHROUGH;
|
||||
|
||||
default: // Pattern character
|
||||
PushOperand(operandStack, codepoint);
|
||||
ImplicitConcatenation(atomCountStack, operatorStack);
|
||||
}
|
||||
}
|
||||
|
||||
while (!operatorStack.Empty())
|
||||
if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
|
||||
return;
|
||||
|
||||
// Link the operand to matching state.
|
||||
if (operandStack.GetSize() == sizeof(Frag)) {
|
||||
Frag* e = operandStack.template Pop<Frag>(1);
|
||||
Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0));
|
||||
root_ = e->start;
|
||||
|
||||
#if RAPIDJSON_REGEX_VERBOSE
|
||||
printf("root: %d\n", root_);
|
||||
for (SizeType i = 0; i < stateCount_ ; i++) {
|
||||
State& s = GetState(i);
|
||||
printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) {
|
||||
State* s = states_.template Push<State>();
|
||||
s->out = out;
|
||||
s->out1 = out1;
|
||||
s->codepoint = codepoint;
|
||||
s->rangeStart = kRegexInvalidRange;
|
||||
return stateCount_++;
|
||||
}
|
||||
|
||||
void PushOperand(Stack<Allocator>& operandStack, unsigned codepoint) {
|
||||
SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint);
|
||||
*operandStack.template Push<Frag>() = Frag(s, s, s);
|
||||
}
|
||||
|
||||
void ImplicitConcatenation(Stack<Allocator>& atomCountStack, Stack<Allocator>& operatorStack) {
|
||||
if (*atomCountStack.template Top<unsigned>())
|
||||
*operatorStack.template Push<Operator>() = kConcatenation;
|
||||
(*atomCountStack.template Top<unsigned>())++;
|
||||
}
|
||||
|
||||
SizeType Append(SizeType l1, SizeType l2) {
|
||||
SizeType old = l1;
|
||||
while (GetState(l1).out != kRegexInvalidState)
|
||||
l1 = GetState(l1).out;
|
||||
GetState(l1).out = l2;
|
||||
return old;
|
||||
}
|
||||
|
||||
void Patch(SizeType l, SizeType s) {
|
||||
for (SizeType next; l != kRegexInvalidState; l = next) {
|
||||
next = GetState(l).out;
|
||||
GetState(l).out = s;
|
||||
}
|
||||
}
|
||||
|
||||
bool Eval(Stack<Allocator>& operandStack, Operator op) {
|
||||
switch (op) {
|
||||
case kConcatenation:
|
||||
RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2);
|
||||
{
|
||||
Frag e2 = *operandStack.template Pop<Frag>(1);
|
||||
Frag e1 = *operandStack.template Pop<Frag>(1);
|
||||
Patch(e1.out, e2.start);
|
||||
*operandStack.template Push<Frag>() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex));
|
||||
}
|
||||
return true;
|
||||
|
||||
case kAlternation:
|
||||
if (operandStack.GetSize() >= sizeof(Frag) * 2) {
|
||||
Frag e2 = *operandStack.template Pop<Frag>(1);
|
||||
Frag e1 = *operandStack.template Pop<Frag>(1);
|
||||
SizeType s = NewState(e1.start, e2.start, 0);
|
||||
*operandStack.template Push<Frag>() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case kZeroOrOne:
|
||||
if (operandStack.GetSize() >= sizeof(Frag)) {
|
||||
Frag e = *operandStack.template Pop<Frag>(1);
|
||||
SizeType s = NewState(kRegexInvalidState, e.start, 0);
|
||||
*operandStack.template Push<Frag>() = Frag(s, Append(e.out, s), e.minIndex);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case kZeroOrMore:
|
||||
if (operandStack.GetSize() >= sizeof(Frag)) {
|
||||
Frag e = *operandStack.template Pop<Frag>(1);
|
||||
SizeType s = NewState(kRegexInvalidState, e.start, 0);
|
||||
Patch(e.out, s);
|
||||
*operandStack.template Push<Frag>() = Frag(s, s, e.minIndex);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case kOneOrMore:
|
||||
if (operandStack.GetSize() >= sizeof(Frag)) {
|
||||
Frag e = *operandStack.template Pop<Frag>(1);
|
||||
SizeType s = NewState(kRegexInvalidState, e.start, 0);
|
||||
Patch(e.out, s);
|
||||
*operandStack.template Push<Frag>() = Frag(e.start, s, e.minIndex);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
default:
|
||||
// syntax error (e.g. unclosed kLeftParenthesis)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool EvalQuantifier(Stack<Allocator>& operandStack, unsigned n, unsigned m) {
|
||||
RAPIDJSON_ASSERT(n <= m);
|
||||
RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag));
|
||||
|
||||
if (n == 0) {
|
||||
if (m == 0) // a{0} not support
|
||||
return false;
|
||||
else if (m == kInfinityQuantifier)
|
||||
Eval(operandStack, kZeroOrMore); // a{0,} -> a*
|
||||
else {
|
||||
Eval(operandStack, kZeroOrOne); // a{0,5} -> a?
|
||||
for (unsigned i = 0; i < m - 1; i++)
|
||||
CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a?
|
||||
for (unsigned i = 0; i < m - 1; i++)
|
||||
Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a?
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a
|
||||
CloneTopOperand(operandStack);
|
||||
|
||||
if (m == kInfinityQuantifier)
|
||||
Eval(operandStack, kOneOrMore); // a{3,} -> a a a+
|
||||
else if (m > n) {
|
||||
CloneTopOperand(operandStack); // a{3,5} -> a a a a
|
||||
Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a?
|
||||
for (unsigned i = n; i < m - 1; i++)
|
||||
CloneTopOperand(operandStack); // a{3,5} -> a a a a? a?
|
||||
for (unsigned i = n; i < m; i++)
|
||||
Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a?
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < n - 1; i++)
|
||||
Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a?
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; }
|
||||
|
||||
void CloneTopOperand(Stack<Allocator>& operandStack) {
|
||||
const Frag src = *operandStack.template Top<Frag>(); // Copy constructor to prevent invalidation
|
||||
SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_)
|
||||
State* s = states_.template Push<State>(count);
|
||||
memcpy(s, &GetState(src.minIndex), count * sizeof(State));
|
||||
for (SizeType j = 0; j < count; j++) {
|
||||
if (s[j].out != kRegexInvalidState)
|
||||
s[j].out += count;
|
||||
if (s[j].out1 != kRegexInvalidState)
|
||||
s[j].out1 += count;
|
||||
}
|
||||
*operandStack.template Push<Frag>() = Frag(src.start + count, src.out + count, src.minIndex + count);
|
||||
stateCount_ += count;
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
bool ParseUnsigned(DecodedStream<InputStream, Encoding>& ds, unsigned* u) {
|
||||
unsigned r = 0;
|
||||
if (ds.Peek() < '0' || ds.Peek() > '9')
|
||||
return false;
|
||||
while (ds.Peek() >= '0' && ds.Peek() <= '9') {
|
||||
if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295
|
||||
return false; // overflow
|
||||
r = r * 10 + (ds.Take() - '0');
|
||||
}
|
||||
*u = r;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
bool ParseRange(DecodedStream<InputStream, Encoding>& ds, SizeType* range) {
|
||||
bool isBegin = true;
|
||||
bool negate = false;
|
||||
int step = 0;
|
||||
SizeType start = kRegexInvalidRange;
|
||||
SizeType current = kRegexInvalidRange;
|
||||
unsigned codepoint;
|
||||
while ((codepoint = ds.Take()) != 0) {
|
||||
if (isBegin) {
|
||||
isBegin = false;
|
||||
if (codepoint == '^') {
|
||||
negate = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
switch (codepoint) {
|
||||
case ']':
|
||||
if (start == kRegexInvalidRange)
|
||||
return false; // Error: nothing inside []
|
||||
if (step == 2) { // Add trailing '-'
|
||||
SizeType r = NewRange('-');
|
||||
RAPIDJSON_ASSERT(current != kRegexInvalidRange);
|
||||
GetRange(current).next = r;
|
||||
}
|
||||
if (negate)
|
||||
GetRange(start).start |= kRangeNegationFlag;
|
||||
*range = start;
|
||||
return true;
|
||||
|
||||
case '\\':
|
||||
if (ds.Peek() == 'b') {
|
||||
ds.Take();
|
||||
codepoint = 0x0008; // Escape backspace character
|
||||
}
|
||||
else if (!CharacterEscape(ds, &codepoint))
|
||||
return false;
|
||||
// fall through to default
|
||||
RAPIDJSON_DELIBERATE_FALLTHROUGH;
|
||||
|
||||
default:
|
||||
switch (step) {
|
||||
case 1:
|
||||
if (codepoint == '-') {
|
||||
step++;
|
||||
break;
|
||||
}
|
||||
// fall through to step 0 for other characters
|
||||
RAPIDJSON_DELIBERATE_FALLTHROUGH;
|
||||
|
||||
case 0:
|
||||
{
|
||||
SizeType r = NewRange(codepoint);
|
||||
if (current != kRegexInvalidRange)
|
||||
GetRange(current).next = r;
|
||||
if (start == kRegexInvalidRange)
|
||||
start = r;
|
||||
current = r;
|
||||
}
|
||||
step = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
RAPIDJSON_ASSERT(step == 2);
|
||||
GetRange(current).end = codepoint;
|
||||
step = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SizeType NewRange(unsigned codepoint) {
|
||||
Range* r = ranges_.template Push<Range>();
|
||||
r->start = r->end = codepoint;
|
||||
r->next = kRegexInvalidRange;
|
||||
return rangeCount_++;
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
bool CharacterEscape(DecodedStream<InputStream, Encoding>& ds, unsigned* escapedCodepoint) {
|
||||
unsigned codepoint;
|
||||
switch (codepoint = ds.Take()) {
|
||||
case '^':
|
||||
case '$':
|
||||
case '|':
|
||||
case '(':
|
||||
case ')':
|
||||
case '?':
|
||||
case '*':
|
||||
case '+':
|
||||
case '.':
|
||||
case '[':
|
||||
case ']':
|
||||
case '{':
|
||||
case '}':
|
||||
case '\\':
|
||||
*escapedCodepoint = codepoint; return true;
|
||||
case 'f': *escapedCodepoint = 0x000C; return true;
|
||||
case 'n': *escapedCodepoint = 0x000A; return true;
|
||||
case 'r': *escapedCodepoint = 0x000D; return true;
|
||||
case 't': *escapedCodepoint = 0x0009; return true;
|
||||
case 'v': *escapedCodepoint = 0x000B; return true;
|
||||
default:
|
||||
return false; // Unsupported escape character
|
||||
}
|
||||
}
|
||||
|
||||
Allocator* ownAllocator_;
|
||||
Allocator* allocator_;
|
||||
Stack<Allocator> states_;
|
||||
Stack<Allocator> ranges_;
|
||||
SizeType root_;
|
||||
SizeType stateCount_;
|
||||
SizeType rangeCount_;
|
||||
|
||||
static const unsigned kInfinityQuantifier = ~0u;
|
||||
|
||||
// For SearchWithAnchoring()
|
||||
bool anchorBegin_;
|
||||
bool anchorEnd_;
|
||||
};
|
||||
|
||||
template <typename RegexType, typename Allocator = CrtAllocator>
|
||||
class GenericRegexSearch {
|
||||
public:
|
||||
typedef typename RegexType::EncodingType Encoding;
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
GenericRegexSearch(const RegexType& regex, Allocator* allocator = 0) :
|
||||
regex_(regex), allocator_(allocator), ownAllocator_(0),
|
||||
state0_(allocator, 0), state1_(allocator, 0), stateSet_()
|
||||
{
|
||||
RAPIDJSON_ASSERT(regex_.IsValid());
|
||||
if (!allocator_)
|
||||
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
|
||||
stateSet_ = static_cast<unsigned*>(allocator_->Malloc(GetStateSetSize()));
|
||||
state0_.template Reserve<SizeType>(regex_.stateCount_);
|
||||
state1_.template Reserve<SizeType>(regex_.stateCount_);
|
||||
}
|
||||
|
||||
~GenericRegexSearch() {
|
||||
Allocator::Free(stateSet_);
|
||||
RAPIDJSON_DELETE(ownAllocator_);
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
bool Match(InputStream& is) {
|
||||
return SearchWithAnchoring(is, true, true);
|
||||
}
|
||||
|
||||
bool Match(const Ch* s) {
|
||||
GenericStringStream<Encoding> is(s);
|
||||
return Match(is);
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
bool Search(InputStream& is) {
|
||||
return SearchWithAnchoring(is, regex_.anchorBegin_, regex_.anchorEnd_);
|
||||
}
|
||||
|
||||
bool Search(const Ch* s) {
|
||||
GenericStringStream<Encoding> is(s);
|
||||
return Search(is);
|
||||
}
|
||||
|
||||
private:
|
||||
typedef typename RegexType::State State;
|
||||
typedef typename RegexType::Range Range;
|
||||
|
||||
template <typename InputStream>
|
||||
bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) {
|
||||
DecodedStream<InputStream, Encoding> ds(is);
|
||||
|
||||
state0_.Clear();
|
||||
Stack<Allocator> *current = &state0_, *next = &state1_;
|
||||
const size_t stateSetSize = GetStateSetSize();
|
||||
std::memset(stateSet_, 0, stateSetSize);
|
||||
|
||||
bool matched = AddState(*current, regex_.root_);
|
||||
unsigned codepoint;
|
||||
while (!current->Empty() && (codepoint = ds.Take()) != 0) {
|
||||
std::memset(stateSet_, 0, stateSetSize);
|
||||
next->Clear();
|
||||
matched = false;
|
||||
for (const SizeType* s = current->template Bottom<SizeType>(); s != current->template End<SizeType>(); ++s) {
|
||||
const State& sr = regex_.GetState(*s);
|
||||
if (sr.codepoint == codepoint ||
|
||||
sr.codepoint == RegexType::kAnyCharacterClass ||
|
||||
(sr.codepoint == RegexType::kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint)))
|
||||
{
|
||||
matched = AddState(*next, sr.out) || matched;
|
||||
if (!anchorEnd && matched)
|
||||
return true;
|
||||
}
|
||||
if (!anchorBegin)
|
||||
AddState(*next, regex_.root_);
|
||||
}
|
||||
internal::Swap(current, next);
|
||||
}
|
||||
|
||||
return matched;
|
||||
}
|
||||
|
||||
size_t GetStateSetSize() const {
|
||||
return (regex_.stateCount_ + 31) / 32 * 4;
|
||||
}
|
||||
|
||||
// Return whether the added states is a match state
|
||||
bool AddState(Stack<Allocator>& l, SizeType index) {
|
||||
RAPIDJSON_ASSERT(index != kRegexInvalidState);
|
||||
|
||||
const State& s = regex_.GetState(index);
|
||||
if (s.out1 != kRegexInvalidState) { // Split
|
||||
bool matched = AddState(l, s.out);
|
||||
return AddState(l, s.out1) || matched;
|
||||
}
|
||||
else if (!(stateSet_[index >> 5] & (1u << (index & 31)))) {
|
||||
stateSet_[index >> 5] |= (1u << (index & 31));
|
||||
*l.template PushUnsafe<SizeType>() = index;
|
||||
}
|
||||
return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation.
|
||||
}
|
||||
|
||||
bool MatchRange(SizeType rangeIndex, unsigned codepoint) const {
|
||||
bool yes = (regex_.GetRange(rangeIndex).start & RegexType::kRangeNegationFlag) == 0;
|
||||
while (rangeIndex != kRegexInvalidRange) {
|
||||
const Range& r = regex_.GetRange(rangeIndex);
|
||||
if (codepoint >= (r.start & ~RegexType::kRangeNegationFlag) && codepoint <= r.end)
|
||||
return yes;
|
||||
rangeIndex = r.next;
|
||||
}
|
||||
return !yes;
|
||||
}
|
||||
|
||||
const RegexType& regex_;
|
||||
Allocator* allocator_;
|
||||
Allocator* ownAllocator_;
|
||||
Stack<Allocator> state0_;
|
||||
Stack<Allocator> state1_;
|
||||
uint32_t* stateSet_;
|
||||
};
|
||||
|
||||
typedef GenericRegex<UTF8<> > Regex;
|
||||
typedef GenericRegexSearch<Regex> RegexSearch;
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) || defined(_MSC_VER)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_INTERNAL_REGEX_H_
|
||||
232
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_internal_stack.h
generated
vendored
Normal file
@@ -0,0 +1,232 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_INTERNAL_STACK_H_
|
||||
#define RAPIDJSON_INTERNAL_STACK_H_
|
||||
|
||||
#include "lottie_rapidjson_allocators.h"
|
||||
#include "lottie_rapidjson_internal_swap.h"
|
||||
#include <cstddef>
|
||||
|
||||
#if defined(__clang__)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(c++98-compat)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Stack
|
||||
|
||||
//! A type-unsafe stack for storing different types of data.
|
||||
/*! \tparam Allocator Allocator for allocating stack memory.
|
||||
*/
|
||||
template <typename Allocator>
|
||||
class Stack {
|
||||
public:
|
||||
// Optimization note: Do not allocate memory for stack_ in constructor.
|
||||
// Do it lazily when first Push() -> Expand() -> Resize().
|
||||
Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) {
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
Stack(Stack&& rhs)
|
||||
: allocator_(rhs.allocator_),
|
||||
ownAllocator_(rhs.ownAllocator_),
|
||||
stack_(rhs.stack_),
|
||||
stackTop_(rhs.stackTop_),
|
||||
stackEnd_(rhs.stackEnd_),
|
||||
initialCapacity_(rhs.initialCapacity_)
|
||||
{
|
||||
rhs.allocator_ = 0;
|
||||
rhs.ownAllocator_ = 0;
|
||||
rhs.stack_ = 0;
|
||||
rhs.stackTop_ = 0;
|
||||
rhs.stackEnd_ = 0;
|
||||
rhs.initialCapacity_ = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
~Stack() {
|
||||
Destroy();
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
Stack& operator=(Stack&& rhs) {
|
||||
if (&rhs != this)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
allocator_ = rhs.allocator_;
|
||||
ownAllocator_ = rhs.ownAllocator_;
|
||||
stack_ = rhs.stack_;
|
||||
stackTop_ = rhs.stackTop_;
|
||||
stackEnd_ = rhs.stackEnd_;
|
||||
initialCapacity_ = rhs.initialCapacity_;
|
||||
|
||||
rhs.allocator_ = 0;
|
||||
rhs.ownAllocator_ = 0;
|
||||
rhs.stack_ = 0;
|
||||
rhs.stackTop_ = 0;
|
||||
rhs.stackEnd_ = 0;
|
||||
rhs.initialCapacity_ = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT {
|
||||
internal::Swap(allocator_, rhs.allocator_);
|
||||
internal::Swap(ownAllocator_, rhs.ownAllocator_);
|
||||
internal::Swap(stack_, rhs.stack_);
|
||||
internal::Swap(stackTop_, rhs.stackTop_);
|
||||
internal::Swap(stackEnd_, rhs.stackEnd_);
|
||||
internal::Swap(initialCapacity_, rhs.initialCapacity_);
|
||||
}
|
||||
|
||||
void Clear() { stackTop_ = stack_; }
|
||||
|
||||
void ShrinkToFit() {
|
||||
if (Empty()) {
|
||||
// If the stack is empty, completely deallocate the memory.
|
||||
Allocator::Free(stack_); // NOLINT (+clang-analyzer-unix.Malloc)
|
||||
stack_ = 0;
|
||||
stackTop_ = 0;
|
||||
stackEnd_ = 0;
|
||||
}
|
||||
else
|
||||
Resize(GetSize());
|
||||
}
|
||||
|
||||
// Optimization note: try to minimize the size of this function for force inline.
|
||||
// Expansion is run very infrequently, so it is moved to another (probably non-inline) function.
|
||||
template<typename T>
|
||||
RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) {
|
||||
// Expand the stack if needed
|
||||
if (RAPIDJSON_UNLIKELY(static_cast<std::ptrdiff_t>(sizeof(T) * count) > (stackEnd_ - stackTop_)))
|
||||
Expand<T>(count);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) {
|
||||
Reserve<T>(count);
|
||||
return PushUnsafe<T>(count);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) {
|
||||
RAPIDJSON_ASSERT(stackTop_);
|
||||
RAPIDJSON_ASSERT(static_cast<std::ptrdiff_t>(sizeof(T) * count) <= (stackEnd_ - stackTop_));
|
||||
T* ret = reinterpret_cast<T*>(stackTop_);
|
||||
stackTop_ += sizeof(T) * count;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* Pop(size_t count) {
|
||||
RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T));
|
||||
stackTop_ -= count * sizeof(T);
|
||||
return reinterpret_cast<T*>(stackTop_);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* Top() {
|
||||
RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
|
||||
return reinterpret_cast<T*>(stackTop_ - sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T* Top() const {
|
||||
RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
|
||||
return reinterpret_cast<T*>(stackTop_ - sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* End() { return reinterpret_cast<T*>(stackTop_); }
|
||||
|
||||
template<typename T>
|
||||
const T* End() const { return reinterpret_cast<T*>(stackTop_); }
|
||||
|
||||
template<typename T>
|
||||
T* Bottom() { return reinterpret_cast<T*>(stack_); }
|
||||
|
||||
template<typename T>
|
||||
const T* Bottom() const { return reinterpret_cast<T*>(stack_); }
|
||||
|
||||
bool HasAllocator() const {
|
||||
return allocator_ != 0;
|
||||
}
|
||||
|
||||
Allocator& GetAllocator() {
|
||||
RAPIDJSON_ASSERT(allocator_);
|
||||
return *allocator_;
|
||||
}
|
||||
|
||||
bool Empty() const { return stackTop_ == stack_; }
|
||||
size_t GetSize() const { return static_cast<size_t>(stackTop_ - stack_); }
|
||||
size_t GetCapacity() const { return static_cast<size_t>(stackEnd_ - stack_); }
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
void Expand(size_t count) {
|
||||
// Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity.
|
||||
size_t newCapacity;
|
||||
if (stack_ == 0) {
|
||||
if (!allocator_)
|
||||
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
|
||||
newCapacity = initialCapacity_;
|
||||
} else {
|
||||
newCapacity = GetCapacity();
|
||||
newCapacity += (newCapacity + 1) / 2;
|
||||
}
|
||||
size_t newSize = GetSize() + sizeof(T) * count;
|
||||
if (newCapacity < newSize)
|
||||
newCapacity = newSize;
|
||||
|
||||
Resize(newCapacity);
|
||||
}
|
||||
|
||||
void Resize(size_t newCapacity) {
|
||||
const size_t size = GetSize(); // Backup the current size
|
||||
stack_ = static_cast<char*>(allocator_->Realloc(stack_, GetCapacity(), newCapacity));
|
||||
stackTop_ = stack_ + size;
|
||||
stackEnd_ = stack_ + newCapacity;
|
||||
}
|
||||
|
||||
void Destroy() {
|
||||
Allocator::Free(stack_);
|
||||
RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack
|
||||
}
|
||||
|
||||
// Prohibit copy constructor & assignment operator.
|
||||
Stack(const Stack&);
|
||||
Stack& operator=(const Stack&);
|
||||
|
||||
Allocator* allocator_;
|
||||
Allocator* ownAllocator_;
|
||||
char *stack_;
|
||||
char *stackTop_;
|
||||
char *stackEnd_;
|
||||
size_t initialCapacity_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#if defined(__clang__)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_STACK_H_
|
||||
69
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_internal_strfunc.h
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_
|
||||
#define RAPIDJSON_INTERNAL_STRFUNC_H_
|
||||
|
||||
#include "lottie_rapidjson_stream.h"
|
||||
#include <cwchar>
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
//! Custom strlen() which works on different character types.
|
||||
/*! \tparam Ch Character type (e.g. char, wchar_t, short)
|
||||
\param s Null-terminated input string.
|
||||
\return Number of characters in the string.
|
||||
\note This has the same semantics as strlen(), the return value is not number of Unicode codepoints.
|
||||
*/
|
||||
template <typename Ch>
|
||||
inline SizeType StrLen(const Ch* s) {
|
||||
RAPIDJSON_ASSERT(s != 0);
|
||||
const Ch* p = s;
|
||||
while (*p) ++p;
|
||||
return SizeType(p - s);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline SizeType StrLen(const char* s) {
|
||||
return SizeType(std::strlen(s));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline SizeType StrLen(const wchar_t* s) {
|
||||
return SizeType(std::wcslen(s));
|
||||
}
|
||||
|
||||
//! Returns number of code points in a encoded string.
|
||||
template<typename Encoding>
|
||||
bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) {
|
||||
RAPIDJSON_ASSERT(s != 0);
|
||||
RAPIDJSON_ASSERT(outCount != 0);
|
||||
GenericStringStream<Encoding> is(s);
|
||||
const typename Encoding::Ch* end = s + length;
|
||||
SizeType count = 0;
|
||||
while (is.src_ < end) {
|
||||
unsigned codepoint;
|
||||
if (!Encoding::Decode(is, &codepoint))
|
||||
return false;
|
||||
count++;
|
||||
}
|
||||
*outCount = count;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_INTERNAL_STRFUNC_H_
|
||||
290
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_internal_strtod.h
generated
vendored
Normal file
@@ -0,0 +1,290 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_STRTOD_
|
||||
#define RAPIDJSON_STRTOD_
|
||||
|
||||
#include "lottie_rapidjson_internal_ieee754.h"
|
||||
#include "lottie_rapidjson_internal_biginteger.h"
|
||||
#include "lottie_rapidjson_internal_diyfp.h"
|
||||
#include "lottie_rapidjson_internal_pow10.h"
|
||||
#include <climits>
|
||||
#include <limits>
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
inline double FastPath(double significand, int exp) {
|
||||
if (exp < -308)
|
||||
return 0.0;
|
||||
else if (exp >= 0)
|
||||
return significand * internal::Pow10(exp);
|
||||
else
|
||||
return significand / internal::Pow10(-exp);
|
||||
}
|
||||
|
||||
inline double StrtodNormalPrecision(double d, int p) {
|
||||
if (p < -308) {
|
||||
// Prevent expSum < -308, making Pow10(p) = 0
|
||||
d = FastPath(d, -308);
|
||||
d = FastPath(d, p + 308);
|
||||
}
|
||||
else
|
||||
d = FastPath(d, p);
|
||||
return d;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T Min3(T a, T b, T c) {
|
||||
T m = a;
|
||||
if (m > b) m = b;
|
||||
if (m > c) m = c;
|
||||
return m;
|
||||
}
|
||||
|
||||
inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) {
|
||||
const Double db(b);
|
||||
const uint64_t bInt = db.IntegerSignificand();
|
||||
const int bExp = db.IntegerExponent();
|
||||
const int hExp = bExp - 1;
|
||||
|
||||
int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0;
|
||||
|
||||
// Adjust for decimal exponent
|
||||
if (dExp >= 0) {
|
||||
dS_Exp2 += dExp;
|
||||
dS_Exp5 += dExp;
|
||||
}
|
||||
else {
|
||||
bS_Exp2 -= dExp;
|
||||
bS_Exp5 -= dExp;
|
||||
hS_Exp2 -= dExp;
|
||||
hS_Exp5 -= dExp;
|
||||
}
|
||||
|
||||
// Adjust for binary exponent
|
||||
if (bExp >= 0)
|
||||
bS_Exp2 += bExp;
|
||||
else {
|
||||
dS_Exp2 -= bExp;
|
||||
hS_Exp2 -= bExp;
|
||||
}
|
||||
|
||||
// Adjust for half ulp exponent
|
||||
if (hExp >= 0)
|
||||
hS_Exp2 += hExp;
|
||||
else {
|
||||
dS_Exp2 -= hExp;
|
||||
bS_Exp2 -= hExp;
|
||||
}
|
||||
|
||||
// Remove common power of two factor from all three scaled values
|
||||
int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2);
|
||||
dS_Exp2 -= common_Exp2;
|
||||
bS_Exp2 -= common_Exp2;
|
||||
hS_Exp2 -= common_Exp2;
|
||||
|
||||
BigInteger dS = d;
|
||||
dS.MultiplyPow5(static_cast<unsigned>(dS_Exp5)) <<= static_cast<unsigned>(dS_Exp2);
|
||||
|
||||
BigInteger bS(bInt);
|
||||
bS.MultiplyPow5(static_cast<unsigned>(bS_Exp5)) <<= static_cast<unsigned>(bS_Exp2);
|
||||
|
||||
BigInteger hS(1);
|
||||
hS.MultiplyPow5(static_cast<unsigned>(hS_Exp5)) <<= static_cast<unsigned>(hS_Exp2);
|
||||
|
||||
BigInteger delta(0);
|
||||
dS.Difference(bS, &delta);
|
||||
|
||||
return delta.Compare(hS);
|
||||
}
|
||||
|
||||
inline bool StrtodFast(double d, int p, double* result) {
|
||||
// Use fast path for string-to-double conversion if possible
|
||||
// see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
|
||||
if (p > 22 && p < 22 + 16) {
|
||||
// Fast Path Cases In Disguise
|
||||
d *= internal::Pow10(p - 22);
|
||||
p = 22;
|
||||
}
|
||||
|
||||
if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1
|
||||
*result = FastPath(d, p);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compute an approximation and see if it is within 1/2 ULP
|
||||
inline bool StrtodDiyFp(const char* decimals, int dLen, int dExp, double* result) {
|
||||
uint64_t significand = 0;
|
||||
int i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999
|
||||
for (; i < dLen; i++) {
|
||||
if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) ||
|
||||
(significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5'))
|
||||
break;
|
||||
significand = significand * 10u + static_cast<unsigned>(decimals[i] - '0');
|
||||
}
|
||||
|
||||
if (i < dLen && decimals[i] >= '5') // Rounding
|
||||
significand++;
|
||||
|
||||
int remaining = dLen - i;
|
||||
const int kUlpShift = 3;
|
||||
const int kUlp = 1 << kUlpShift;
|
||||
int64_t error = (remaining == 0) ? 0 : kUlp / 2;
|
||||
|
||||
DiyFp v(significand, 0);
|
||||
v = v.Normalize();
|
||||
error <<= -v.e;
|
||||
|
||||
dExp += remaining;
|
||||
|
||||
int actualExp;
|
||||
DiyFp cachedPower = GetCachedPower10(dExp, &actualExp);
|
||||
if (actualExp != dExp) {
|
||||
static const DiyFp kPow10[] = {
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 0x00000000), -60), // 10^1
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 0x00000000), -57), // 10^2
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 0x00000000), -54), // 10^3
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), -50), // 10^4
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 0x00000000), -47), // 10^5
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 0x00000000), -44), // 10^6
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 0x00000000), -40) // 10^7
|
||||
};
|
||||
int adjustment = dExp - actualExp;
|
||||
RAPIDJSON_ASSERT(adjustment >= 1 && adjustment < 8);
|
||||
v = v * kPow10[adjustment - 1];
|
||||
if (dLen + adjustment > 19) // has more digits than decimal digits in 64-bit
|
||||
error += kUlp / 2;
|
||||
}
|
||||
|
||||
v = v * cachedPower;
|
||||
|
||||
error += kUlp + (error == 0 ? 0 : 1);
|
||||
|
||||
const int oldExp = v.e;
|
||||
v = v.Normalize();
|
||||
error <<= oldExp - v.e;
|
||||
|
||||
const int effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e);
|
||||
int precisionSize = 64 - effectiveSignificandSize;
|
||||
if (precisionSize + kUlpShift >= 64) {
|
||||
int scaleExp = (precisionSize + kUlpShift) - 63;
|
||||
v.f >>= scaleExp;
|
||||
v.e += scaleExp;
|
||||
error = (error >> scaleExp) + 1 + kUlp;
|
||||
precisionSize -= scaleExp;
|
||||
}
|
||||
|
||||
DiyFp rounded(v.f >> precisionSize, v.e + precisionSize);
|
||||
const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp;
|
||||
const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp;
|
||||
if (precisionBits >= halfWay + static_cast<unsigned>(error)) {
|
||||
rounded.f++;
|
||||
if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340)
|
||||
rounded.f >>= 1;
|
||||
rounded.e++;
|
||||
}
|
||||
}
|
||||
|
||||
*result = rounded.ToDouble();
|
||||
|
||||
return halfWay - static_cast<unsigned>(error) >= precisionBits || precisionBits >= halfWay + static_cast<unsigned>(error);
|
||||
}
|
||||
|
||||
inline double StrtodBigInteger(double approx, const char* decimals, int dLen, int dExp) {
|
||||
RAPIDJSON_ASSERT(dLen >= 0);
|
||||
const BigInteger dInt(decimals, static_cast<unsigned>(dLen));
|
||||
Double a(approx);
|
||||
int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp);
|
||||
if (cmp < 0)
|
||||
return a.Value(); // within half ULP
|
||||
else if (cmp == 0) {
|
||||
// Round towards even
|
||||
if (a.Significand() & 1)
|
||||
return a.NextPositiveDouble();
|
||||
else
|
||||
return a.Value();
|
||||
}
|
||||
else // adjustment
|
||||
return a.NextPositiveDouble();
|
||||
}
|
||||
|
||||
inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) {
|
||||
RAPIDJSON_ASSERT(d >= 0.0);
|
||||
RAPIDJSON_ASSERT(length >= 1);
|
||||
|
||||
double result = 0.0;
|
||||
if (StrtodFast(d, p, &result))
|
||||
return result;
|
||||
|
||||
RAPIDJSON_ASSERT(length <= INT_MAX);
|
||||
int dLen = static_cast<int>(length);
|
||||
|
||||
RAPIDJSON_ASSERT(length >= decimalPosition);
|
||||
RAPIDJSON_ASSERT(length - decimalPosition <= INT_MAX);
|
||||
int dExpAdjust = static_cast<int>(length - decimalPosition);
|
||||
|
||||
RAPIDJSON_ASSERT(exp >= INT_MIN + dExpAdjust);
|
||||
int dExp = exp - dExpAdjust;
|
||||
|
||||
// Make sure length+dExp does not overflow
|
||||
RAPIDJSON_ASSERT(dExp <= INT_MAX - dLen);
|
||||
|
||||
// Trim leading zeros
|
||||
while (dLen > 0 && *decimals == '0') {
|
||||
dLen--;
|
||||
decimals++;
|
||||
}
|
||||
|
||||
// Trim trailing zeros
|
||||
while (dLen > 0 && decimals[dLen - 1] == '0') {
|
||||
dLen--;
|
||||
dExp++;
|
||||
}
|
||||
|
||||
if (dLen == 0) { // Buffer only contains zeros.
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Trim right-most digits
|
||||
const int kMaxDecimalDigit = 767 + 1;
|
||||
if (dLen > kMaxDecimalDigit) {
|
||||
dExp += dLen - kMaxDecimalDigit;
|
||||
dLen = kMaxDecimalDigit;
|
||||
}
|
||||
|
||||
// If too small, underflow to zero.
|
||||
// Any x <= 10^-324 is interpreted as zero.
|
||||
if (dLen + dExp <= -324)
|
||||
return 0.0;
|
||||
|
||||
// If too large, overflow to infinity.
|
||||
// Any x >= 10^309 is interpreted as +infinity.
|
||||
if (dLen + dExp > 309)
|
||||
return std::numeric_limits<double>::infinity();
|
||||
|
||||
if (StrtodDiyFp(decimals, dLen, dExp, &result))
|
||||
return result;
|
||||
|
||||
// Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison
|
||||
return StrtodBigInteger(result, decimals, dLen, dExp);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_STRTOD_
|
||||
46
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_internal_swap.h
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_INTERNAL_SWAP_H_
|
||||
#define RAPIDJSON_INTERNAL_SWAP_H_
|
||||
|
||||
#include "lottie_rapidjson_rapidjson.h"
|
||||
|
||||
#if defined(__clang__)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(c++98-compat)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
//! Custom swap() to avoid dependency on C++ <algorithm> header
|
||||
/*! \tparam T Type of the arguments to swap, should be instantiated with primitive C++ types only.
|
||||
\note This has the same semantics as std::swap().
|
||||
*/
|
||||
template <typename T>
|
||||
inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT {
|
||||
T tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#if defined(__clang__)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_INTERNAL_SWAP_H_
|
||||
128
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_istreamwrapper.h
generated
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ISTREAMWRAPPER_H_
|
||||
#define RAPIDJSON_ISTREAMWRAPPER_H_
|
||||
|
||||
#include "lottie_rapidjson_stream.h"
|
||||
#include <iosfwd>
|
||||
#include <ios>
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(padded)
|
||||
#elif defined(_MSC_VER)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept.
|
||||
/*!
|
||||
The classes can be wrapped including but not limited to:
|
||||
|
||||
- \c std::istringstream
|
||||
- \c std::stringstream
|
||||
- \c std::wistringstream
|
||||
- \c std::wstringstream
|
||||
- \c std::ifstream
|
||||
- \c std::fstream
|
||||
- \c std::wifstream
|
||||
- \c std::wfstream
|
||||
|
||||
\tparam StreamType Class derived from \c std::basic_istream.
|
||||
*/
|
||||
|
||||
template <typename StreamType>
|
||||
class BasicIStreamWrapper {
|
||||
public:
|
||||
typedef typename StreamType::char_type Ch;
|
||||
|
||||
//! Constructor.
|
||||
/*!
|
||||
\param stream stream opened for read.
|
||||
*/
|
||||
BasicIStreamWrapper(StreamType &stream) : stream_(stream), buffer_(peekBuffer_), bufferSize_(4), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) {
|
||||
Read();
|
||||
}
|
||||
|
||||
//! Constructor.
|
||||
/*!
|
||||
\param stream stream opened for read.
|
||||
\param buffer user-supplied buffer.
|
||||
\param bufferSize size of buffer in bytes. Must >=4 bytes.
|
||||
*/
|
||||
BasicIStreamWrapper(StreamType &stream, char* buffer, size_t bufferSize) : stream_(stream), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) {
|
||||
RAPIDJSON_ASSERT(bufferSize >= 4);
|
||||
Read();
|
||||
}
|
||||
|
||||
Ch Peek() const { return *current_; }
|
||||
Ch Take() { Ch c = *current_; Read(); return c; }
|
||||
size_t Tell() const { return count_ + static_cast<size_t>(current_ - buffer_); }
|
||||
|
||||
// Not implemented
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
// For encoding detection only.
|
||||
const Ch* Peek4() const {
|
||||
return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0;
|
||||
}
|
||||
|
||||
private:
|
||||
BasicIStreamWrapper();
|
||||
BasicIStreamWrapper(const BasicIStreamWrapper&);
|
||||
BasicIStreamWrapper& operator=(const BasicIStreamWrapper&);
|
||||
|
||||
void Read() {
|
||||
if (current_ < bufferLast_)
|
||||
++current_;
|
||||
else if (!eof_) {
|
||||
count_ += readCount_;
|
||||
readCount_ = bufferSize_;
|
||||
bufferLast_ = buffer_ + readCount_ - 1;
|
||||
current_ = buffer_;
|
||||
|
||||
if (!stream_.read(buffer_, static_cast<std::streamsize>(bufferSize_))) {
|
||||
readCount_ = static_cast<size_t>(stream_.gcount());
|
||||
*(bufferLast_ = buffer_ + readCount_) = '\0';
|
||||
eof_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StreamType &stream_;
|
||||
Ch peekBuffer_[4], *buffer_;
|
||||
size_t bufferSize_;
|
||||
Ch *bufferLast_;
|
||||
Ch *current_;
|
||||
size_t readCount_;
|
||||
size_t count_; //!< Number of characters read
|
||||
bool eof_;
|
||||
};
|
||||
|
||||
typedef BasicIStreamWrapper<std::istream> IStreamWrapper;
|
||||
typedef BasicIStreamWrapper<std::wistream> WIStreamWrapper;
|
||||
|
||||
#if defined(__clang__) || defined(_MSC_VER)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_ISTREAMWRAPPER_H_
|
||||
70
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_memorybuffer.h
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_MEMORYBUFFER_H_
|
||||
#define RAPIDJSON_MEMORYBUFFER_H_
|
||||
|
||||
#include "lottie_rapidjson_stream.h"
|
||||
#include "lottie_rapidjson_internal_stack.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Represents an in-memory output byte stream.
|
||||
/*!
|
||||
This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream.
|
||||
|
||||
It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file.
|
||||
|
||||
Differences between MemoryBuffer and StringBuffer:
|
||||
1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer.
|
||||
2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator.
|
||||
|
||||
\tparam Allocator type for allocating memory buffer.
|
||||
\note implements Stream concept
|
||||
*/
|
||||
template <typename Allocator = CrtAllocator>
|
||||
struct GenericMemoryBuffer {
|
||||
typedef char Ch; // byte
|
||||
|
||||
GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {}
|
||||
|
||||
void Put(Ch c) { *stack_.template Push<Ch>() = c; }
|
||||
void Flush() {}
|
||||
|
||||
void Clear() { stack_.Clear(); }
|
||||
void ShrinkToFit() { stack_.ShrinkToFit(); }
|
||||
Ch* Push(size_t count) { return stack_.template Push<Ch>(count); }
|
||||
void Pop(size_t count) { stack_.template Pop<Ch>(count); }
|
||||
|
||||
const Ch* GetBuffer() const {
|
||||
return stack_.template Bottom<Ch>();
|
||||
}
|
||||
|
||||
size_t GetSize() const { return stack_.GetSize(); }
|
||||
|
||||
static const size_t kDefaultCapacity = 256;
|
||||
mutable internal::Stack<Allocator> stack_;
|
||||
};
|
||||
|
||||
typedef GenericMemoryBuffer<> MemoryBuffer;
|
||||
|
||||
//! Implement specialized version of PutN() with memset() for better performance.
|
||||
template<>
|
||||
inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) {
|
||||
std::memset(memoryBuffer.stack_.Push<char>(n), c, n * sizeof(c));
|
||||
}
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_MEMORYBUFFER_H_
|
||||
71
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_memorystream.h
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_MEMORYSTREAM_H_
|
||||
#define RAPIDJSON_MEMORYSTREAM_H_
|
||||
|
||||
#include "lottie_rapidjson_stream.h"
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(unreachable-code)
|
||||
RAPIDJSON_DIAG_OFF(missing-noreturn)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Represents an in-memory input byte stream.
|
||||
/*!
|
||||
This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream.
|
||||
|
||||
It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file.
|
||||
|
||||
Differences between MemoryStream and StringStream:
|
||||
1. StringStream has encoding but MemoryStream is a byte stream.
|
||||
2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source.
|
||||
3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4().
|
||||
\note implements Stream concept
|
||||
*/
|
||||
struct MemoryStream {
|
||||
typedef char Ch; // byte
|
||||
|
||||
MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {}
|
||||
|
||||
Ch Peek() const { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; }
|
||||
Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; }
|
||||
size_t Tell() const { return static_cast<size_t>(src_ - begin_); }
|
||||
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
// For encoding detection only.
|
||||
const Ch* Peek4() const {
|
||||
return Tell() + 4 <= size_ ? src_ : 0;
|
||||
}
|
||||
|
||||
const Ch* src_; //!< Current read position.
|
||||
const Ch* begin_; //!< Original head of the string.
|
||||
const Ch* end_; //!< End of stream.
|
||||
size_t size_; //!< Size of the stream.
|
||||
};
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_MEMORYBUFFER_H_
|
||||
316
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_msinttypes_inttypes.h
generated
vendored
Normal file
@@ -0,0 +1,316 @@
|
||||
// ISO C9x compliant inttypes.h for Microsoft Visual Studio
|
||||
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
|
||||
//
|
||||
// Copyright (c) 2006-2013 Alexander Chemeris
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the product nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// The above software in this distribution may have been modified by
|
||||
// THL A29 Limited ("Tencent Modifications").
|
||||
// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited.
|
||||
|
||||
#ifndef _MSC_VER // [
|
||||
#error "Use this header only with Microsoft Visual C++ compilers!"
|
||||
#endif // _MSC_VER ]
|
||||
|
||||
#ifndef _MSC_INTTYPES_H_ // [
|
||||
#define _MSC_INTTYPES_H_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "lottie_rapidjson_msinttypes_stdint.h"
|
||||
|
||||
// miloyip: VC supports inttypes.h since VC2013
|
||||
#if _MSC_VER >= 1800
|
||||
#include <inttypes.h>
|
||||
#else
|
||||
|
||||
// 7.8 Format conversion of integer types
|
||||
|
||||
typedef struct {
|
||||
intmax_t quot;
|
||||
intmax_t rem;
|
||||
} imaxdiv_t;
|
||||
|
||||
// 7.8.1 Macros for format specifiers
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198
|
||||
|
||||
// The fprintf macros for signed integers are:
|
||||
#define PRId8 "d"
|
||||
#define PRIi8 "i"
|
||||
#define PRIdLEAST8 "d"
|
||||
#define PRIiLEAST8 "i"
|
||||
#define PRIdFAST8 "d"
|
||||
#define PRIiFAST8 "i"
|
||||
|
||||
#define PRId16 "hd"
|
||||
#define PRIi16 "hi"
|
||||
#define PRIdLEAST16 "hd"
|
||||
#define PRIiLEAST16 "hi"
|
||||
#define PRIdFAST16 "hd"
|
||||
#define PRIiFAST16 "hi"
|
||||
|
||||
#define PRId32 "I32d"
|
||||
#define PRIi32 "I32i"
|
||||
#define PRIdLEAST32 "I32d"
|
||||
#define PRIiLEAST32 "I32i"
|
||||
#define PRIdFAST32 "I32d"
|
||||
#define PRIiFAST32 "I32i"
|
||||
|
||||
#define PRId64 "I64d"
|
||||
#define PRIi64 "I64i"
|
||||
#define PRIdLEAST64 "I64d"
|
||||
#define PRIiLEAST64 "I64i"
|
||||
#define PRIdFAST64 "I64d"
|
||||
#define PRIiFAST64 "I64i"
|
||||
|
||||
#define PRIdMAX "I64d"
|
||||
#define PRIiMAX "I64i"
|
||||
|
||||
#define PRIdPTR "Id"
|
||||
#define PRIiPTR "Ii"
|
||||
|
||||
// The fprintf macros for unsigned integers are:
|
||||
#define PRIo8 "o"
|
||||
#define PRIu8 "u"
|
||||
#define PRIx8 "x"
|
||||
#define PRIX8 "X"
|
||||
#define PRIoLEAST8 "o"
|
||||
#define PRIuLEAST8 "u"
|
||||
#define PRIxLEAST8 "x"
|
||||
#define PRIXLEAST8 "X"
|
||||
#define PRIoFAST8 "o"
|
||||
#define PRIuFAST8 "u"
|
||||
#define PRIxFAST8 "x"
|
||||
#define PRIXFAST8 "X"
|
||||
|
||||
#define PRIo16 "ho"
|
||||
#define PRIu16 "hu"
|
||||
#define PRIx16 "hx"
|
||||
#define PRIX16 "hX"
|
||||
#define PRIoLEAST16 "ho"
|
||||
#define PRIuLEAST16 "hu"
|
||||
#define PRIxLEAST16 "hx"
|
||||
#define PRIXLEAST16 "hX"
|
||||
#define PRIoFAST16 "ho"
|
||||
#define PRIuFAST16 "hu"
|
||||
#define PRIxFAST16 "hx"
|
||||
#define PRIXFAST16 "hX"
|
||||
|
||||
#define PRIo32 "I32o"
|
||||
#define PRIu32 "I32u"
|
||||
#define PRIx32 "I32x"
|
||||
#define PRIX32 "I32X"
|
||||
#define PRIoLEAST32 "I32o"
|
||||
#define PRIuLEAST32 "I32u"
|
||||
#define PRIxLEAST32 "I32x"
|
||||
#define PRIXLEAST32 "I32X"
|
||||
#define PRIoFAST32 "I32o"
|
||||
#define PRIuFAST32 "I32u"
|
||||
#define PRIxFAST32 "I32x"
|
||||
#define PRIXFAST32 "I32X"
|
||||
|
||||
#define PRIo64 "I64o"
|
||||
#define PRIu64 "I64u"
|
||||
#define PRIx64 "I64x"
|
||||
#define PRIX64 "I64X"
|
||||
#define PRIoLEAST64 "I64o"
|
||||
#define PRIuLEAST64 "I64u"
|
||||
#define PRIxLEAST64 "I64x"
|
||||
#define PRIXLEAST64 "I64X"
|
||||
#define PRIoFAST64 "I64o"
|
||||
#define PRIuFAST64 "I64u"
|
||||
#define PRIxFAST64 "I64x"
|
||||
#define PRIXFAST64 "I64X"
|
||||
|
||||
#define PRIoMAX "I64o"
|
||||
#define PRIuMAX "I64u"
|
||||
#define PRIxMAX "I64x"
|
||||
#define PRIXMAX "I64X"
|
||||
|
||||
#define PRIoPTR "Io"
|
||||
#define PRIuPTR "Iu"
|
||||
#define PRIxPTR "Ix"
|
||||
#define PRIXPTR "IX"
|
||||
|
||||
// The fscanf macros for signed integers are:
|
||||
#define SCNd8 "d"
|
||||
#define SCNi8 "i"
|
||||
#define SCNdLEAST8 "d"
|
||||
#define SCNiLEAST8 "i"
|
||||
#define SCNdFAST8 "d"
|
||||
#define SCNiFAST8 "i"
|
||||
|
||||
#define SCNd16 "hd"
|
||||
#define SCNi16 "hi"
|
||||
#define SCNdLEAST16 "hd"
|
||||
#define SCNiLEAST16 "hi"
|
||||
#define SCNdFAST16 "hd"
|
||||
#define SCNiFAST16 "hi"
|
||||
|
||||
#define SCNd32 "ld"
|
||||
#define SCNi32 "li"
|
||||
#define SCNdLEAST32 "ld"
|
||||
#define SCNiLEAST32 "li"
|
||||
#define SCNdFAST32 "ld"
|
||||
#define SCNiFAST32 "li"
|
||||
|
||||
#define SCNd64 "I64d"
|
||||
#define SCNi64 "I64i"
|
||||
#define SCNdLEAST64 "I64d"
|
||||
#define SCNiLEAST64 "I64i"
|
||||
#define SCNdFAST64 "I64d"
|
||||
#define SCNiFAST64 "I64i"
|
||||
|
||||
#define SCNdMAX "I64d"
|
||||
#define SCNiMAX "I64i"
|
||||
|
||||
#ifdef _WIN64 // [
|
||||
# define SCNdPTR "I64d"
|
||||
# define SCNiPTR "I64i"
|
||||
#else // _WIN64 ][
|
||||
# define SCNdPTR "ld"
|
||||
# define SCNiPTR "li"
|
||||
#endif // _WIN64 ]
|
||||
|
||||
// The fscanf macros for unsigned integers are:
|
||||
#define SCNo8 "o"
|
||||
#define SCNu8 "u"
|
||||
#define SCNx8 "x"
|
||||
#define SCNX8 "X"
|
||||
#define SCNoLEAST8 "o"
|
||||
#define SCNuLEAST8 "u"
|
||||
#define SCNxLEAST8 "x"
|
||||
#define SCNXLEAST8 "X"
|
||||
#define SCNoFAST8 "o"
|
||||
#define SCNuFAST8 "u"
|
||||
#define SCNxFAST8 "x"
|
||||
#define SCNXFAST8 "X"
|
||||
|
||||
#define SCNo16 "ho"
|
||||
#define SCNu16 "hu"
|
||||
#define SCNx16 "hx"
|
||||
#define SCNX16 "hX"
|
||||
#define SCNoLEAST16 "ho"
|
||||
#define SCNuLEAST16 "hu"
|
||||
#define SCNxLEAST16 "hx"
|
||||
#define SCNXLEAST16 "hX"
|
||||
#define SCNoFAST16 "ho"
|
||||
#define SCNuFAST16 "hu"
|
||||
#define SCNxFAST16 "hx"
|
||||
#define SCNXFAST16 "hX"
|
||||
|
||||
#define SCNo32 "lo"
|
||||
#define SCNu32 "lu"
|
||||
#define SCNx32 "lx"
|
||||
#define SCNX32 "lX"
|
||||
#define SCNoLEAST32 "lo"
|
||||
#define SCNuLEAST32 "lu"
|
||||
#define SCNxLEAST32 "lx"
|
||||
#define SCNXLEAST32 "lX"
|
||||
#define SCNoFAST32 "lo"
|
||||
#define SCNuFAST32 "lu"
|
||||
#define SCNxFAST32 "lx"
|
||||
#define SCNXFAST32 "lX"
|
||||
|
||||
#define SCNo64 "I64o"
|
||||
#define SCNu64 "I64u"
|
||||
#define SCNx64 "I64x"
|
||||
#define SCNX64 "I64X"
|
||||
#define SCNoLEAST64 "I64o"
|
||||
#define SCNuLEAST64 "I64u"
|
||||
#define SCNxLEAST64 "I64x"
|
||||
#define SCNXLEAST64 "I64X"
|
||||
#define SCNoFAST64 "I64o"
|
||||
#define SCNuFAST64 "I64u"
|
||||
#define SCNxFAST64 "I64x"
|
||||
#define SCNXFAST64 "I64X"
|
||||
|
||||
#define SCNoMAX "I64o"
|
||||
#define SCNuMAX "I64u"
|
||||
#define SCNxMAX "I64x"
|
||||
#define SCNXMAX "I64X"
|
||||
|
||||
#ifdef _WIN64 // [
|
||||
# define SCNoPTR "I64o"
|
||||
# define SCNuPTR "I64u"
|
||||
# define SCNxPTR "I64x"
|
||||
# define SCNXPTR "I64X"
|
||||
#else // _WIN64 ][
|
||||
# define SCNoPTR "lo"
|
||||
# define SCNuPTR "lu"
|
||||
# define SCNxPTR "lx"
|
||||
# define SCNXPTR "lX"
|
||||
#endif // _WIN64 ]
|
||||
|
||||
#endif // __STDC_FORMAT_MACROS ]
|
||||
|
||||
// 7.8.2 Functions for greatest-width integer types
|
||||
|
||||
// 7.8.2.1 The imaxabs function
|
||||
#define imaxabs _abs64
|
||||
|
||||
// 7.8.2.2 The imaxdiv function
|
||||
|
||||
// This is modified version of div() function from Microsoft's div.c found
|
||||
// in %MSVC.NET%\crt\src\div.c
|
||||
#ifdef STATIC_IMAXDIV // [
|
||||
static
|
||||
#else // STATIC_IMAXDIV ][
|
||||
_inline
|
||||
#endif // STATIC_IMAXDIV ]
|
||||
imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom)
|
||||
{
|
||||
imaxdiv_t result;
|
||||
|
||||
result.quot = numer / denom;
|
||||
result.rem = numer % denom;
|
||||
|
||||
if (numer < 0 && result.rem > 0) {
|
||||
// did division wrong; must fix up
|
||||
++result.quot;
|
||||
result.rem -= denom;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 7.8.2.3 The strtoimax and strtoumax functions
|
||||
#define strtoimax _strtoi64
|
||||
#define strtoumax _strtoui64
|
||||
|
||||
// 7.8.2.4 The wcstoimax and wcstoumax functions
|
||||
#define wcstoimax _wcstoi64
|
||||
#define wcstoumax _wcstoui64
|
||||
|
||||
#endif // _MSC_VER >= 1800
|
||||
|
||||
#endif // _MSC_INTTYPES_H_ ]
|
||||
300
vendor/github.com/Benau/go_rlottie/lottie_rapidjson_msinttypes_stdint.h
generated
vendored
Normal file
@@ -0,0 +1,300 @@
|
||||
// ISO C9x compliant stdint.h for Microsoft Visual Studio
|
||||
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
|
||||
//
|
||||
// Copyright (c) 2006-2013 Alexander Chemeris
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the product nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// The above software in this distribution may have been modified by
|
||||
// THL A29 Limited ("Tencent Modifications").
|
||||
// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited.
|
||||
|
||||
#ifndef _MSC_VER // [
|
||||
#error "Use this header only with Microsoft Visual C++ compilers!"
|
||||
#endif // _MSC_VER ]
|
||||
|
||||
#ifndef _MSC_STDINT_H_ // [
|
||||
#define _MSC_STDINT_H_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010.
|
||||
#if _MSC_VER >= 1600 // [
|
||||
#include <stdint.h>
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
|
||||
|
||||
#undef INT8_C
|
||||
#undef INT16_C
|
||||
#undef INT32_C
|
||||
#undef INT64_C
|
||||
#undef UINT8_C
|
||||
#undef UINT16_C
|
||||
#undef UINT32_C
|
||||
#undef UINT64_C
|
||||
|
||||
// 7.18.4.1 Macros for minimum-width integer constants
|
||||
|
||||
#define INT8_C(val) val##i8
|
||||
#define INT16_C(val) val##i16
|
||||
#define INT32_C(val) val##i32
|
||||
#define INT64_C(val) val##i64
|
||||
|
||||
#define UINT8_C(val) val##ui8
|
||||
#define UINT16_C(val) val##ui16
|
||||
#define UINT32_C(val) val##ui32
|
||||
#define UINT64_C(val) val##ui64
|
||||
|
||||
// 7.18.4.2 Macros for greatest-width integer constants
|
||||
// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
|
||||
// Check out Issue 9 for the details.
|
||||
#ifndef INTMAX_C // [
|
||||
# define INTMAX_C INT64_C
|
||||
#endif // INTMAX_C ]
|
||||
#ifndef UINTMAX_C // [
|
||||
# define UINTMAX_C UINT64_C
|
||||
#endif // UINTMAX_C ]
|
||||
|
||||
#endif // __STDC_CONSTANT_MACROS ]
|
||||
|
||||
#else // ] _MSC_VER >= 1700 [
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
|
||||
// compiling for ARM we have to wrap <wchar.h> include with 'extern "C++" {}'
|
||||
// or compiler would give many errors like this:
|
||||
// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
|
||||
#if defined(__cplusplus) && !defined(_M_ARM)
|
||||
extern "C" {
|
||||
#endif
|
||||
# include <wchar.h>
|
||||
#if defined(__cplusplus) && !defined(_M_ARM)
|
||||
}
|
||||
#endif
|
||||
|
||||
// Define _W64 macros to mark types changing their size, like intptr_t.
|
||||
#ifndef _W64
|
||||
# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
|
||||
# define _W64 __w64
|
||||
# else
|
||||
# define _W64
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
// 7.18.1 Integer types
|
||||
|
||||
// 7.18.1.1 Exact-width integer types
|
||||
|
||||
// Visual Studio 6 and Embedded Visual C++ 4 doesn't
|
||||
// realize that, e.g. char has the same size as __int8
|
||||
// so we give up on __intX for them.
|
||||
#if (_MSC_VER < 1300)
|
||||
typedef signed char int8_t;
|
||||
typedef signed short int16_t;
|
||||
typedef signed int int32_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
#else
|
||||
typedef signed __int8 int8_t;
|
||||
typedef signed __int16 int16_t;
|
||||
typedef signed __int32 int32_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
#endif
|
||||
typedef signed __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
|
||||
|
||||
// 7.18.1.2 Minimum-width integer types
|
||||
typedef int8_t int_least8_t;
|
||||
typedef int16_t int_least16_t;
|
||||
typedef int32_t int_least32_t;
|
||||
typedef int64_t int_least64_t;
|
||||
typedef uint8_t uint_least8_t;
|
||||
typedef uint16_t uint_least16_t;
|
||||
typedef uint32_t uint_least32_t;
|
||||
typedef uint64_t uint_least64_t;
|
||||
|
||||
// 7.18.1.3 Fastest minimum-width integer types
|
||||
typedef int8_t int_fast8_t;
|
||||
typedef int16_t int_fast16_t;
|
||||
typedef int32_t int_fast32_t;
|
||||
typedef int64_t int_fast64_t;
|
||||
typedef uint8_t uint_fast8_t;
|
||||
typedef uint16_t uint_fast16_t;
|
||||
typedef uint32_t uint_fast32_t;
|
||||
typedef uint64_t uint_fast64_t;
|
||||
|
||||
// 7.18.1.4 Integer types capable of holding object pointers
|
||||
#ifdef _WIN64 // [
|
||||
typedef signed __int64 intptr_t;
|
||||
typedef unsigned __int64 uintptr_t;
|
||||
#else // _WIN64 ][
|
||||
typedef _W64 signed int intptr_t;
|
||||
typedef _W64 unsigned int uintptr_t;
|
||||
#endif // _WIN64 ]
|
||||
|
||||
// 7.18.1.5 Greatest-width integer types
|
||||
typedef int64_t intmax_t;
|
||||
typedef uint64_t uintmax_t;
|
||||
|
||||
|
||||
// 7.18.2 Limits of specified-width integer types
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
|
||||
|
||||
// 7.18.2.1 Limits of exact-width integer types
|
||||
#define INT8_MIN ((int8_t)_I8_MIN)
|
||||
#define INT8_MAX _I8_MAX
|
||||
#define INT16_MIN ((int16_t)_I16_MIN)
|
||||
#define INT16_MAX _I16_MAX
|
||||
#define INT32_MIN ((int32_t)_I32_MIN)
|
||||
#define INT32_MAX _I32_MAX
|
||||
#define INT64_MIN ((int64_t)_I64_MIN)
|
||||
#define INT64_MAX _I64_MAX
|
||||
#define UINT8_MAX _UI8_MAX
|
||||
#define UINT16_MAX _UI16_MAX
|
||||
#define UINT32_MAX _UI32_MAX
|
||||
#define UINT64_MAX _UI64_MAX
|
||||
|
||||
// 7.18.2.2 Limits of minimum-width integer types
|
||||
#define INT_LEAST8_MIN INT8_MIN
|
||||
#define INT_LEAST8_MAX INT8_MAX
|
||||
#define INT_LEAST16_MIN INT16_MIN
|
||||
#define INT_LEAST16_MAX INT16_MAX
|
||||
#define INT_LEAST32_MIN INT32_MIN
|
||||
#define INT_LEAST32_MAX INT32_MAX
|
||||
#define INT_LEAST64_MIN INT64_MIN
|
||||
#define INT_LEAST64_MAX INT64_MAX
|
||||
#define UINT_LEAST8_MAX UINT8_MAX
|
||||
#define UINT_LEAST16_MAX UINT16_MAX
|
||||
#define UINT_LEAST32_MAX UINT32_MAX
|
||||
#define UINT_LEAST64_MAX UINT64_MAX
|
||||
|
||||
// 7.18.2.3 Limits of fastest minimum-width integer types
|
||||
#define INT_FAST8_MIN INT8_MIN
|
||||
#define INT_FAST8_MAX INT8_MAX
|
||||
#define INT_FAST16_MIN INT16_MIN
|
||||
#define INT_FAST16_MAX INT16_MAX
|
||||
#define INT_FAST32_MIN INT32_MIN
|
||||
#define INT_FAST32_MAX INT32_MAX
|
||||
#define INT_FAST64_MIN INT64_MIN
|
||||
#define INT_FAST64_MAX INT64_MAX
|
||||
#define UINT_FAST8_MAX UINT8_MAX
|
||||
#define UINT_FAST16_MAX UINT16_MAX
|
||||
#define UINT_FAST32_MAX UINT32_MAX
|
||||
#define UINT_FAST64_MAX UINT64_MAX
|
||||
|
||||
// 7.18.2.4 Limits of integer types capable of holding object pointers
|
||||
#ifdef _WIN64 // [
|
||||
# define INTPTR_MIN INT64_MIN
|
||||
# define INTPTR_MAX INT64_MAX
|
||||
# define UINTPTR_MAX UINT64_MAX
|
||||
#else // _WIN64 ][
|
||||
# define INTPTR_MIN INT32_MIN
|
||||
# define INTPTR_MAX INT32_MAX
|
||||
# define UINTPTR_MAX UINT32_MAX
|
||||
#endif // _WIN64 ]
|
||||
|
||||
// 7.18.2.5 Limits of greatest-width integer types
|
||||
#define INTMAX_MIN INT64_MIN
|
||||
#define INTMAX_MAX INT64_MAX
|
||||
#define UINTMAX_MAX UINT64_MAX
|
||||
|
||||
// 7.18.3 Limits of other integer types
|
||||
|
||||
#ifdef _WIN64 // [
|
||||
# define PTRDIFF_MIN _I64_MIN
|
||||
# define PTRDIFF_MAX _I64_MAX
|
||||
#else // _WIN64 ][
|
||||
# define PTRDIFF_MIN _I32_MIN
|
||||
# define PTRDIFF_MAX _I32_MAX
|
||||
#endif // _WIN64 ]
|
||||
|
||||
#define SIG_ATOMIC_MIN INT_MIN
|
||||
#define SIG_ATOMIC_MAX INT_MAX
|
||||
|
||||
#ifndef SIZE_MAX // [
|
||||
# ifdef _WIN64 // [
|
||||
# define SIZE_MAX _UI64_MAX
|
||||
# else // _WIN64 ][
|
||||
# define SIZE_MAX _UI32_MAX
|
||||
# endif // _WIN64 ]
|
||||
#endif // SIZE_MAX ]
|
||||
|
||||
// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
|
||||
#ifndef WCHAR_MIN // [
|
||||
# define WCHAR_MIN 0
|
||||
#endif // WCHAR_MIN ]
|
||||
#ifndef WCHAR_MAX // [
|
||||
# define WCHAR_MAX _UI16_MAX
|
||||
#endif // WCHAR_MAX ]
|
||||
|
||||
#define WINT_MIN 0
|
||||
#define WINT_MAX _UI16_MAX
|
||||
|
||||
#endif // __STDC_LIMIT_MACROS ]
|
||||
|
||||
|
||||
// 7.18.4 Limits of other integer types
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
|
||||
|
||||
// 7.18.4.1 Macros for minimum-width integer constants
|
||||
|
||||
#define INT8_C(val) val##i8
|
||||
#define INT16_C(val) val##i16
|
||||
#define INT32_C(val) val##i32
|
||||
#define INT64_C(val) val##i64
|
||||
|
||||
#define UINT8_C(val) val##ui8
|
||||
#define UINT16_C(val) val##ui16
|
||||
#define UINT32_C(val) val##ui32
|
||||
#define UINT64_C(val) val##ui64
|
||||
|
||||
// 7.18.4.2 Macros for greatest-width integer constants
|
||||
// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
|
||||
// Check out Issue 9 for the details.
|
||||
#ifndef INTMAX_C // [
|
||||
# define INTMAX_C INT64_C
|
||||
#endif // INTMAX_C ]
|
||||
#ifndef UINTMAX_C // [
|
||||
# define UINTMAX_C UINT64_C
|
||||
#endif // UINTMAX_C ]
|
||||
|
||||
#endif // __STDC_CONSTANT_MACROS ]
|
||||
|
||||
#endif // _MSC_VER >= 1600 ]
|
||||
|
||||
#endif // _MSC_STDINT_H_ ]
|
||||