pax_global_header00006660000000000000000000000064145356601350014522gustar00rootroot0000000000000052 comment=34765b4a9e937ab927b5bd57231e98c68fe13109 go-toml-2.1.1/000077500000000000000000000000001453566013500131015ustar00rootroot00000000000000go-toml-2.1.1/.dockerignore000066400000000000000000000000461453566013500155550ustar00rootroot00000000000000cmd/tomll/tomll cmd/tomljson/tomljson go-toml-2.1.1/.gitattributes000066400000000000000000000001121453566013500157660ustar00rootroot00000000000000* text=auto benchmark/benchmark.toml text eol=lf testdata/** text eol=lf go-toml-2.1.1/.github/000077500000000000000000000000001453566013500144415ustar00rootroot00000000000000go-toml-2.1.1/.github/ISSUE_TEMPLATE/000077500000000000000000000000001453566013500166245ustar00rootroot00000000000000go-toml-2.1.1/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000010401453566013500213110ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior. Including TOML files. **Expected behavior** A clear and concise description of what you expected to happen, if other than "should work". **Versions** - go-toml: version (git sha) - go: version - operating system: e.g. macOS, Windows, Linux **Additional context** Add any other context about the problem here that you think may help to diagnose. go-toml-2.1.1/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000006501453566013500202430ustar00rootroot00000000000000 Explanation of what this pull request does. More detailed description of the decisions being made and the reasons why (if the patch is non-trivial). --- Paste `benchstat` results here go-toml-2.1.1/.github/dependabot.yml000066400000000000000000000005311453566013500172700ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: gomod directory: / schedule: interval: daily open-pull-requests-limit: 10 - package-ecosystem: github-actions directory: / schedule: interval: daily open-pull-requests-limit: 10 - package-ecosystem: docker directory: / schedule: interval: daily open-pull-requests-limit: 10 go-toml-2.1.1/.github/release.yml000066400000000000000000000005451453566013500166100ustar00rootroot00000000000000changelog: exclude: labels: - build - testing categories: - title: What's new labels: - feature - title: Performance labels: - performance - title: Fixed bugs labels: - bug - title: Documentation labels: - doc - title: Other changes labels: - "*" go-toml-2.1.1/.github/workflows/000077500000000000000000000000001453566013500164765ustar00rootroot00000000000000go-toml-2.1.1/.github/workflows/cifuzz.yml000066400000000000000000000012711453566013500205340ustar00rootroot00000000000000name: CIFuzz on: [pull_request] jobs: Fuzzing: runs-on: ubuntu-latest steps: - name: Build Fuzzers id: build uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master with: oss-fuzz-project-name: 'go-toml' dry-run: false language: go - name: Run Fuzzers uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master with: oss-fuzz-project-name: 'go-toml' fuzz-seconds: 300 dry-run: false language: go - name: Upload Crash uses: actions/upload-artifact@v3 if: failure() && steps.build.outcome == 'success' with: name: artifacts path: ./out/artifacts go-toml-2.1.1/.github/workflows/codeql-analysis.yml000066400000000000000000000044711453566013500223170ustar00rootroot00000000000000# For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ master, v2 ] pull_request: # The branches below must be a subset of the branches above branches: [ master ] schedule: - cron: '26 19 * * 0' jobs: analyze: name: Analyze runs-on: ubuntu-latest strategy: fail-fast: false matrix: language: [ 'go' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # Learn more: # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed steps: - name: Checkout repository uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v2 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 go-toml-2.1.1/.github/workflows/coverage.yml000066400000000000000000000006241453566013500210160ustar00rootroot00000000000000name: coverage on: pull_request: branches: - v2 jobs: report: runs-on: "ubuntu-latest" name: report steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup go uses: actions/setup-go@v4 with: go-version: "1.21" - name: Run tests with coverage run: ./ci.sh coverage -d "${GITHUB_BASE_REF-HEAD}" go-toml-2.1.1/.github/workflows/release.yml000066400000000000000000000016701453566013500206450ustar00rootroot00000000000000name: release on: push: tags: - "v2.*" workflow_call: inputs: args: description: "Extra arguments to pass goreleaser" default: "" required: false type: string jobs: release: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Go uses: actions/setup-go@v4 with: go-version: "1.21" - name: Login to GitHub Container Registry uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Run GoReleaser uses: goreleaser/goreleaser-action@v3 with: distribution: goreleaser version: latest args: release ${{ inputs.args }} --rm-dist env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} go-toml-2.1.1/.github/workflows/workflow.yml000066400000000000000000000013351453566013500210750ustar00rootroot00000000000000name: test on: push: branches: - v2 pull_request: branches: - v2 jobs: build: strategy: matrix: os: [ 'ubuntu-latest', 'windows-latest', 'macos-latest' ] go: [ '1.20', '1.21' ] runs-on: ${{ matrix.os }} name: ${{ matrix.go }}/${{ matrix.os }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup go ${{ matrix.go }} uses: actions/setup-go@v4 with: go-version: ${{ matrix.go }} - name: Run unit tests run: go test -race ./... release-check: if: ${{ github.ref != 'refs/heads/v2' }} uses: pelletier/go-toml/.github/workflows/release.yml@v2 with: args: --snapshot go-toml-2.1.1/.gitignore000066400000000000000000000001621453566013500150700ustar00rootroot00000000000000test_program/test_program_bin fuzz/ cmd/tomll/tomll cmd/tomljson/tomljson cmd/tomltestgen/tomltestgen dist tests/ go-toml-2.1.1/.golangci.toml000066400000000000000000000026041453566013500156410ustar00rootroot00000000000000[service] golangci-lint-version = "1.39.0" [linters-settings.wsl] allow-assign-and-anything = true [linters-settings.exhaustive] default-signifies-exhaustive = true [linters] disable-all = true enable = [ "asciicheck", "bodyclose", "cyclop", "deadcode", "depguard", "dogsled", "dupl", "durationcheck", "errcheck", "errorlint", "exhaustive", # "exhaustivestruct", "exportloopref", "forbidigo", # "forcetypeassert", "funlen", "gci", # "gochecknoglobals", "gochecknoinits", "gocognit", "goconst", "gocritic", "gocyclo", "godot", "godox", # "goerr113", "gofmt", "gofumpt", "goheader", "goimports", "golint", "gomnd", # "gomoddirectives", "gomodguard", "goprintffuncname", "gosec", "gosimple", "govet", # "ifshort", "importas", "ineffassign", "lll", "makezero", "misspell", "nakedret", "nestif", "nilerr", # "nlreturn", "noctx", "nolintlint", #"paralleltest", "prealloc", "predeclared", "revive", "rowserrcheck", "sqlclosecheck", "staticcheck", "structcheck", "stylecheck", # "testpackage", "thelper", "tparallel", "typecheck", "unconvert", "unparam", "unused", "varcheck", "wastedassign", "whitespace", # "wrapcheck", # "wsl" ] go-toml-2.1.1/.goreleaser.yaml000066400000000000000000000052001453566013500161700ustar00rootroot00000000000000before: hooks: - go mod tidy - go fmt ./... - go test ./... builds: - id: tomll main: ./cmd/tomll binary: tomll env: - CGO_ENABLED=0 flags: - -trimpath ldflags: - -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} mod_timestamp: '{{ .CommitTimestamp }}' targets: - linux_amd64 - linux_arm64 - linux_arm - linux_riscv64 - windows_amd64 - windows_arm64 - windows_arm - darwin_amd64 - darwin_arm64 - id: tomljson main: ./cmd/tomljson binary: tomljson env: - CGO_ENABLED=0 flags: - -trimpath ldflags: - -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} mod_timestamp: '{{ .CommitTimestamp }}' targets: - linux_amd64 - linux_arm64 - linux_arm - linux_riscv64 - windows_amd64 - windows_arm64 - windows_arm - darwin_amd64 - darwin_arm64 - id: jsontoml main: ./cmd/jsontoml binary: jsontoml env: - CGO_ENABLED=0 flags: - -trimpath ldflags: - -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} mod_timestamp: '{{ .CommitTimestamp }}' targets: - linux_amd64 - linux_arm64 - linux_riscv64 - linux_arm - windows_amd64 - windows_arm64 - windows_arm - darwin_amd64 - darwin_arm64 universal_binaries: - id: tomll replace: true name_template: tomll - id: tomljson replace: true name_template: tomljson - id: jsontoml replace: true name_template: jsontoml archives: - id: jsontoml format: tar.xz builds: - jsontoml files: - none* name_template: "{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}" - id: tomljson format: tar.xz builds: - tomljson files: - none* name_template: "{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}" - id: tomll format: tar.xz builds: - tomll files: - none* name_template: "{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}" dockers: - id: tools goos: linux goarch: amd64 ids: - jsontoml - tomljson - tomll image_templates: - "ghcr.io/pelletier/go-toml:latest" - "ghcr.io/pelletier/go-toml:{{ .Tag }}" - "ghcr.io/pelletier/go-toml:v{{ .Major }}" skip_push: false checksum: name_template: 'sha256sums.txt' snapshot: name_template: "{{ incpatch .Version }}-next" release: github: owner: pelletier name: go-toml draft: true prerelease: auto mode: replace changelog: use: github-native announce: skip: true go-toml-2.1.1/CONTRIBUTING.md000066400000000000000000000165251453566013500153430ustar00rootroot00000000000000# Contributing Thank you for your interest in go-toml! We appreciate you considering contributing to go-toml! The main goal is the project is to provide an easy-to-use and efficient TOML implementation for Go that gets the job done and gets out of your way – dealing with TOML is probably not the central piece of your project. As the single maintainer of go-toml, time is scarce. All help, big or small, is more than welcomed! ## Ask questions Any question you may have, somebody else might have it too. Always feel free to ask them on the [discussion board][discussions]. We will try to answer them as clearly and quickly as possible, time permitting. Asking questions also helps us identify areas where the documentation needs improvement, or new features that weren't envisioned before. Sometimes, a seemingly innocent question leads to the fix of a bug. Don't hesitate and ask away! [discussions]: https://github.com/pelletier/go-toml/discussions ## Improve the documentation The best way to share your knowledge and experience with go-toml is to improve the documentation. Fix a typo, clarify an interface, add an example, anything goes! The documentation is present in the [README][readme] and thorough the source code. On release, it gets updated on [pkg.go.dev][pkg.go.dev]. To make a change to the documentation, create a pull request with your proposed changes. For simple changes like that, the easiest way to go is probably the "Fork this project and edit the file" button on Github, displayed at the top right of the file. Unless it's a trivial change (for example a typo), provide a little bit of context in your pull request description or commit message. ## Report a bug Found a bug! Sorry to hear that :(. Help us and other track them down and fix by reporting it. [File a new bug report][bug-report] on the [issues tracker][issues-tracker]. The template should provide enough guidance on what to include. When in doubt: add more details! By reducing ambiguity and providing more information, it decreases back and forth and saves everyone time. ## Code changes Want to contribute a patch? Very happy to hear that! First, some high-level rules: - A short proposal with some POC code is better than a lengthy piece of text with no code. Code speaks louder than words. That being said, bigger changes should probably start with a [discussion][discussions]. - No backward-incompatible patch will be accepted unless discussed. Sometimes it's hard, but we try not to break people's programs unless we absolutely have to. - If you are writing a new feature or extending an existing one, make sure to write some documentation. - Bug fixes need to be accompanied with regression tests. - New code needs to be tested. - Your commit messages need to explain why the change is needed, even if already included in the PR description. It does sound like a lot, but those best practices are here to save time overall and continuously improve the quality of the project, which is something everyone benefits from. ### Get started The fairly standard code contribution process looks like that: 1. [Fork the project][fork]. 2. Make your changes, commit on any branch you like. 3. [Open up a pull request][pull-request] 4. Review, potential ask for changes. 5. Merge. Feel free to ask for help! You can create draft pull requests to gather some early feedback! ### Run the tests You can run tests for go-toml using Go's test tool: `go test -race ./...`. During the pull request process, all tests will be ran on Linux, Windows, and MacOS on the last two versions of Go. However, given GitHub's new policy to _not_ run Actions on pull requests until a maintainer clicks on button, it is highly recommended that you run them locally as you make changes. ### Check coverage We use `go tool cover` to compute test coverage. Most code editors have a way to run and display code coverage, but at the end of the day, we do this: ``` go test -covermode=atomic -coverprofile=coverage.out go tool cover -func=coverage.out ``` and verify that the overall percentage of tested code does not go down. This is a requirement. As a rule of thumb, all lines of code touched by your changes should be covered. On Unix you can use `./ci.sh coverage -d v2` to check if your code lowers the coverage. ### Verify performance Go-toml aims to stay efficient. We rely on a set of scenarios executed with Go's builtin benchmark systems. Because of their noisy nature, containers provided by Github Actions cannot be reliably used for benchmarking. As a result, you are responsible for checking that your changes do not incur a performance penalty. You can run their following to execute benchmarks: ``` go test ./... -bench=. -count=10 ``` Benchmark results should be compared against each other with [benchstat][benchstat]. Typical flow looks like this: 1. On the `v2` branch, run `go test ./... -bench=. -count 10` and save output to a file (for example `old.txt`). 2. Make some code changes. 3. Run `go test ....` again, and save the output to an other file (for example `new.txt`). 4. Run `benchstat old.txt new.txt` to check that time/op does not go up in any test. On Unix you can use `./ci.sh benchmark -d v2` to verify how your code impacts performance. It is highly encouraged to add the benchstat results to your pull request description. Pull requests that lower performance will receive more scrutiny. [benchstat]: https://pkg.go.dev/golang.org/x/perf/cmd/benchstat ### Style Try to look around and follow the same format and structure as the rest of the code. We enforce using `go fmt` on the whole code base. --- ## Maintainers-only ### Merge pull request Checklist: - Passing CI. - Does not introduce backward-incompatible changes (unless discussed). - Has relevant doc changes. - Benchstat does not show performance regression. - Pull request is [labeled appropriately][pr-labels]. - Title will be understandable in the changelog. 1. Merge using "squash and merge". 2. Make sure to edit the commit message to keep all the useful information nice and clean. 3. Make sure the commit title is clear and contains the PR number (#123). ### New release 1. Decide on the next version number. Use semver. 2. Generate release notes using [`gh`][gh]. Example: ``` $ gh api -X POST \ -F tag_name='v2.0.0-beta.5' \ -F target_commitish='v2' \ -F previous_tag_name='v2.0.0-beta.4' \ --jq '.body' \ repos/pelletier/go-toml/releases/generate-notes ``` 3. Look for "Other changes". That would indicate a pull request not labeled properly. Tweak labels and pull request titles until changelog looks good for users. 4. [Draft new release][new-release]. 5. Fill tag and target with the same value used to generate the changelog. 6. Set title to the new tag value. 7. Paste the generated changelog. 8. Check "create discussion", in the "Releases" category. 9. Check pre-release if new version is an alpha or beta. [issues-tracker]: https://github.com/pelletier/go-toml/issues [bug-report]: https://github.com/pelletier/go-toml/issues/new?template=bug_report.md [pkg.go.dev]: https://pkg.go.dev/github.com/pelletier/go-toml [readme]: ./README.md [fork]: https://help.github.com/articles/fork-a-repo [pull-request]: https://help.github.com/en/articles/creating-a-pull-request [new-release]: https://github.com/pelletier/go-toml/releases/new [gh]: https://github.com/cli/cli [pr-labels]: https://github.com/pelletier/go-toml/blob/v2/.github/release.yml go-toml-2.1.1/Dockerfile000066400000000000000000000001611453566013500150710ustar00rootroot00000000000000FROM scratch ENV PATH "$PATH:/bin" COPY tomll /bin/tomll COPY tomljson /bin/tomljson COPY jsontoml /bin/jsontoml go-toml-2.1.1/LICENSE000066400000000000000000000021151453566013500141050ustar00rootroot00000000000000The MIT License (MIT) go-toml v2 Copyright (c) 2021 - 2023 Thomas Pelletier 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. go-toml-2.1.1/README.md000066400000000000000000000417551453566013500143740ustar00rootroot00000000000000# go-toml v2 Go library for the [TOML](https://toml.io/en/) format. This library supports [TOML v1.0.0](https://toml.io/en/v1.0.0). [🐞 Bug Reports](https://github.com/pelletier/go-toml/issues) [💬 Anything else](https://github.com/pelletier/go-toml/discussions) ## Documentation Full API, examples, and implementation notes are available in the Go documentation. [![Go Reference](https://pkg.go.dev/badge/github.com/pelletier/go-toml/v2.svg)](https://pkg.go.dev/github.com/pelletier/go-toml/v2) ## Import ```go import "github.com/pelletier/go-toml/v2" ``` See [Modules](#Modules). ## Features ### Stdlib behavior As much as possible, this library is designed to behave similarly as the standard library's `encoding/json`. ### Performance While go-toml favors usability, it is written with performance in mind. Most operations should not be shockingly slow. See [benchmarks](#benchmarks). ### Strict mode `Decoder` can be set to "strict mode", which makes it error when some parts of the TOML document was not present in the target structure. This is a great way to check for typos. [See example in the documentation][strict]. [strict]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#example-Decoder.DisallowUnknownFields ### Contextualized errors When most decoding errors occur, go-toml returns [`DecodeError`][decode-err], which contains a human readable contextualized version of the error. For example: ``` 1| [server] 2| path = 100 | ~~~ cannot decode TOML integer into struct field toml_test.Server.Path of type string 3| port = 50 ``` [decode-err]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#DecodeError ### Local date and time support TOML supports native [local date/times][ldt]. It allows to represent a given date, time, or date-time without relation to a timezone or offset. To support this use-case, go-toml provides [`LocalDate`][tld], [`LocalTime`][tlt], and [`LocalDateTime`][tldt]. Those types can be transformed to and from `time.Time`, making them convenient yet unambiguous structures for their respective TOML representation. [ldt]: https://toml.io/en/v1.0.0#local-date-time [tld]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#LocalDate [tlt]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#LocalTime [tldt]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#LocalDateTime ### Commented config Since TOML is often used for configuration files, go-toml can emit documents annotated with [comments and commented-out values][comments-example]. For example, it can generate the following file: ```toml # Host IP to connect to. host = '127.0.0.1' # Port of the remote server. port = 4242 # Encryption parameters (optional) # [TLS] # cipher = 'AEAD-AES128-GCM-SHA256' # version = 'TLS 1.3' ``` [comments-example]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#example-Marshal-Commented ## Getting started Given the following struct, let's see how to read it and write it as TOML: ```go type MyConfig struct { Version int Name string Tags []string } ``` ### Unmarshaling [`Unmarshal`][unmarshal] reads a TOML document and fills a Go structure with its content. For example: ```go doc := ` version = 2 name = "go-toml" tags = ["go", "toml"] ` var cfg MyConfig err := toml.Unmarshal([]byte(doc), &cfg) if err != nil { panic(err) } fmt.Println("version:", cfg.Version) fmt.Println("name:", cfg.Name) fmt.Println("tags:", cfg.Tags) // Output: // version: 2 // name: go-toml // tags: [go toml] ``` [unmarshal]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#Unmarshal ### Marshaling [`Marshal`][marshal] is the opposite of Unmarshal: it represents a Go structure as a TOML document: ```go cfg := MyConfig{ Version: 2, Name: "go-toml", Tags: []string{"go", "toml"}, } b, err := toml.Marshal(cfg) if err != nil { panic(err) } fmt.Println(string(b)) // Output: // Version = 2 // Name = 'go-toml' // Tags = ['go', 'toml'] ``` [marshal]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#Marshal ## Unstable API This API does not yet follow the backward compatibility guarantees of this library. They provide early access to features that may have rough edges or an API subject to change. ### Parser Parser is the unstable API that allows iterative parsing of a TOML document at the AST level. See https://pkg.go.dev/github.com/pelletier/go-toml/v2/unstable. ## Benchmarks Execution time speedup compared to other Go TOML libraries:
Benchmarkgo-toml v1BurntSushi/toml
Marshal/HugoFrontMatter-21.9x2.2x
Marshal/ReferenceFile/map-21.7x2.1x
Marshal/ReferenceFile/struct-22.2x3.0x
Unmarshal/HugoFrontMatter-22.9x2.7x
Unmarshal/ReferenceFile/map-22.6x2.7x
Unmarshal/ReferenceFile/struct-24.6x5.1x
See more

The table above has the results of the most common use-cases. The table below contains the results of all benchmarks, including unrealistic ones. It is provided for completeness.

Benchmarkgo-toml v1BurntSushi/toml
Marshal/SimpleDocument/map-21.8x2.7x
Marshal/SimpleDocument/struct-22.7x3.8x
Unmarshal/SimpleDocument/map-23.8x3.0x
Unmarshal/SimpleDocument/struct-25.6x4.1x
UnmarshalDataset/example-23.0x3.2x
UnmarshalDataset/code-22.3x2.9x
UnmarshalDataset/twitter-22.6x2.7x
UnmarshalDataset/citm_catalog-22.2x2.3x
UnmarshalDataset/canada-21.8x1.5x
UnmarshalDataset/config-24.1x2.9x
geomean2.7x2.8x

This table can be generated with ./ci.sh benchmark -a -html.

## Modules go-toml uses Go's standard modules system. Installation instructions: - Go ≥ 1.16: Nothing to do. Use the import in your code. The `go` command deals with it automatically. - Go ≥ 1.13: `GO111MODULE=on go get github.com/pelletier/go-toml/v2`. In case of trouble: [Go Modules FAQ][mod-faq]. [mod-faq]: https://github.com/golang/go/wiki/Modules#why-does-installing-a-tool-via-go-get-fail-with-error-cannot-find-main-module ## Tools Go-toml provides three handy command line tools: * `tomljson`: Reads a TOML file and outputs its JSON representation. ``` $ go install github.com/pelletier/go-toml/v2/cmd/tomljson@latest $ tomljson --help ``` * `jsontoml`: Reads a JSON file and outputs a TOML representation. ``` $ go install github.com/pelletier/go-toml/v2/cmd/jsontoml@latest $ jsontoml --help ``` * `tomll`: Lints and reformats a TOML file. ``` $ go install github.com/pelletier/go-toml/v2/cmd/tomll@latest $ tomll --help ``` ### Docker image Those tools are also available as a [Docker image][docker]. For example, to use `tomljson`: ``` docker run -i ghcr.io/pelletier/go-toml:v2 tomljson < example.toml ``` Multiple versions are availble on [ghcr.io][docker]. [docker]: https://github.com/pelletier/go-toml/pkgs/container/go-toml ## Migrating from v1 This section describes the differences between v1 and v2, with some pointers on how to get the original behavior when possible. ### Decoding / Unmarshal #### Automatic field name guessing When unmarshaling to a struct, if a key in the TOML document does not exactly match the name of a struct field or any of the `toml`-tagged field, v1 tries multiple variations of the key ([code][v1-keys]). V2 instead does a case-insensitive matching, like `encoding/json`. This could impact you if you are relying on casing to differentiate two fields, and one of them is a not using the `toml` struct tag. The recommended solution is to be specific about tag names for those fields using the `toml` struct tag. [v1-keys]: https://github.com/pelletier/go-toml/blob/a2e52561804c6cd9392ebf0048ca64fe4af67a43/marshal.go#L775-L781 #### Ignore preexisting value in interface When decoding into a non-nil `interface{}`, go-toml v1 uses the type of the element in the interface to decode the object. For example: ```go type inner struct { B interface{} } type doc struct { A interface{} } d := doc{ A: inner{ B: "Before", }, } data := ` [A] B = "After" ` toml.Unmarshal([]byte(data), &d) fmt.Printf("toml v1: %#v\n", d) // toml v1: main.doc{A:main.inner{B:"After"}} ``` In this case, field `A` is of type `interface{}`, containing a `inner` struct. V1 sees that type and uses it when decoding the object. When decoding an object into an `interface{}`, V2 instead disregards whatever value the `interface{}` may contain and replaces it with a `map[string]interface{}`. With the same data structure as above, here is what the result looks like: ```go toml.Unmarshal([]byte(data), &d) fmt.Printf("toml v2: %#v\n", d) // toml v2: main.doc{A:map[string]interface {}{"B":"After"}} ``` This is to match `encoding/json`'s behavior. There is no way to make the v2 decoder behave like v1. #### Values out of array bounds ignored When decoding into an array, v1 returns an error when the number of elements contained in the doc is superior to the capacity of the array. For example: ```go type doc struct { A [2]string } d := doc{} err := toml.Unmarshal([]byte(`A = ["one", "two", "many"]`), &d) fmt.Println(err) // (1, 1): unmarshal: TOML array length (3) exceeds destination array length (2) ``` In the same situation, v2 ignores the last value: ```go err := toml.Unmarshal([]byte(`A = ["one", "two", "many"]`), &d) fmt.Println("err:", err, "d:", d) // err: d: {[one two]} ``` This is to match `encoding/json`'s behavior. There is no way to make the v2 decoder behave like v1. #### Support for `toml.Unmarshaler` has been dropped This method was not widely used, poorly defined, and added a lot of complexity. A similar effect can be achieved by implementing the `encoding.TextUnmarshaler` interface and use strings. #### Support for `default` struct tag has been dropped This feature adds complexity and a poorly defined API for an effect that can be accomplished outside of the library. It does not seem like other format parsers in Go support that feature (the project referenced in the original ticket #202 has not been updated since 2017). Given that go-toml v2 should not touch values not in the document, the same effect can be achieved by pre-filling the struct with defaults (libraries like [go-defaults][go-defaults] can help). Also, string representation is not well defined for all types: it creates issues like #278. The recommended replacement is pre-filling the struct before unmarshaling. [go-defaults]: https://github.com/mcuadros/go-defaults #### `toml.Tree` replacement This structure was the initial attempt at providing a document model for go-toml. It allows manipulating the structure of any document, encoding and decoding from their TOML representation. While a more robust feature was initially planned in go-toml v2, this has been ultimately [removed from scope][nodoc] of this library, with no plan to add it back at the moment. The closest equivalent at the moment would be to unmarshal into an `interface{}` and use type assertions and/or reflection to manipulate the arbitrary structure. However this would fall short of providing all of the TOML features such as adding comments and be specific about whitespace. #### `toml.Position` are not retrievable anymore The API for retrieving the position (line, column) of a specific TOML element do not exist anymore. This was done to minimize the amount of concepts introduced by the library (query path), and avoid the performance hit related to storing positions in the absence of a document model, for a feature that seemed to have little use. Errors however have gained more detailed position information. Position retrieval seems better fitted for a document model, which has been [removed from the scope][nodoc] of go-toml v2 at the moment. ### Encoding / Marshal #### Default struct fields order V1 emits struct fields order alphabetically by default. V2 struct fields are emitted in order they are defined. For example: ```go type S struct { B string A string } data := S{ B: "B", A: "A", } b, _ := tomlv1.Marshal(data) fmt.Println("v1:\n" + string(b)) b, _ = tomlv2.Marshal(data) fmt.Println("v2:\n" + string(b)) // Output: // v1: // A = "A" // B = "B" // v2: // B = 'B' // A = 'A' ``` There is no way to make v2 encoder behave like v1. A workaround could be to manually sort the fields alphabetically in the struct definition, or generate struct types using `reflect.StructOf`. #### No indentation by default V1 automatically indents content of tables by default. V2 does not. However the same behavior can be obtained using [`Encoder.SetIndentTables`][sit]. For example: ```go data := map[string]interface{}{ "table": map[string]string{ "key": "value", }, } b, _ := tomlv1.Marshal(data) fmt.Println("v1:\n" + string(b)) b, _ = tomlv2.Marshal(data) fmt.Println("v2:\n" + string(b)) buf := bytes.Buffer{} enc := tomlv2.NewEncoder(&buf) enc.SetIndentTables(true) enc.Encode(data) fmt.Println("v2 Encoder:\n" + string(buf.Bytes())) // Output: // v1: // // [table] // key = "value" // // v2: // [table] // key = 'value' // // // v2 Encoder: // [table] // key = 'value' ``` [sit]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#Encoder.SetIndentTables #### Keys and strings are single quoted V1 always uses double quotes (`"`) around strings and keys that cannot be represented bare (unquoted). V2 uses single quotes instead by default (`'`), unless a character cannot be represented, then falls back to double quotes. As a result of this change, `Encoder.QuoteMapKeys` has been removed, as it is not useful anymore. There is no way to make v2 encoder behave like v1. #### `TextMarshaler` emits as a string, not TOML Types that implement [`encoding.TextMarshaler`][tm] can emit arbitrary TOML in v1. The encoder would append the result to the output directly. In v2 the result is wrapped in a string. As a result, this interface cannot be implemented by the root object. There is no way to make v2 encoder behave like v1. [tm]: https://golang.org/pkg/encoding/#TextMarshaler #### `Encoder.CompactComments` has been removed Emitting compact comments is now the default behavior of go-toml. This option is not necessary anymore. #### Struct tags have been merged V1 used to provide multiple struct tags: `comment`, `commented`, `multiline`, `toml`, and `omitempty`. To behave more like the standard library, v2 has merged `toml`, `multiline`, `commented`, and `omitempty`. For example: ```go type doc struct { // v1 F string `toml:"field" multiline:"true" omitempty:"true" commented:"true"` // v2 F string `toml:"field,multiline,omitempty,commented"` } ``` Has a result, the `Encoder.SetTag*` methods have been removed, as there is just one tag now. #### `Encoder.ArraysWithOneElementPerLine` has been renamed The new name is `Encoder.SetArraysMultiline`. The behavior should be the same. #### `Encoder.Indentation` has been renamed The new name is `Encoder.SetIndentSymbol`. The behavior should be the same. #### Embedded structs behave like stdlib V1 defaults to merging embedded struct fields into the embedding struct. This behavior was unexpected because it does not follow the standard library. To avoid breaking backward compatibility, the `Encoder.PromoteAnonymous` method was added to make the encoder behave correctly. Given backward compatibility is not a problem anymore, v2 does the right thing by default: it follows the behavior of `encoding/json`. `Encoder.PromoteAnonymous` has been removed. [nodoc]: https://github.com/pelletier/go-toml/discussions/506#discussioncomment-1526038 ### `query` go-toml v1 provided the [`go-toml/query`][query] package. It allowed to run JSONPath-style queries on TOML files. This feature is not available in v2. For a replacement, check out [dasel][dasel]. This package has been removed because it was essentially not supported anymore (last commit May 2020), increased the complexity of the code base, and more complete solutions exist out there. [query]: https://github.com/pelletier/go-toml/tree/f99d6bbca119636aeafcf351ee52b3d202782627/query [dasel]: https://github.com/TomWright/dasel ## Versioning Go-toml follows [Semantic Versioning](https://semver.org). The supported version of [TOML](https://github.com/toml-lang/toml) is indicated at the beginning of this document. The last two major versions of Go are supported (see [Go Release Policy](https://golang.org/doc/devel/release.html#policy)). ## License The MIT License (MIT). Read [LICENSE](LICENSE). go-toml-2.1.1/SECURITY.md000066400000000000000000000010501453566013500146660ustar00rootroot00000000000000# Security Policy ## Supported Versions | Version | Supported | | ---------- | ------------------ | | Latest 2.x | :white_check_mark: | | All 1.x | :x: | | All 0.x | :x: | ## Reporting a Vulnerability Email a vulnerability report to `security@pelletier.codes`. Make sure to include as many details as possible to reproduce the vulnerability. This is a side-project: I will try to get back to you as quickly as possible, time permitting in my personal life. Providing a working patch helps very much! go-toml-2.1.1/benchmark/000077500000000000000000000000001453566013500150335ustar00rootroot00000000000000go-toml-2.1.1/benchmark/bench_datasets_test.go000066400000000000000000000034161453566013500213740ustar00rootroot00000000000000package benchmark_test import ( "compress/gzip" "encoding/json" "io/ioutil" "os" "path/filepath" "testing" "github.com/pelletier/go-toml/v2" "github.com/stretchr/testify/require" ) var bench_inputs = []struct { name string jsonLen int }{ // from https://gist.githubusercontent.com/feeeper/2197d6d734729625a037af1df14cf2aa/raw/2f22b120e476d897179be3c1e2483d18067aa7df/config.toml {"config", 806507}, // converted from https://github.com/miloyip/nativejson-benchmark {"canada", 2090234}, {"citm_catalog", 479897}, {"twitter", 428778}, {"code", 1940472}, // converted from https://raw.githubusercontent.com/mailru/easyjson/master/benchmark/example.json {"example", 7779}, } func TestUnmarshalDatasetCode(t *testing.T) { for _, tc := range bench_inputs { t.Run(tc.name, func(t *testing.T) { buf := fixture(t, tc.name) var v interface{} require.NoError(t, toml.Unmarshal(buf, &v)) b, err := json.Marshal(v) require.NoError(t, err) require.Equal(t, len(b), tc.jsonLen) }) } } func BenchmarkUnmarshalDataset(b *testing.B) { for _, tc := range bench_inputs { b.Run(tc.name, func(b *testing.B) { buf := fixture(b, tc.name) b.SetBytes(int64(len(buf))) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { var v interface{} require.NoError(b, toml.Unmarshal(buf, &v)) } }) } } // fixture returns the uncompressed contents of path. func fixture(tb testing.TB, path string) []byte { tb.Helper() file := path + ".toml.gz" f, err := os.Open(filepath.Join("testdata", file)) if os.IsNotExist(err) { tb.Skip("benchmark fixture not found:", file) } require.NoError(tb, err) defer f.Close() gz, err := gzip.NewReader(f) require.NoError(tb, err) buf, err := ioutil.ReadAll(gz) require.NoError(tb, err) return buf } go-toml-2.1.1/benchmark/benchmark.toml000066400000000000000000000121711453566013500176640ustar00rootroot00000000000000################################################################################ ## Comment # Speak your mind with the hash symbol. They go from the symbol to the end of # the line. ################################################################################ ## Table # Tables (also known as hash tables or dictionaries) are collections of # key/value pairs. They appear in square brackets on a line by themselves. [table] key = "value" # Yeah, you can do this. # Nested tables are denoted by table names with dots in them. Name your tables # whatever crap you please, just don't use #, ., [ or ]. [table.subtable] key = "another value" # You don't need to specify all the super-tables if you don't want to. TOML # knows how to do it for you. # [x] you # [x.y] don't # [x.y.z] need these [x.y.z.w] # for this to work ################################################################################ ## Inline Table # Inline tables provide a more compact syntax for expressing tables. They are # especially useful for grouped data that can otherwise quickly become verbose. # Inline tables are enclosed in curly braces `{` and `}`. No newlines are # allowed between the curly braces unless they are valid within a value. [table.inline] name = { first = "Tom", last = "Preston-Werner" } point = { x = 1, y = 2 } ################################################################################ ## String # There are four ways to express strings: basic, multi-line basic, literal, and # multi-line literal. All strings must contain only valid UTF-8 characters. [string.basic] basic = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF." [string.multiline] # The following strings are byte-for-byte equivalent: key1 = "One\nTwo" key2 = """One\nTwo""" key3 = """ One Two""" [string.multiline.continued] # The following strings are byte-for-byte equivalent: key1 = "The quick brown fox jumps over the lazy dog." key2 = """ The quick brown \ fox jumps over \ the lazy dog.""" key3 = """\ The quick brown \ fox jumps over \ the lazy dog.\ """ [string.literal] # What you see is what you get. winpath = 'C:\Users\nodejs\templates' winpath2 = '\\ServerX\admin$\system32\' quoted = 'Tom "Dubs" Preston-Werner' regex = '<\i\c*\s*>' [string.literal.multiline] regex2 = '''I [dw]on't need \d{2} apples''' lines = ''' The first newline is trimmed in raw strings. All other whitespace is preserved. ''' ################################################################################ ## Integer # Integers are whole numbers. Positive numbers may be prefixed with a plus sign. # Negative numbers are prefixed with a minus sign. [integer] key1 = +99 key2 = 42 key3 = 0 key4 = -17 [integer.underscores] # For large numbers, you may use underscores to enhance readability. Each # underscore must be surrounded by at least one digit. key1 = 1_000 key2 = 5_349_221 key3 = 1_2_3_4_5 # valid but inadvisable ################################################################################ ## Float # A float consists of an integer part (which may be prefixed with a plus or # minus sign) followed by a fractional part and/or an exponent part. [float.fractional] key1 = +1.0 key2 = 3.1415 key3 = -0.01 [float.exponent] key1 = 5e+22 key2 = 1e6 key3 = -2E-2 [float.both] key = 6.626e-34 [float.underscores] key1 = 9_224_617.445_991_228_313 key2 = 1e1_00 ################################################################################ ## Boolean # Booleans are just the tokens you're used to. Always lowercase. [boolean] True = true False = false ################################################################################ ## Datetime # Datetimes are RFC 3339 dates. [datetime] key1 = 1979-05-27T07:32:00Z key2 = 1979-05-27T00:32:00-07:00 key3 = 1979-05-27T00:32:00.999999-07:00 ################################################################################ ## Array # Arrays are square brackets with other primitives inside. Whitespace is # ignored. Elements are separated by commas. Data types may not be mixed. [array] key1 = [ 1, 2, 3 ] key2 = [ "red", "yellow", "green" ] key3 = [ [ 1, 2 ], [3, 4, 5] ] key4 = [ [ 1, 2 ], ["a", "b", "c"] ] # this is ok # Arrays can also be multiline. So in addition to ignoring whitespace, arrays # also ignore newlines between the brackets. Terminating commas are ok before # the closing bracket. key5 = [ 1, 2, 3 ] key6 = [ 1, 2, # this is ok ] ################################################################################ ## Array of Tables # These can be expressed by using a table name in double brackets. Each table # with the same double bracketed name will be an element in the array. The # tables are inserted in the order encountered. [[products]] name = "Hammer" sku = 738594937 [[products]] [[products]] name = "Nail" sku = 284758393 color = "gray" # You can create nested arrays of tables as well. [[fruit]] name = "apple" [fruit.physical] color = "red" shape = "round" [[fruit.variety]] name = "red delicious" [[fruit.variety]] name = "granny smith" [[fruit]] name = "banana" [[fruit.variety]] name = "plantain" go-toml-2.1.1/benchmark/benchmark_test.go000066400000000000000000000260761453566013500203660ustar00rootroot00000000000000package benchmark_test import ( "bytes" "io/ioutil" "testing" "time" "github.com/pelletier/go-toml/v2" "github.com/stretchr/testify/require" ) func TestUnmarshalSimple(t *testing.T) { doc := []byte(`A = "hello"`) d := struct { A string }{} err := toml.Unmarshal(doc, &d) if err != nil { panic(err) } } func BenchmarkUnmarshal(b *testing.B) { b.Run("SimpleDocument", func(b *testing.B) { doc := []byte(`A = "hello"`) b.Run("struct", func(b *testing.B) { b.SetBytes(int64(len(doc))) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { d := struct { A string }{} err := toml.Unmarshal(doc, &d) if err != nil { panic(err) } } }) b.Run("map", func(b *testing.B) { b.SetBytes(int64(len(doc))) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { d := map[string]interface{}{} err := toml.Unmarshal(doc, &d) if err != nil { panic(err) } } }) }) b.Run("ReferenceFile", func(b *testing.B) { bytes, err := ioutil.ReadFile("benchmark.toml") if err != nil { b.Fatal(err) } b.Run("struct", func(b *testing.B) { b.SetBytes(int64(len(bytes))) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { d := benchmarkDoc{} err := toml.Unmarshal(bytes, &d) if err != nil { panic(err) } } }) b.Run("map", func(b *testing.B) { b.SetBytes(int64(len(bytes))) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { d := map[string]interface{}{} err := toml.Unmarshal(bytes, &d) if err != nil { panic(err) } } }) }) b.Run("HugoFrontMatter", func(b *testing.B) { b.SetBytes(int64(len(hugoFrontMatterbytes))) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { d := map[string]interface{}{} err := toml.Unmarshal(hugoFrontMatterbytes, &d) if err != nil { panic(err) } } }) } func marshal(v interface{}) ([]byte, error) { var b bytes.Buffer enc := toml.NewEncoder(&b) err := enc.Encode(v) return b.Bytes(), err } func BenchmarkMarshal(b *testing.B) { b.Run("SimpleDocument", func(b *testing.B) { doc := []byte(`A = "hello"`) b.Run("struct", func(b *testing.B) { d := struct { A string }{} err := toml.Unmarshal(doc, &d) if err != nil { panic(err) } b.ReportAllocs() b.ResetTimer() var out []byte for i := 0; i < b.N; i++ { out, err = marshal(d) if err != nil { panic(err) } } b.SetBytes(int64(len(out))) }) b.Run("map", func(b *testing.B) { d := map[string]interface{}{} err := toml.Unmarshal(doc, &d) if err != nil { panic(err) } b.ReportAllocs() b.ResetTimer() var out []byte for i := 0; i < b.N; i++ { out, err = marshal(d) if err != nil { panic(err) } } b.SetBytes(int64(len(out))) }) }) b.Run("ReferenceFile", func(b *testing.B) { bytes, err := ioutil.ReadFile("benchmark.toml") if err != nil { b.Fatal(err) } b.Run("struct", func(b *testing.B) { d := benchmarkDoc{} err := toml.Unmarshal(bytes, &d) if err != nil { panic(err) } b.ReportAllocs() b.ResetTimer() var out []byte for i := 0; i < b.N; i++ { out, err = marshal(d) if err != nil { panic(err) } } b.SetBytes(int64(len(out))) }) b.Run("map", func(b *testing.B) { d := map[string]interface{}{} err := toml.Unmarshal(bytes, &d) if err != nil { panic(err) } b.ReportAllocs() b.ResetTimer() var out []byte for i := 0; i < b.N; i++ { out, err = marshal(d) if err != nil { panic(err) } } b.SetBytes(int64(len(out))) }) }) b.Run("HugoFrontMatter", func(b *testing.B) { d := map[string]interface{}{} err := toml.Unmarshal(hugoFrontMatterbytes, &d) if err != nil { panic(err) } b.ReportAllocs() b.ResetTimer() var out []byte for i := 0; i < b.N; i++ { out, err = marshal(d) if err != nil { panic(err) } } b.SetBytes(int64(len(out))) }) } type benchmarkDoc struct { Table struct { Key string Subtable struct { Key string } Inline struct { Name struct { First string Last string } Point struct { X int64 Y int64 } } } String struct { Basic struct { Basic string } Multiline struct { Key1 string Key2 string Key3 string Continued struct { Key1 string Key2 string Key3 string } } Literal struct { Winpath string Winpath2 string Quoted string Regex string Multiline struct { Regex2 string Lines string } } } Integer struct { Key1 int64 Key2 int64 Key3 int64 Key4 int64 Underscores struct { Key1 int64 Key2 int64 Key3 int64 } } Float struct { Fractional struct { Key1 float64 Key2 float64 Key3 float64 } Exponent struct { Key1 float64 Key2 float64 Key3 float64 } Both struct { Key float64 } Underscores struct { Key1 float64 Key2 float64 } } Boolean struct { True bool False bool } Datetime struct { Key1 time.Time Key2 time.Time Key3 time.Time } Array struct { Key1 []int64 Key2 []string Key3 [][]int64 // TODO: Key4 not supported by go-toml's Unmarshal Key4 []interface{} Key5 []int64 Key6 []int64 } Products []struct { Name string Sku int64 Color string } Fruit []struct { Name string Physical struct { Color string Shape string } Variety []struct { Name string } } } func TestUnmarshalReferenceFile(t *testing.T) { bytes, err := ioutil.ReadFile("benchmark.toml") require.NoError(t, err) d := benchmarkDoc{} err = toml.Unmarshal(bytes, &d) require.NoError(t, err) expected := benchmarkDoc{ Table: struct { Key string Subtable struct{ Key string } Inline struct { Name struct { First string Last string } Point struct { X int64 Y int64 } } }{ Key: "value", Subtable: struct{ Key string }{ Key: "another value", }, // note: x.y.z.w is purposefully missing Inline: struct { Name struct { First string Last string } Point struct { X int64 Y int64 } }{ Name: struct { First string Last string }{ First: "Tom", Last: "Preston-Werner", }, Point: struct { X int64 Y int64 }{ X: 1, Y: 2, }, }, }, String: struct { Basic struct{ Basic string } Multiline struct { Key1 string Key2 string Key3 string Continued struct { Key1 string Key2 string Key3 string } } Literal struct { Winpath string Winpath2 string Quoted string Regex string Multiline struct { Regex2 string Lines string } } }{ Basic: struct{ Basic string }{ Basic: "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF.", }, Multiline: struct { Key1 string Key2 string Key3 string Continued struct { Key1 string Key2 string Key3 string } }{ Key1: "One\nTwo", Key2: "One\nTwo", Key3: "One\nTwo", Continued: struct { Key1 string Key2 string Key3 string }{ Key1: `The quick brown fox jumps over the lazy dog.`, Key2: `The quick brown fox jumps over the lazy dog.`, Key3: `The quick brown fox jumps over the lazy dog.`, }, }, Literal: struct { Winpath string Winpath2 string Quoted string Regex string Multiline struct { Regex2 string Lines string } }{ Winpath: `C:\Users\nodejs\templates`, Winpath2: `\\ServerX\admin$\system32\`, Quoted: `Tom "Dubs" Preston-Werner`, Regex: `<\i\c*\s*>`, Multiline: struct { Regex2 string Lines string }{ Regex2: `I [dw]on't need \d{2} apples`, Lines: `The first newline is trimmed in raw strings. All other whitespace is preserved. `, }, }, }, Integer: struct { Key1 int64 Key2 int64 Key3 int64 Key4 int64 Underscores struct { Key1 int64 Key2 int64 Key3 int64 } }{ Key1: 99, Key2: 42, Key3: 0, Key4: -17, Underscores: struct { Key1 int64 Key2 int64 Key3 int64 }{ Key1: 1000, Key2: 5349221, Key3: 12345, }, }, Float: struct { Fractional struct { Key1 float64 Key2 float64 Key3 float64 } Exponent struct { Key1 float64 Key2 float64 Key3 float64 } Both struct{ Key float64 } Underscores struct { Key1 float64 Key2 float64 } }{ Fractional: struct { Key1 float64 Key2 float64 Key3 float64 }{ Key1: 1.0, Key2: 3.1415, Key3: -0.01, }, Exponent: struct { Key1 float64 Key2 float64 Key3 float64 }{ Key1: 5e+22, Key2: 1e6, Key3: -2e-2, }, Both: struct{ Key float64 }{ Key: 6.626e-34, }, Underscores: struct { Key1 float64 Key2 float64 }{ Key1: 9224617.445991228313, Key2: 1e100, }, }, Boolean: struct { True bool False bool }{ True: true, False: false, }, Datetime: struct { Key1 time.Time Key2 time.Time Key3 time.Time }{ Key1: time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC), Key2: time.Date(1979, 5, 27, 0, 32, 0, 0, time.FixedZone("", -7*3600)), Key3: time.Date(1979, 5, 27, 0, 32, 0, 999999000, time.FixedZone("", -7*3600)), }, Array: struct { Key1 []int64 Key2 []string Key3 [][]int64 Key4 []interface{} Key5 []int64 Key6 []int64 }{ Key1: []int64{1, 2, 3}, Key2: []string{"red", "yellow", "green"}, Key3: [][]int64{{1, 2}, {3, 4, 5}}, Key4: []interface{}{ []interface{}{int64(1), int64(2)}, []interface{}{"a", "b", "c"}, }, Key5: []int64{1, 2, 3}, Key6: []int64{1, 2}, }, Products: []struct { Name string Sku int64 Color string }{ { Name: "Hammer", Sku: 738594937, }, {}, { Name: "Nail", Sku: 284758393, Color: "gray", }, }, Fruit: []struct { Name string Physical struct { Color string Shape string } Variety []struct{ Name string } }{ { Name: "apple", Physical: struct { Color string Shape string }{ Color: "red", Shape: "round", }, Variety: []struct{ Name string }{ {Name: "red delicious"}, {Name: "granny smith"}, }, }, { Name: "banana", Variety: []struct{ Name string }{ {Name: "plantain"}, }, }, }, } require.Equal(t, expected, d) } var hugoFrontMatterbytes = []byte(` categories = ["Development", "VIM"] date = "2012-04-06" description = "spf13-vim is a cross platform distribution of vim plugins and resources for Vim." slug = "spf13-vim-3-0-release-and-new-website" tags = [".vimrc", "plugins", "spf13-vim", "vim"] title = "spf13-vim 3.0 release and new website" include_toc = true show_comments = false [[cascade]] background = "yosemite.jpg" [cascade._target] kind = "page" lang = "en" path = "/blog/**" [[cascade]] background = "goldenbridge.jpg" [cascade._target] kind = "section" `) go-toml-2.1.1/benchmark/testdata/000077500000000000000000000000001453566013500166445ustar00rootroot00000000000000go-toml-2.1.1/benchmark/testdata/canada.toml.gz000066400000000000000000020754161453566013500214060ustar00rootroot00000000000000`canada.tomlˎ(;$+ ŬOz!hj4*UߋᤛyΜս>0oۿ/_˿O??'[-Ԑj_r''QhQSۺh;!aϋIFG YiLOYo-m1${poV۬?SY>a)< He#;m)m"L"r/6mf \;R3:7s(k04@+>qy#XsagMaڜj3֒7b`$׌Ɋ/&hYF]t>Րe Ϟ?zϏxRګ#l`Z\QJ!1odhf -Ei!Óz_Ȯ1f\8:E5G="kT̾Շ0o3VOF)Yߵ[EXz\MY0;|vЅoz#뜢{ ٥3!g4E[6{[5p~f|ۯ1a^`>r^UVzբF/zi=iE[&pkk<'sS+zq?mc_SfuJ3@Xղ~bzibZ=c9GYΜ&s]'ƞu+=FD6PHWp,ux؋W,03А0xm}'ʊGg{lW8s65{l% Lv^z3OR~O@b8օ4;WCMi!.sQ/ِs\h?ۜy4l3S8.|OU(jXXX?|`` jSn[<o6wc[CSyfYZ}ڰ?wF/ոg߮]v A*C5Ӥ^\|̞dMi :M:ѡ_ =SZ (&L5_\ERti?o"iٱϼy&i.`XuM+@y<wVbrSv|ƚ"2,(Wbf}BN3{Oύs/"O\R@]k2@P껷ܼؒI[벭_n<ꮨ92K* 4CwTiqBս`5o2\y~11efeٙ/D:wdw0~@\[{to\NkpZ~`Yk OAiΥ}FC~:ƹ9jna=^S<7ͽ}u$~I9ޓNm`*n}/M7汐+Vs._ `^2)r9ݞ=bǸ"G^&j6ۼE=1OfIrxTa8|_/_^038dw$_m''>&#ckVտs짓=N@2Ηf&&nƕ[/?4"eh"wqn9Wdlgּ^ վNnɅL8`dѫynxoIou P7dԳޓ0r# ^@$0Nΰco{40!߀ F  JϟG B6yEfZɐ%Eu5V 8_}wpV0 e<vH8W3.`ׯ&M8aVm+ђYu-^!mN+%S&O]{Fu :xFhJ4uT t3mmo Ϟh<02Е%y+g^/y8q9;YqK PD`@8 x s#ցp;;GnvÆ3w_ivhf:z; ͝&r͉%:ھڱȲdƏ h@"l??H7 'Lic8b@şÔgS1Hͱ˥1=Wf-W7K@y6Msn[Wi@Ƥlg/T?jiR˺]k['mVhXfrGE>ax>| i ѫ Wu}jξ9 41{;d4=+3{R,*G30ZT:E]_Av ./5؎l~QVMf1-NqfH$hD$apCߚ 0,*yo](kv5Y|:}Z7sߦJ dfW=[kWa}ܗA>&g" l9Y݌Ufeo~<=]79e5 1t7.4Ԅ͖aw9Xm,v|[ojqYV,)>)@?fNR;;'b.eQuK6 #l:6܁_kN^ٸ-}Fd`&Nm7=!;eXjrfnZX#[us|n-zjC&E5(K>$w\gt#^۬q[")Ӛ?!]" %^GJ@L:斴^ .;|f =NW OݦI(rDR5.tOdȶ,ߋΨ\hי; .O'[kBeQQ!2E˅a7*]xei+~ԏVq'욇OخkgLNý<:gwfRh*_vha5W>Fe}p5,zq~= s.czm)l Wk9\u8iiYiwDV(f{Ǟbܜ>(o"p~Au/jQ*VU!k7Ќ p=|BdP؊KeOc. >v/` sxOܐ[G1t8]oe‘{u=u>4￑$ ot߅?&̻ h 5фN}}v{4۩y_ :>BH }fXhb]'Ey\@% xffM/+!DkssH̩ 5U!TUQ} S~:@J@R?#*wU<6J$",4:o& ȳ9߮+\v5߇N?Z%s5+m4.U9+kc$9s_% ZY+2sڞ}O j'=~bIMve6NĽ}}Ԗ3(SB#Yl!#JPos>2j:9͛~X"J&R,Fvn) gdT{nj Jf~_ʐ{dK?*'IsGʐ3{DF##E<6\`~D9))(q'w!ϙ@Nh~=}F"e黦;*"զljFVSWwEZ CQq|dM{пobOB POhCI=H Ʊ oj2.Zyh;um7JwuM`oQbHH"9^UE0Ѻ*:pڱ뉜[SBţ$sL`"@W@7>_蔍RG+sJ۵6IY95ͿebbϦS t5@p J5)AճIQ<=a[+[#!r)/[a55dG&Dnw%~\޾꘩yiW;r'?B"r\fꜣ?S.ŋ 9U$c ˞=%rm V[#tmB>v.ɂ>Z:&-X||Hp8<]jG5&=58@c<))gz'@Q|Ör=D +Tz$;!^[iX6 2Ul SS\`MK5Iryڭs)?1UH?juG@BٵEh]--zG*AVvOvВGM>ys,~jhO?|.sDXkW" 3!ޔPt2-PkIf7طF?_8.<ig讲5CA!~&A ٮb(Pe+VT(Ϛ&+DvRW`%#;xx~t~?~hsdYOo[)ζ}:t(o^A#q`'*L&HRhDl='Lٛ~ PԬ%.Z4 KS:@qfOdZ?E,=#i &*9o$}O* i3# >ȡ{CL}7hI+ה6$,s}=XiՋ0n|ztҧQm Hz}ѯ8htƑsfS6S? /^S-#hUx{f>{2 #&;h{d6 Ε,Sbo6џ_2a LLiΓ5 'I4 \iXNR*_ =FN#x RV(VrXW"Br8k;?63wz e3\Nـ-W:=9)Z! FY37vL(>TZ)S_=ȃR5Ko>`q#N~_*?)^`ʶ*&b_>A <9MwWme9LelPz JPk) suddSv!]c#HrBI3f,&]aBQGR\_uS0Yz/,]W-jQa|\ӮMf++@+e[yS"r}L#|6JyjEkaZn H9k- u+Bxs}3t\ud;>⥗@\x rP G;"85*"U(i4mߖ% ?RR&O58@*:w'@wc{NZdkl+wgU0t%IÐjiP͕`ȳp{=ϗb4*g1OS5,N/zB?T/~ڸsh@<ԋ̷=h ˊKyPŹ<9]˯||yX0w*GDXxR*o>w^ɫ 6 h]2䕣hi`XGYyLhHex5mCT roѵɢkwm X w#'ac ]4S¡xهSHV 7yjҵI-DsX=j]o<|6V@Nn,yuR(!KY|nӕLCOs km{:S ;Zdވ}#Z6@k-%C^^'u[}4M>n>n|Ro6e9iRt("dI,xLv*&&*ﺷ $PL)y_9J?+i @nql w (FWre4P1!9UmpYI^I@V)$%RCV<8d"U\rZfO[EQ'fNw;lҚGf?5.Ilq@Э'"XߊD:oEy]u.yn$/-CCO\.M' ѷy:"0dĺq)SO^Qǀ'i{Oצ_P-uβ2 #`;Lp:?jJ~60t._;~2l.\ؼ&IlVh&mžqv| '52R]ܴEUH\11><%p)g9Mzg";﬇86B롇, t׋ O5Ǚ&{:7kpW JY%^D{OhI !\ b9 xߚ&GX9bM,]`2k%kr5/H{Քj`=Ph>_ \ީ#~'pFqMCɈ%@Jt `?;l}<*%Imdfi{͞gә27KW [>J<=;PZsZ2 "׷(90{Q + oQwG2Fh3z ^mM(mtF| dӡe҉-go&zIO FWkl/rFC!2Ilg 8_53v[??8RDGij[|R#PA%N)Aq1eQμL`${Ԟq=w0" /˗2?-U *r\Ly dS,k0"M|,N#5Rb}Ii:`L<^~|G+3(S29i̷U%IHnOޮn^$]KqwRTG@GݙI:SZiBTFG+@!6hN/ 9^!p:& st1ndC)R4U d*.meyGNM{$Ұ[k)Wɻ RSIţ˖Lo%zvd JMqwtY/dH8_ݽƺG(hsv/*ѷj8\ʎİH+؇9( mG> ]}6jy$]5$70Q?@CH[x]BסDoEѐk1֊\}@O"mǡ#]t4A؋AV܁}R;D5mh>"3gf_bO^u~?I+E^NErFFkmzJb2FгZeEozL Ej"Eݏ_`yS& Դ&Ѧ;(P ~gy eY6 A5Rs e7QJ'J~e#_N}@.qMz՜|PMlcXT.XgaWm#T'5_]^I 'QGFϫUҩV1ԍ@Rf onb@_?ŎdȰ3޻>CF!N"zRs7sy xf|Mk._Nں%MIJ y^Q[ޒ -Dc )/X?@s/bU"Dr&Hx:unyA()HMD+U*W~hA)foVA!$zc~h}HhI:2Od#{~<4ro'>B{h=8Vhp Z% P=WHPKew<('SF}߇~lH 4 ?}]6FcdxzO*{a;' 0<ww4z\]*\7 K6u;mצ<0pc)yfGy"*_0/Djύ$!;of7EZyӫC/]]'"* c}/l֍BSEM"y)xgsН>_uQ`J2WZ-(NW 4|r^M ϰ:! ,.lă  VlҬ>R! {@x4XALIJ cv8"}IQ۠]O7fſBP@YX#&Yj>@"I ҝVBk=F"Ɯ FP/Es N(IK41Lj얆,x D^ƊWV¬K UM6 2._Ȑm'Qu6ol֋dJE(:.N))U !Z]ωg(JLA"kD=K|83$7qr2( d6ˠ(von & LT@/ \V0{%" 9\7ĉ," EVv\ew"h]v'ΓSer6E=:V?IR/H ~'ti文*M"QA\{%̈́^I+ COg* &ָ}M(S'b[OW3tBü?(Y 97Ȓ=_(H)|=ݎ~RS*KaͩY> PJۛ@Ni.Nb( mVE$?7/91?onH8#Nj(iim7x'y}ZC*[+4IkE^T!<2^ i%nzb;ՉTO(s 12gH|V[|#D~/0gNҶ['qH"aͨ}?N-gՖz'BvNIM(Jc?? t%%Wq S +ׇ<Ͼ s/yK ySk햣ǞULQK|='08vrvچ~-޿YjB_.klp7Ir;wI&z,4YA'>4Ur\A{T$Dz/]VXZ2D"R㽛͚;AVC?#%̏H4X i|krq UW||I]%ajuy-Es,cA*ӈ($%N?*rfNTz&u;^tUdJtuhTm{$0>^uA@A{Bꞃl m!kJۢgTxO @ƽ\~YN,;yuDmX1cEnWbv:<ѣR6wġj1+7Cs W2x=s5#pi_㭌*>Ǵ ۧR͜Tӂm:,n%s#@F l?Dqs=!O Ȥ#YyɻziJ {[0FN+RcNqUwl$X_<1)H N,dA˺&+J-Xh2<jI|aƶHR@t PT85 hr3ԫTCٷaI +辚 ?\lGÄ9uzhÊ[<\kg1!8m각eO?*Vo@Xψm՞_!Π32)#"Hݑ)ʚxֽE,:(7ךWۃ_Gg2֏+tj̙WЉO"{=b]US)~Rh| >gecᾸڵL sx_h%DX+z/.| | I~!cn&"E>$ɷ\G~ϛOc,OlnBt%^zUQ"Hx/kmkGЖY;J9mC[? ?6ɉɲE%0M-Aȭ)y^{kZOyȹ̯Vd]CmQcD~Y6[N艭ːjXkCahcмJyJ DKSm!Tpy+ ImeH8Գ5zulYfKoy'X?~d3?!aX뉁LB֖;I'Z7i^p#J8)1^wl&k,RONDJSR]T}9"nUkQjutGmڽSECyi!)%~Q0Β N*darr (8iUNw֋"%]z"HьDBn}F^D ."22]\p3{Ξ88i0)by^"}t"ST"{F9 ׳uxyA}K[Edۼ%BlŊEO(ȻP "0 O:d P m{~^ޗ= baI߳ۤM, ~c< LWVjMxLJsEbk2KM'^|D Q )ȃ~WIZB#r}rnQWsl;RXM+}^OOn)K f>M経Au#2 }._|qDisTl>òзDC!lߛ M V,G+hШeIIxpL$Yk&%_kK[;vkslIJu7,ldQa-XR!0/weʔ05!Y Xju=aS"_z}K_*3@tWnhAzPqG}%TvnBבx* UNuOr.*A+:;8oypJZ HNɰހF4"juy{(u^>_:AZLV7]a&emfs,9Tŗ> i?](=ZDfMԉ=X ,7O@F{|!$9ODxRim9& Kn/%+!HO9<#qfiSȦ] ǜ} f4XCCv@cUT*nSI؆ V~yd7n ^)17쮃b;BG!Y_h wێz]6r$E-YDА(jav]ш1fDy7(jzX+^j-an`G[ ޤx̤ =}+{!̃FD&vo7:mb0qjl/:bbUXkxkK d;׭77(X_=|@{7ln[SJ%Th{/ }h3PNt˜"붗INJ),Q)ٱ mBoV&VZ/! KC fTxxu)8DfIDE;xi$kR9C]S[bGHt$$/4E"h=k &=㫩)4D!8#)!1v,P>$MI2Awo+]r^+WHЄ{$gw.sB־CS\ j@z} QL^ړzӕ1ɞR#ƾQ`~yD~ 3O,$~6 8$"$82y+mv^6@\ePm'/=]t|Nd+rTd>VM^))HG?n9מv99J4h =ör"ge܋;eqnѲ'A`X}~P }|/r|$ 8(-Ⱦ(q>3>c@O~:nvRl6s+#/k89oħOt5k}֏u=`\9}JN6²׎`Ħ'~é5P3{H)w>ƙ|+_80&nbDM/ %`N( 4H1rN/(N5IY $sٷq}<<%g0]e򃐞5V2w)u֑w/HAr'7jsdg>&q\eՒē͢d/qJL{q}_2žq+yT(2"gvN}yAzmӈ嶐u7 %#}AքHyzqnW!υ9)/(Wє^o P sN 'ƍ􈁮%J93}#FB6W"M~At"(M*4 `b7z[ʮI:?Rj i-L,]<ȴW )lau= sk?ۏ@7u8e'[D=vHTDW!z ]%@O<ƶ{B&,Q4(ڂڮH,Atw/x1':} Q3B˟\sњ{=|BuM"[[Vyk QT-ّ 6w !ynGu달ՀͭL/$El˵2;괟8ة6R-ߋ,'eݑ!^-paz\jAEfw[O2Ze?]-8?Nr/4Qr}YM+cP|3+ȴ C]po_˥p傖 trF14d2Wҩ$ZX~i jϥEwW)L|pnJd%gNkTWa!oQ'd[m(Ct͵U~iOȈm7 qb+6\[m&'-lNeanS xA\Øn|Ubg_w)_RL(ߓi MÐ'cjc?B:p[w^8 IlPHOE//2xȲs5 2ؑ얻F2$9P" Xr$^\!Q0~ҹpz%NR`- cx6aPw|h\,gt/Ά|s >Op?j O9@2]2w(}_ߔTD9mT su2Pތ5*Xk,zʠ )mT*苸zK;O*M!̝d6̟Vl{7 . M Zʹ;9iX--8qNsfCeP*tAZmលO,OL}-bVbZ\X\y.F L 7ڒ6oaAHsMR<az\9֨l+m菝Fr\x5J0t%wE.sk [VLJ_M$5JN9<O(UR. mHsN<6{n1xF݌[g5а |$!95&κ%T_h}Bqugq1{&W*~|_Rk=̊3_]<? e|.4%)- NLcC(x̍xE.|,Τ@X E @07,(:.+%'$V6%TS׋(}3_${~䣀!hVC» P?䃂xr9G('`v NveYWLcە+jT"vrM7%J݅lt4$pgPEŅj#y$b-IBn"{$"k11j44)+p^Xfדwfs/=3-H*Qs~X3_XmBeҪQsscDd ~'Ͷ GҴM^(@uVj3zq0= >i C)=RάN+SW?Ù(fyibD o$ Bm~#d{=RzJks-%:==z,\ۛx&$;%*芒_]1؆.w&i r8 ߙ$U5H8x>T sAc )R#G-*8(^Gt#/CӧCnN̒/R_oj7jSґ^x_nWo19i@x|=(r UA^L/Cp$(c񃛷T|4quDi!yt 8\WgOI@"7"oGD;Y.%ws'[Mu]O/DWMi8\S^Eel|Sh%|W]YUN9s[Ϫ;R:(jN5Jd(O@G}5n~sfS2Wz i7HtVRNZS;IAI.9NHգNZ2  y 1^g`&@N nf$-=xӗ>D]mdnZ80:tS>Qf' DjaH\%vє xOط⌔N6C ,?1,$g睪!t "В{Hw6 6B4<Vlh8Ս@;F]-쯂Gw8FHͼ1_8j&tM8f)Ut$@"ͩk 3 2RoFO$(z֧0t|> $ d9.Pi:(\ydj>tsi8QѴI|eZ9pUZaQtyN$Rb/b7F-rbvn0b>-s$Th@U6:_I*.aer 9[]Dz'ZvG h ZL8&A Kq?ʝDRTHxu{._~c(m :; Ik5^cJ@5&Gw(n}R"+&z2;@GJq-v0M OIV?&>fژuϐsI@HJ/!˵Wm!;FX@4ƯHnLWsjJH|N#緯|z9 :Jl 7͹8 C 4w fBaHloP=ճrDnr𤶆pc/2Pe$!$9Sj_*y}fCRMy V`"37ΈL[3cwO U3Y7]90<"mbCCR:!Uq"1q .%м@Q)EtHgEE/5|2nĉ\uvo $<_<8IT87H8_|j-'5|&x7~}tSO!1hXƼ ~V6ͣjE5*J1T{3YU(|vjw":c,nwTd:^* D>>xY: S(K^zb]p_5#s<#TMʼn}5[1(C]Cq -[xIGao!'ǝ^4F%R5G,ھـ?p r98.NJ mN|$Wg-XΈ&µ%PF1]l*p-%O{#•9ʪ9ǁUU pRW8.[Xn3"{U拪 Ѿ؅_DЈ_~* Bkǒ2oDɼg)4m$0z#78 /Y.HԼA}q}җܚ$OZ:# 55 $jaOKH{}9HˎFB ](пd"G \º_)KL7AYWD38uG7خb\y*'ٺ>|pY7N@H2ML6k?MkcbT > a U~Ra5FD\rbn,LA =-e#%H.,LCJ-sG><_~jxhcƥK>(=\f|d5EvuPO r48YM]/$r {@J0 Q^z_L/ip.7z%!NՎЙ BE9I}Aqusy k1$/2z"xѪ~"U!03-y'U9]}R #d#FCR$PB&)X9\qnwmRf ׳WV4(RĉDKH)_XjJV$]^evu?}H5P;/Cv8W%6ٓZĈs,>)e$ݦl2?o3R8t'Æ@ ܚpP? mD6_ E?HK>^ʊN^VIzwI"2$\2Aw B;hM8<99vFܚ ͊QWBӑ$7@(pi&Np&߫["7 d3r9 ~+-Ÿe.y?* 7 R!cVNhR~]*V!Y$45?6}Q vb5%R<-\LD̆v2&zZ}+c/^7C^=2u@T?1A/HlX3HToɐzB_sIP\|' ASOK*fhSx[ñj^Z];,SAG³u] ^S:K%^ VfBnJ5% v*ߖ?l.:aӾ "{g<^fI V` 8[89&Ѭk W0M,C?UV_NxΛc[N ԗWsÐ_ d? ;S~W 9O>2M!XmuO `s rP 3m2b0nH5zJ@ ^3tMVe ?ᩣ$) ckfҨLrI"ɿC.s--urOLMvB4yI]l@oϼޚcJC],Nm€أ{lS}2~HZPI+0Xs)2:B| -ԕQ! MdWܤ&u 1"޴'kX@r?t;Yp;xAptNdw=[ t:_H |$%6̞Ii=:9;hNMzFMi#%2#Q0&MEY2rJAjIrKU]0٩5)!a%/&7,#A ZfZ"IɐKDF &(n.CSEO ۇasڋM7gX'6E]6썤)/0m Ts* W]? ϰ[٭B7+,:KY3P[7Ew*Nt ,`O@%dQ1k">'i[OEi_͘}1˽Am柖ӡ4yI@_w mk)Sc< clQBD]O ⇘r0M̶U {t3\RQ& }$Eη/G% 7tz1Ӹq#0ղwٶ "?$`dt@)_*օlwj0J6X.h\=\ \.` I<ߋ jVdli/52fDIwa@ %Qwx:ϚĚBI]?-!T%j y[ Bm=ў쐴g 6I+_]9=Ed5:(ET)(?rXՓG_H9jH|HW6Z;9[H2y/q"dj~s-yv#hd5!mi#byA~_Ί1 u"h=8JiGֱ̨o( ys$ZrK(ۀN*斤U.. āW J0ZrN`xpI.wZd"pe 8\, H6Uf@'˹j:}¯eަKe#Uqm"PP/p<9{/72fBlkW>9͐r|w\l4Scp*u 2VPL)͒LAٯY?Xn_tv $FF f%QX:V,z-;S`opծy&z^usK(mO CB(s=qT"}Df"`z¦ً'Pƾ^?ڙm:~B´>q-.¶aڬ4#,Cб$yE $vYYgo@ʄ4X=ʚ/ߛ72z{a<HZaS(RxePj!+^.ӨG{ sjKDQgN$(CwbD°Q)ITVy$uPF E}1>׷'{e30%g㓴c60&pP:ȣN9 -_NW/f8-i tHhG)8>"f:lC(ЮD-uMق`g.봍SǤ5Qe1`4݅5}7*r^>ptv6׬é*Hlǭ y5:f-cM;l@X}pPT @:Ōrvr CJ j@F0McR9D#lcVaq?T-IV  IwK>Ce:O#&B*0n5o޽ǤA>:V y%ꊻ2汀 }8Im7$5=]!UsD}&z1f8 ZAfA&KbkKEiΒ@A̩2M@"Cx5퉔n3`P|"iΑ(;W?8^A< t\c򠰮6|\H#pe >{K9l ոci 1#;y}N=SttdB2E ~? HЖs͋8ՆU_MHSPꐇNBlR'G=q(5myA,fn@p*v9ճP'@vL5qzbxYb&30qRm5\Sk؏{TdWCamg j~>"LW-ݻA;_[/Wv yP8lS+U QwʟvBK&2s }[2o'W7suߡl51ђmv cL{߲+ZȾyȽ22(uf$qT_5¼à~~/z:/a>Оө:%_!~R{% >,ӄMYe\Δ'9P@"'2^n܈^zpyuٴþz6 T "~\}fMҪުI5As3U+ PtB.xJ&e1nD|,n^ȆW"|8%e6.H(+"~_Ȉ =k-M1/EnFG M~/\8)$\ @RzD>xwZH_oƨf͊7`O?û;95JMڟ{8wE&֐z=o`RMqEz&kP X3H ܡ&)̉5@q2V{DrhVY*{}{ f 1c*Qs@$Ly/A(HlÂh+Aiw  HH购37~ߕ|`ui)Upf}C材9""AtvH G0\E iȖT+ Id45G'phOo6έT?KDHLpGdJ~[rgJʙׄ pIX\ѯ _1r}" k#Ͱo!%TED<7ExY@@sMt`ÚK]T["Zl|P&Eȗ:b9H>V1Dy#G`}cz*xDQaR&aޛ;'%s~#*NMwEv0=Nt6p'ս/ @R& )P!gdݒ{ G](HNsJ,W[(L.-S60l7Ug2|#p#2PetDC ק 'p Z%} ꬾIȺ^1AҤHAeM \zX 7?$s` h@]qYſw6'㕴Vuk$Vcgc%e(H5bJO>Y-%;SCLTot׽YAg6ezRrmx΢q rՋH\[rA]SuVY]d_ \r7GۉKX׆K_ wnxd <+eȦ9\\=mK[pz Ťw`1 UE=u":0vNm@ w];ʏ0Go&J_MvyցHY.EԐP iIlh{BhZ}ȭM#jWa}eT-9 i#B߳vtcge:|JO>7r&VmͼvT*JHfS~]kG;89H<[+6mWszKЛZv߀$n`>}}<+pB6R\}ȬL!r \ozpJkX,`R|>-4d/Z-vJɆٲֲ!*-QH=4Im7)B~V?9PE@EP9luY/JT&eh+Cc.YԅR@|&46 Q'SBxZ♥4A bgV` 0G(EVL8Cb5 X( 'PHq5?+`{#j@&"{4Ba8(H1$3>s[`OC&R(E6s[}h1!oYdqoUӭE-|=cfEy9.BAFu)@Ǡ芣Tu 'pV=c5l.8.swy ?֦º9\Ljl cj)Ӫ,8 a1;ADѹ^g~b@:ԥRY%YPtCᎱҘgޞt ;YlB_AMQ L3F|9$Q}6rά;фtDJL Iwh^2Bx'岥H ^9"PM})tfAkAP MdUv9,L?pӼm+GW*pQ s!}&xGc-^֋p6 q-4tD`7p;!-]m^ $_ wPX/QrOS"Q)\Z~&dT=j&)IU- I̼3D`j| +i=K9WvJQL z8lKY3g31#+BN2O,e9*87/,n!j8&Hjs> rqwh}((PJ+Ƹ^kųgv;F8uήLqg7,jp}_cLNUq=2 Hk3:}dzQb|!Nq!\T^7nv|rRI(8CuĬjկ"eҮ. !+;%ٲ][lsx.WNы߫y& BKB5?Ydۡ`q0I0d*o5 +%{rlHWh[C33@L+8${/My .NJxNEW\D,l+Ҫ0-YGmm||05KaU"5LݏKm7wkđG_v\`oU_۲;m%X (^0X̛^]61\Ŵ U,N^=C= O׀BPsWULrlت҇W2(N.)K{y;݁úd#*։;,eyE%+bqY'7 浡#o '=\OkV]TPʳj'*SH1a3 EFJ,W,h_Q,OђFZ=ԭ_Qȉ[y|6 NTR2p{z>ȅJc${ȟg:b{*h|"Ѵ95 IF2{v EPS_c kNŰr/A;6sI*[N4 p~ 6!?,#eZ`%ӀE2ԂI\GȺa ZONKwܔ@8k|<W,S8MwLd:JJeQ ׁ阕KbUnMoݧzK(Z Ϋ) {w[z^!-ĄWyޖ7v.BU޳o}8&?𮹿>F¶R3ӮXgTK>!8$nD7d򚶘V#*dԲ5Õ:$Y`: x7~+jO(n$+ hϢA[Jkz}dCR*^ 15x+"LdF[zB':[") ^^)՚D!v׳h72u Au/K$+}vDj;|<>G6bVkQ_)H.y]q a"*HL+|f>D^jrgg?t~h.@q[#m(|Bڜ67e%Ҫ͘1c>Y@:Gz>7+TlęΣ5sv!EY"/|wwxFE]5P͝]$hߑc ccϢ")ŶWtݿH!l0.Döe;v}v/gx{ ;jn%,p9qUQm6RYT*a3s/D(oփKSЧ Ml t䱎X!e}k@`[iD,CockpEtUG*~[>sl# yu[A2-(p*kd[=6j{C*-09ӶtLq):Ӗ^) uѵx{amݥ0Fֳ&:·u}%vxbǑ S׊,APT{C7ٮf=~nBɱvӑ9zyچި%O}3kb?"UF"逸Kr:Q{L2@~%`0&ڪk!6P6t}T:LB =ΊBk%ƥzt|< P<}6[}V.^Τu{C.*&뒃}l6&g%n@uXwE~Pgθv5\CRfxEǛؾ>u{W<;C닩ci57%nB錾fc!}-xUq?zIr慼_Z~6 daHT/m%-BM;rI揳@KE])Ǥ)u|,Ig(sXja=l^CH'6mencz^܅I]ũSݽ Mv&[_Ύ,v.a:Qpi#ƨՓ]2"dۢ"!Q~ s+*Lq& Т9A[yfERge2|b@~ݝȭ=p1-æz'*#rm)2ETЪ"xD.̈RktCAu@EmZUWhw=~!2ªgY,XPMI$*CNRA-UF2%&$!w @eRC^u! HQ1Kg8ly[VY\e@ZKyސ޳o -qBvmvd)nZ$~DJv(}9,DCf@ʱd#v1Btxp(%Bڋ a ּ[J2Nɀ?? dvnÔ#n> 'ҭ &#krwf]H Ca{_ϼ ;Lt 0L^1']Cޞ21v 0dWKyy۴"3y.VyMܝ@+J>ݞ]|28@zTK rNfgb+`Vs8m5?fzx>l&G@S<C] [^AtvG`Ә^cVԋw;*Ҷ;,ҏ¥j]?X[Ju\,+_/iG!)?>,/Wo! 3qM̱5/G>jS/*gQȾ[~{&+R $v{B&5A(6V j8 {שXY:dx(`sf%Z_8gzTܾ*@2U63 EHcH䑞 u8PZȸ+N.0t΍bJK (u~pqI4S\"Qr|q}ז`2}K! ޷K.5xz$/F^<ۉ\-rN>tx%%J0Fiu& f*9=u1"\G} D[Jr˕uQYkqxyO<IV󬑠Jqb 6ӜNa(:m[ H=deΎ,$SL2B@wV`B㨩8ٽP̂ W eJōDG<7 8eT޼ 4=%vyU\)\D#~׮g QN^;Lӽ RyI('}'tEe\($CdY?DEh1ʫ vXqa73!6-/V\+.?FqF6{f2ɻ@reܼ*>?)Ӈ xr^BJs3Dr@Aԯ] R !Ώ#̭v$Bs҃xtW۳UqT;Hs"Yj aExDMnreEW 쐖)q*#-QsT0 OtmS\8o.dtt[PMt3)NTԿMU^A&en :WT+I³l6SjZ.7Q($?7|t0[}H)+%Lff&ߎL sSh 8MVu.X6PGlw'X R bgNk^s ].8 _gZB̧Ox&|o==j|W'Y:[ .\ȩɻx^t&e"$b\'~:4š8Lr>Z]3$[ Qou S\rRʹ2Nd*g' eWDP:g]D>;"?Ȯ?XR%YP1I_R8,`-hWؙY.4+W7.Ⴜ+ D6 ۓxx6q"{f1S 1'Sy]̏gfTBݬi՚4̝:H̡͹0նY9R!Zߒ %TТ ŶՁU6)¶LfQ}X'iRi~< xC!9_b_ہ@@?S%Y@ŧWOK# N(`ނlOˍ;_sBVoN$F@D"9*u,)0zևL ވ ]7ޤKiGZfKa>~h1j r࠻o=-!f9mcߤڢ,p f6t3D?0צhwkr[~?:Lu3D9T|l >dYҟH:IF(D$[+6Waj* M磢 X piYӜHɮ늝sl / Gv0PEh(O]>n,fs03!;> 1.Ƌc/L^lͮnXnANF?dD4NR ~ПeQ=}A%"~ xiuw$"mpQqbP#ٴZQ7>ճ b=z| 5j~ /؃F'nH]lӎE\RX#UDmIl+JsNCx {)m''ε{æU3Y`MX5tBgjWG8 `E &Hf09ٿJ5iw|,9[N=X’@7֡Sl~A_ h OtUʯz;A^$N8莵Ώ9@n-|ؓy`<;tlf 1'`[~^A]2/|5!IRrX@gm &Cem>X̐kxǫvvƇYLLlׅ8lM=&1oV/8iTS]a5ݕ@D!ePNE&) έ6|?;jړIeHE6Ll!+zgpZK-QvsW|TH`]!#+s0~C- ģ-EhE~BM-_feܱ#9Iݮ{z.(HOAf%ꇯ!e~%9PE )}Oqm'ZIY3e9v'װh(9jG<\)/&r\{S_^@Q'wB tf)`: x]~2⠻Vhvh ,DqoZ$ $O,޵K<`͑қ9:R+C92u$J/v{YlF~ obi~ ɭ+/~>`J){z{90<LG^dL|LFu ~uڳT x.Ȳ$p b$͊HӖ>]bjgfZVO9$|1q&h kd Y3jfWz" 5SW,@6 I;2&Iߔa\#k% %PjxDӝԑYSWqsJDEt)Ϟ繉qOD%zt:D':'𑉟N<,jb}23/*,{AJ\UfG Vlst-|UlN},Ʒ@9 su?F#H|-$lga&In=kRڷ-rdϕݑu-)Z˅ND\zq=ܹ@F`s_ӛiGzAWk:ޮm n0FV8-a˻ȖEGo\FRxDvePH25{Zľ@G{›SlN7N娱yq,g[Š`@[t 8Hր* f%C>jdm˞q.xT(?˦c:*hoe:xhwNAP0mO3؁0o: :,,tiBB]L&Ȩm@ʄFٶlz RS>.Y;Bqu61&kn߬Yb~O &IG@n(\؇Z̭?m"٬֊A1WoR=X[(_s|!-Mp$N0FdrI6SFb?%fFKtܐp[n)2]@ܯND^6 [`|WJ)=>Prd3XהHZU#!3WufdE3luo@z}=Xj4XG {9`'4($'x6Ewjۼ,BU@")<yȲxJG7(g Dvg!ÐsJ+/Q&yd\UO廜W^6 CFrS ('%zHf}vhii&hz#|OQwk~ݿ-NBE&76ZYGn;o&m涥E ّjhg߮ D QU|a%J9R۹$%?Geѩ}Ѐdi+T.䔭79@i%zgJKzHo 7"DHRr.LaL;n~D:;GQ^ D}uE9jE݁z}}8ψZIH!Z a_K`VDhu&UY[Uۃ>xPʗ^L.4:XB?f#ݢ#5ɉ(H$B":v#ڋ7z L$dzapq>V٧F!hN -D"-kd`X+ 깿dn X"$ANh[P%%ؤP*SUX_$W:;qiLB\x"GQ,;1|~Κ}S9)5Fꅵq#/_MΫH 5fc@K5;)gLr Eո~ܘ]o헸D? GN , ȭ)H6r2DT85 27UݴswBVi=Y;yAynTMn,kȓ>@JJtģ]DP BlFP=nSA{sypak>CrE^rv}R{cOpvO'.[}zVH>vԡ2i1u%H).Ps rb P@_ؐStDT1f6(JG5##q@Ѝ \^ǘeAU5k2 t0(v7V콺Yvq̉9&.Q t;)V\kAl,c5D1HCV X (VN03dXk)r&o? ݮI~gb̓C4t+"(VF?XA"]um!Ɉ>a;B9wXֱusjZ"PuC2.t}j'E4>qVyN(";FD4O:V-.L^/;rXk4*Qș,>2]nӼPfs\Pp)\`cٱ?eǾU,HD@pdmj)/Ry;1mYvQ=pReZ~R )0]ǾaclI&+oidw"=/49BEzOQjI{>V١@l/&Bva&xm|8ާ(}T0ւ0  e w$ ysba(vR9LCh|Stn>"QpI+"E? ӶZDHڷ :XzN$ Ċ Pg>qvG,&$Β6mĈsQ!D]/TvFتP!I Dݠ̌Gl0yȭMr\tJW=zL]c@VtāLF[l3^($ُ>a$Kzɟ&%2d91 든OG*921.xYF5-:`lj5j \aw6 YuT@uoKOr^c,aYi6~&n۹j蟂O>Z*|ӯ1v3]n'$i~$)tiNpQr 9A"]x+yƌPq ٴX ZFw 4YE)Sde'6ʱ5D%O,C·٩@z"lM>*y}[f8(G̏H*En` .V!{`w|VR"RnpmZ>O`d:AX8씹o%4~0fwɉ#ms-\Gs0] 1lŔ[n!;"|WGma)fЋUA$j3m,2u,Ԝ<>s?1cCoϐ|.gR?9d7)U]}FNJuѶ*ElP<"/{.D3 &6{y0byI۩&4y7:ú /?N*72 :j%X#i7.G\]ӷv.|ntCʓ%!C"ͥy=_$jc_q<F"A32'MJx"zmrI #ÚUxLlڽJ*׫-oW5YR QVO4 r(k"5By$AbYEXn3mn_~DKzP66y'N3C/HpqÖIJ+4 ;[ sZ#WY^;CݵׇZM ,ؒ YzȊ #ь s{5P ތ^O*@ GAe 5IrYh,_PBԟ/ -wN^_D:JEtNyg%Ww?~.5*eQy !߮gFGg;8YƩmrLxYu| A =&\8{+7P:9OM,mUuZ@{,_'$GUpZqӆ`[jnHg| ZYe?F>C pq{7vKHy] \NsbNh8k:?)qaa.N·M,q]Eq՘Bw"1\W_9|l~2ȟSNkΣφDS#3c~}-zp p~ɑ?ڡxS>`qRY!WL߀xH)Xԇa.uAhE_0!quDP}Ӄ8}X'W"tHkMC8>~&]E9tIb 1R NpŲ'9*{+Cd}E_k4VC/!l@1#ު$\H8u)#;yK'h|AB@ʡ UbtĒC ̑^@^%ziyZAi$nsLmQ=.Fn4܊XOw. W'c[ D@0G.\ljo/m?ogZUH+ۡ])S9 2 J#tcO?PMϯ޾)}GЇ\{f^!s>ZQƦ] 9fO?9WwC7o`l=RΡ=;)$EI9&|9\Y&.#"O #ey1 GTgWx7l:G e_ϭi۲ zv|,Ul2\X?#k~j&.&='S-7YImSwPKF,BXneXө{b/0@5&T5\]>tG '=ޞ$`VJGVw+,N_T@{L) :Az9ZE xe@b4Ƶc: 3֕ll"US\W*]@@㡴P\xxDko5b}=+'U]N…d<6@L!Qډem=2v!2 @J` 7kT^鹳 =uDuR5p78w^B"PQn 'HRI4s3KHW"ә986 -gt$-skSmiz.6P<\L+ d a+ R{{pl`4M+{cND.Qo5kRT^~@"g+U=}iIij$`h$H$gm`o:ܱB=spD7zp~D/>^l#WN[&#kbJ7oD(Q6>;orci-h"fW9цfc\DoA: 8Mi^֊BJ9zڤY'}lyV:{L~9e]xǮnJƗ6퀠CP hA.IB,5\ج Bb. ڶ ݰ˹6 I-i7fv~nΟSr 7XL' {; /GtqOA.l+:y@'^)` F4Mqp-"Rd{ջuСZ:ƕ_݃0:Dy DD?}<g ꣟Krmx4rWm}nn gxՁ8!2.o_dYEǀwo8ӯZIVck?Եz_]dF_6"lA"hawx{HZ#d@D7٩V(KB\ŮCTȄ5bY= ~Nd\|oDYّ4vR%>T8hu@[yHRr'h1Ke>IOH/r`K)TDt ¿Y}u5bWz.bk5/ 迺nӼ0%<SZcdK!@J! ҍ(HnuH?R6 ec;/pWdjV5=$}Ԛꁬ5E3{m`2n263(Gn?Bb#9$c7`:!Ge9C KEn3tl &:P{P}=:'^׫Swʩnc[1swR*llGZMfCbx)]t.#78Mx,l'6uvӑŚΉ 2/ 56]31ܥRM.b3_tB'ؐhx3" \:5,yɜo񡽣&3E#Ao`ęDK7`s&~ 3`-,o&jгә 9P >%Z!/ oС s BB~HaT빚UpS[Z\ݻbQ;vG9a,0UID?6?J<ɾdQw}9!"ߞ**Vp:}@]9B=c>n$N@&X}X]݉l>ue, @ր~#65I /Z*dQ#"*!tivl^A"1k79GҮEcT(Ѵ1^D@F#g0'K9Yd+G8@{!/ 8U.i#]15hY~'x:ޘFroNBu]HH_n\wBP)5Ut)?xL&\*:M#+snj1x`>eBM1 eX6vX娽ćL"mESimmFcZ3*i_jЎ] r_OKȓWk{vhFkWuA,]ZTJ ^[YDKGmqb'1!(Gv9{8O'K m TD 0bv&9ˉ zL6 +E-D\!^+ܤ,+Z%LISR]mPt0IцFBMhL"&^yF[.CC))^+ k2f&/#&wor.+IbOXK'Z*mL(3PGꏰZI-8~&Y&w:Ij&r^O _mE+Ԟ/8Nۋ{/pN-e  :qir1QBR=U_2Ss S/Igd~NZI$.[TP!T$}:!(YԘbyn73 qeAq<Ў<P91*!iH+,Jdn[䊑XXN^_7 |MThy|ԕNI|_,vqT*5AO? `W i2H\rV h䒥ik,} _8?4ߊsdL*um4 :jvmL4m%ѝ/)G*Kwkp%K^ $lqc_v~3 )GTB ]eHʏoY0 m+1̌$ h ꖻh, dӍF=kS|Άoqs.7nJa | 9=0sMf%O.1#KXK˞zi\ zQys+% YqC0Л=h_ cpY%c_u` \=F_ U{ۊ GsG>%b\Lיp)b\| p,ԽyG{,'voB~Dc9&WRF}땑-0?bi8a=|KڢkHf0W"IJ[zsf|gٻ/GPWp_9i,; GD2o??x${yH"~sŸ<ARg4-7|.+Щe_[x HVV)dR Aj5aLUpOFm\ 8LK>oQ!AЎG3N.|fa; 'bO4'XtD. SbZ0'%>:3aINށg:Qw p΃kl9)#Y y 饛d@՗AD%P* N($MjWP٦ .\=c^Z!ӟI}=ەXmu؉~罨c( zxǦ/Ԕ<T\”w'&/ˍf=2)FpE㧤Gɵj^Dc8=TcV#E+Ux`Iv:ǐVrbDzM&{2BA L"|g9P?my|3!&%ỹ yxEN )̌b:\ï7٢omT9 N; %t( /gy6n^ A$Ww@ϿE[/,*0G=m˗˭KcQ;7^{*W_LeHTUyN~؎dx?3i 7s9n*HtQE|SͿ`(b=[+L#mvg΀3r\=A(#ОuAL51s!_~Oc}sbN܊b/NUxJ8%\"C#šbRHMIqskȨd6i c77>}8>FO?0 x`XQ<\Js&fH6>*} W'ُpO-䗯L ˑy$Sr|EV88X= I/*`X A@q!nj9B¬l} N]xBi,EH e 5?rHwAN?DOBWBg2rі9/*^݈VM_ 7D=d1a Oا:#XzGA'.&$O׀RĕB(7:nv j}Yف]kj9=3=g<4iTgs=u鴧mƨ=%#sxrR?nxy|H !Rsl~"DIxiU|au7{!2 g%_ˉGԺ5)tk7 /jfj>0>IT~k|v_iMo.8g_>P<= Znyc *,Z;Q Sҭ8qyY58VȠ _;9ۦ8_BؑzqUAdz&a_D&-^Ej4`)oCI'2RN[g9]*[@| d 9y jy>BvqF Y(ϛT03n!k)EE6O rѢ73LJt E N?:]dzMdn_U??~d?RD&fVX&]e)d&%p﯌*TBL ,d& ~ҍ۸rP^d nU՞ >| ;IXDtNGuI~F?HkpHyAr#'"+E_/x%Rc)Rb$!!ҙA2t~lnp`5!DO,@<_# @bQsرSc'na<|@&-{f֥J%d"a=IQSs z(r8Jwgڨq4i>kx^Ƙr<8ݩs'6-hY/4̑g8Y[vSwo|5=M7@*Q&؜#U7Ɇ̴r5OJ|Ay/zD2?*e=z5.dgebZG=9FtLm Vp沍zFƳ5?nLDud;Ë o*MwiJ6?],:9S $ҽX'mD8"zХJ{`QZDvN&? ,wB}7}XԴsKr+=`HL,EYR:4T^ w@ kW^|#ZXRa O~$@PTHH"W׎ t"KSɽhD`n,p]}JlR${GWi9Q~] ,f众6;Ofn).9!Cl 󷟲@ @_R?N XfOm#cZg6 ?ݖD܋6xϩ6,юT zd5B =9Q@Yֺ]WAMjngPZtp_WԱWZdOEVY[T?W1,n;qYX}>R=Ci/ŐHqJ/~pl>g_FV w!}*!#jӎ kx1?v>xr8C?O(i,)o#csŁBeEc3aB5klM9WK fi体h*^q.\ޗq59?,!-84 Ҹ4&y DʲPp%9?f#?љ8E⻀G1,PRGz3lh(۶p 4!va*5㟃i"g˥.({RwR=p^C{ш>ߕá1 |>fّ.s5aeuЦ#Jw]i)>&#=.\1vsIe>Vx9n2E?f*B{*;֞~sQVaqsRth_n$vLKK"y"Jd.^BEuD l[5iS3'qF%-f(Aѷ \FB>Oy= Қ~D%-PpML^rPlPHiDuTDDPȧ|!1cqg?A';Wiɀqmgr&K0RZ[O"uea}*'g49|@?w=L6O= "^Gw1ԥ ґ2;pf{ԼXڷvޟw[ݙa"p]@;qUtu u;޲^K5jJ`iv?Xӽt+1- LJ<nym!T=:$€ %F6"~bv_pUV[Ö|hety2Nk u5o7mܚ&;%ivĔC A[تh9;ni,+ `'~| % PYsV:~kz! v}HC<|H![94AW8H9×TI&&4sȤMt8/FZ'N _+eX2^P5d\YzwᏄTW9m_E) Q§bbkU=$^ R_2ﱱ?,$ 6F.Y83 r _/rk ә2#ͭH}-+d0*f$pnn2O:VƼ-Qe}Ei9VlQa) Qvcd~=SH|\6W ڠ0!|JY{+z-#8].zxfOs)9uuNπ^FBz{OM?Zm_OIw ppBCv8W@0$ėynTg2%b~|VJaS׋t Yk{8Èc\@Ya輜՚Q@[A(O兟gXwiuq7ՙLfڡXqyطrfj8?MjݞAKY9lE 2if+x.~#YS:;wn/#ྶ#pFSΜ,ҏL^ziQ4͑5茠cm}:A0Q-i쥏PަYThXV?_ٻmVճ$1}N¢QT(u\Hрȝ[ 7{;1S,CkAsX˿m}ZeE·ڿ%s CגkK$qN[ LS"X;eԤqa?oђA䀴1d/ۗ(,ErA#9B ve}pYީAK1Z"A& )׮M3g!UxdXSt@$8[<&)'3@E%Cvť5<\ƅ$D4BةKaD%~pI㠋Lmw ut&M:zr;ʾN=/jAtenmG%9A:z ^[n@ oxϐr4͙֨@Ghޛ +pwOnˌvxSQlmU;q7Gs*7;c' H..QeSĹh+S.;z"[>Uо1򽠆uczA,?nJ5ȾX]wdsz=_5(]ȨnGTe-UwM%ґ@( ӑ\v%/5z? rJry-Bf&u)zCcdN 4_+g5(|Fn! z_ɒ(a} DNm Rc-$K4sjHM˟Bم'\:x{ys-w)钅{2&hGQ-T !~;At(U/&DT f }uBZ^we*g+K)2ܼ"b"tܿĐ),]MaHv®ByÝ4]kNǺՀ3ZwX:wţ.A, Q9N%\߳w 6\s "rcsNa4᭑ĥk \dҖ-HSANVKh*8$|]kzGȱN2 )F(tϵǃ9xK&SsLPT':܇4/sKGDy8uP{3Wh/=,Hn-}7SF7ҘLp^Z]E#g]LGw }$W(vgTK p=EC@NGLWw$JA^5p d͋J/Q*v9 0#L&+FoNbW&s/gNJ6X9 :j &[ǷWO3E%bϡ,ۆZs+B,g`D T'Q2:5i BJ|v:>fun-Ssՙ\) F⺂FYR=|ΟiG/IhNėFV-wEh""<\g3C6&1}F_qθL> cTEMuP@)!}~w:n]6o L l+8CM"9N{Qh;x_ juFwͻy^xk㳿.!Qw"?qCsꮗ3R5#*u A GWŃl[䄃~o" 7ה2v/sǍ?A gH_r K5fs&ShޢYb$[gjv3fa|MuaPudume9۬kQu9G75eV^5 2ߖRWbw,Z9ng7$So3?n<=ua/b#dOUpLC(ZHi:}6zԥ|7K(4,e?/F>4)q ډBLlp<~nڄ*ZQDž_&:LI,>Ih!VI[^"Qf+&9JU*ſMK6THo)f!hg D3dm7yΚϣG>^)S_yQv!lL>}%!w9t08{4[g."&ϡV-t:ed2Z+>R i'蛷-;ӴLj 40V$3 O_XOFs@!֚angKlU&yʬk,;#.<ވz%¿8nN|uD}7r%q0Eh{Od* K◓ir*,oMFcI|Z *;,/HhFikwCꂠ"m7χfˆ$ЀD8}dS<-bPFO1du8Ze&7f1GzIr&A|R54 >6a:2XC辏|+nZk[[\=jax@yљ&檧{BӸs(o}+N?ߥ uizD9{eod.l4>JੲzC,Y Pk%6T( L޾5Ԑx+k):Йf[IfuI~RWu1$FY"<1 ku ˋ}$mkqx0P {b;Q")ķI~ 1PbRdgw@{ZD"ĚjH6)>Üf8 1+Ǎfn"<nJԝKۛ:1`ŝ,07B݀>HYro8!N\`'sEL˥.%,ȋ%w*0KjociziM|Ub`=*ytYŀ5fa=T AZa-f aTYE͵^|N>(>d;9WU 88sPdW"VtHYW6El|#Jخ;]kf+R@WZҦmamm²Q kGxۡ0 !QME">jF->?]lht"#2Nȇj2R7,&FM@߱^WtM 4!ڱ&n㐾oK42CTh:Ѕ+I$g=}"dr}BukAg;\ {r):>wK-YJ#8;%<]7):*u; C Zg.vr家8$VFE΁|!ZΦ|^j?A@cx?X}ET 9AƚL/;K'I"r t/NH5r!FqɬnXR{(xHL5Eִ) =I<|AܹFpԥ>e$PFF}GBNbHDN'¤z|wJ2+R :ϣ zJ݉dY*ZIYR JK{ZK@8>ubޝPw^l"e }ޫwV? !9Ir|ݪG-sZU}Y>ɐs %mOPc'HB"ƞN'yvyIlAX(untT~'q>Tqլ'1Μ2!dViv-XP}|U:m8bL&8lC璜ތW)K'J##d\ІpD@`=^/\<\/VsD,F_(2!5JbD 4+i Z i jT?JXQ-\V|Q֚P:^܁, S*YXP >\Ĥvsе4.[}w*b$ Av\o>+z}T S⇧~wA)'> (_ hmIX>Et$ tӜb3h♒A>ۇ^d[A4V(1EtK4iML\L]{:|S5dN=Q%_Y|sRoA&|b%T+FXdxGkU7V^_|pfaM[ rnSԐ_ Qh0nK] ;w,./U:8 Eۦ79} CYȨ%WŮ^$ƹq5r>njT3׃"$_E!wNBl1gSI_;N]<ůBs+ԍ7M &ƻkR]w1c_ïaQ^R86 1Y;\_m/yGmK7/',_}y 9hIg shhUոn;@ƞgc$ 5/n_d-7x%tY+=M?^MB[ kVFjZ\hjIzGrD@f)$#Nґ*cwb0 m-Za뼖TInR!+ #aw/A,ZM3Ff 0`5[ G^$ |_f=qJ\Lx?6Ȁ3\)mӗWx9[-HP\^O`$A}9w&^crrUfЮ5z|g,6h78f< wrʺ-5\)luY|_BY۱ަڜfxHlCc48m3OFx%<7O ʶUMƮL:v-ߏHUiű|pG>>)b﮽*@Da^=V0"koՉdiz*xmUsPl0@ˢ0l qm`SnZ8 i$&t-7㇕SzPNɻ#=|pg)ߝNsB8;oo I ɯ= nm^ 3j}>K/mVy"M y".ؗtSV  ڥYڪa#>XD`, SW~Fy|G0$'kkRgPI uN ;]zXboBΞLOK}vǻ߉!l6GyIz"Yp^ƭ[HK"R.7vLg<*B,hyMީ'VG9p*#@G2w[m++3lSlպ8ʠC}LʖvT@6[@H mOz w$'^kkG%nݰnZDt{X'Nx8A;EվdAgqf1;o|h9dw5PC;x|&*3xh3q&f}(y͆S}1 ܉ S>JhCUqeh;x&5mŁn$mG][ >#UYӿ|:d#ᩊs$彳i~HO[PH~q@ U(ȮL|k!װ"`AP$!9t)[whzs2iJ~F'eQ7(F߾ “^oL'Jd(UP$5R|]TYt=RDݝm7 PL fBl> Q<w3O;`P`T5ev6 VE@Ooy 0@*YD xT;6@ DTpwO¬:\8|]Rz\|H=@^$e~~e|(4.ػr$+xmW٣P*pNfyq O[}ngTE^U/,εjQɋ ɩx\T!i$^d,?}XTd:䗥 3o ݱ |]P]jfMCz6*|ўmSL+{iY&ƶU'l?0qBht6/`Cf=*$R\ʄ((6KO$nB6+HК*kp"&g֍~ hQK(/Һg%t6IJ Dd+\v}N/)}uHQVZ ̓(:R5@ [g&8iBf㿗?õf5ҋ$_c+&@݀C3>ݹ'0K\+;َu1M#Ir8L΁VeuŅ$ME\ ׼$.|9%7=/edΊ:HgRJ"4A6qIg WH>;uY^t$ Eʚ p&m}U8mZhKǦ@$Q3O v]dznl&>ppbҪĠqE M(d6pPH^@ĕMQ>ڜ{)߂ e.4{/t}{gF$wȺ;aXA߭X/gH G O6F O395+Mœj,ʜNHt[1U^A@KXZ=ghDڹ:(}\^++P8- ɥST" يoDX?wI،`tϴ ĘXGd4ݶ2@PiK}{kqNĿnB_PECr-Ԅnw 5 ͌Q[DDʿm!P չJO1@˹(2e Qjp@kfv2ɩ"n2\Hߐ-㎺L "%en ݟ#kAZSC %/Iv`%[lMg.lڃ6ROD^l[J5cLi]IgNx _%ev\}R?fxa;' %> Z휶>I[G!AV7w5綇L{x(joUQ㈤S~$;# tG\H)kM3IRlد3@6ީQ3lw'=M &M;5h:&w[H˲.F^(NW`V>S2xRy NW@pζH' RS%fI)Tp1G)F RZBS$Dm2*!.(7{QiA!&5lrB.yC-^`]|츜 KSRe"NZAW)#kA%[+(rj+\ONU)[_A bxmQ73dMrkx /-jﳕd]+#&t;JJa*Z*V8T58˛ }3.C+@Mۢ>ЪꏮWn z"_ ] vqVPOmMB^W XWp5ep1S[~xutT?ur\{]]ǔǭ=% Mg7>6g7 +k=,gI{։|-z䷸>v Atcv5[P\%2{<%N*/Ck̠PuO{!8R]N zG7^%27i$8"[O( uιZ:2C3퇛- KƸ jtE#͖T4$&jrZ_%܀(NGLׂ}zB.z<؍ݶ 1L+W|n@RXq lEߙW喐&[rlSJҹ,md!em@sgNЂTvӡcةRm7l\ַ+ߘBS}giZ}VcY|tT9O-K"L 7mA !݃sTAEu{S9` VvB8Ht`!nĜ{^V)YZ0SvgfWp>::}/o >94v^{@_x/v$u˗sJ,M">6ó{)AU-DPrG砐]Ā˗a}FAb}9cRۑ28au~ש a93*R9hpo"IcCiWf AS'.pۄJnX讫F4V+gR%\\зA ł4hZ$0E#czLzI|#Sq J!v@ɝW:lJ r{3=|M?Td6|,8~ (* 7" P4ʖڿΪ?SV7ZWSq s˳ s]Cvx6Ml]ǽQ?U)z ?)fKLtp\ ԩLD v; >c@'zw-Q-9H=D^J*ݕHp2S(soHLVqs9;<-"kC5|OpfRg\eMȒ}Zf [r)R~s&1}J҇/d>ts;6t-x(¾VwwW9ko$FV NJuYx]ɇ= Iy'iy;1Prm>ȓR+jX. nGA (w>LAgmR@R Uh9Ljt7,CRPuZaгw/-0\YL/JŽm.RuBMGr_ 5P% oI:TRFю3K*w}ϴ)*=Ko3e0 gGiSnJ=[\[c)$u-c]O{{}:g1,Ƿ>t*%XҒ҄oڴ5hT HWIY6.+ŌsY;ܔ,.M)^S" Kq;4lSaw.th܃ ]`;̩ef|Rmwؔ !jp.ZOL/|"FZDjĮǑqDŇS]ʐڵ=Hd9칰ȑwrjI I0&.m v w[}˺I,{@ٹ4~VoT0B("F#I 2S5pQ]M3;6j_C`^zbhFFﭔ^r(Kd/jhuTm3\Vͮp"?m!7/$INՔT+PZ d V__Bƍ;}d,^HI!ܺ!TM pxUNCzO>'Ç@Q$| B"7lY(B Su%go gb(5[ݾQIJa=4UF-g|u6ږ-$BN}!=?V渝J&e8\Ӳ⡷J:H-IYH?6{z$^\v_9Yp kwJI0+5;cCf;{e{SF]0`?Q[l{S1&2EU '" _Ҕ-xIYwn\-G^.pZw/t˜yWYb5Mtqq4pϙ/@:RVK^% r?euWqtEVAj]#/H1oPo+%{&Q p* q%bY&>vbT{^wy%(g F$V }JJsv^sLU*t7մH's" Af=вTW ;rjcG6- dW{gU$oW\=CUvji\vjkmk;7IXRr:gj[v7s`2\f<_L.Q(iQyӨoYF_BU#35$V=*):רt̍"iG2)@* k茷b3_)40gۭ*vNڦ]Ɂ㵆qiDeXQ>}7R;h&'Cs }UItvL?POQ"m-y]38c [9Kdz՝Bʴޮ^d1ޅlt~jH~&: @w!ɏE'JA&">]aWI=Dz&/մΪjIuʳ>. 1KN]y3R %=`3̍*/$v`%DZ5ѪӷlK'*]?IgQ-zJQщ %>CwL$NiӐp۸#6bAOwt_rWXf rkdE(鼛lAb(y}vr -p<c'r JЖ]}5,CgKҴ | * 7'wJk ٴU eGC/׋Bv<}L.'EvJFk9]}2#J3N$z$;;e7d/CS{V M>Up;Q+jH' 2EM<-dc4 +b4>#9 8H0SiS;IWA:f;f@+;$O1bx~e"=w+@-UG?u~/hbNwv*[^W$uU^T\@<>^1vM f1颗@N_WLnZE\(̚ 2Ȭ\P%RV nO%T5. KyYoH ꅣ>$شdmkFz[H?!?-ut{D2+u #+ iSt -5BW xϛ[c\;NRGoH8I!1{R` $w*r&.:VW;EDH:mzuSː}NFv@Wg-B:3 (!{ ,Utu[{ /;lXiJu~k N"DPRexϘd (-BHrsE ė!(N|Y2SK _-'lCM6xc4Yd>KkǹK{?m<>ob^?Ӧc۞s* FRc=u/yZ0-2}8EM6W a^393G6 Uqyy}8MS9q T 3򼧞&swzNyIq pY-`#U8Jtd>vycׅ`3-*;3{n9ltjb+kt3ԒNWogFdCxIW_)=?ݦ o.iR/5Gr(v/k}l;kLsώKdXgkթowB\e C9hY,w7W4s`-762l;&q ._}Hf8(W{ q#݊3s$܇)F}b쓶qk;e ܹpUYoY)@sY].x:%z!ϨIrB6;92D=?ZSX\bq:8$mcpN(Ojў Cq Ji'}Ƣ꿮s?m? eHOë9>UfCϖ,4&>l1>Ґwh^wj۸ѯ2LUAڌH!N%A3|NM䧑\.$9;m}qwZJ)H=MZDSgԪĦ1h@Pg6 )'K#Rxw _q$R]^t Ao{_&=K\GOp`Q"rɻQJdȹR:O>bPbK)⑁)k M)<GUwZhg1P L7AWrTtL:r9 Wu}A;/ԏL 1&8{K<@1]A;H˩_q0 r!L36%~# CE$!{aK8=hBsӢ7h>*m"fJBoW^/HүIT8[D`(BXL| b?3_"T , BoDxv.c9sh9BbGWץqO|:0l!񓮃W :Ld szq|{60Ʒe~&6V@1vO[n|ȾW ߉鄲0>Mz~gflo-~rL*Q{>E&c 7᝜^őMh *ԉ4TPP"pA P?^EeΡ\ߥ:Gj|7Cݶ>l229'u'eg꾏 }mph-cSƆx0dO.Wn*6+fl 9] ͵(yZwEv;Rdo3uPBE:6ksn{,غ]\F(b4n'wj}~ӱ/`W[)1E(9s.Ka;{"ʏ&@Vz=┨h5yNoGMl%-dV8 *bG;A| h Yu\&C~ٿNwKrLM3ɐ*[uC֝%Xg=[PBOn>Ԁ$Ȱ)=0y0J1\7w#&:i-iV;1{"ғ{R`]Fl@gAPK[tt)*Ń޼DtGh:r`eOvj1V$ּ+S}Pxw4$uN}!ɕsIvb@s'/-vIj0w8(+p>j>gK*M$] d3vg(Vh화y*DK\;ip$LzPWC)& wВ)j3"8.h~똰!(9}%w@.'|s`M@*k迭cR5޷,sbGr,SJm*`u)vr6T9PC] ^;>2ʫå^3rFS`4ID lNP<s<5&5)(kOuTxJ{Sο%.x+N|Gq9wst- VIlS J}z;58oVy~e^9~ MÓO` /P Z5&m5)@ D'0iD[- r\ej4&=&p5)4PR(&Y껗*tKW䷰ JsW~q eM))hbZ9=ԢofҲʛmw48Ύ0, 0nݸ {쁉D}{rw ߁dN~]*_f)ﵩAXGx政7O$@қd&_bw3In괐y[H]7%)FgKdO_HG*ȋ6G^@&N2j6}ߋ]/#e0lsʶ:_~9̠j?} i:lI~FW1+ڻ9n-A3H"grG,BM*-rs}rxqS :'$tœn{"=TP$5XԿ-l ?'Q Sg- b2;ʘFDSbxO\:K& <ՠL{yU1Ҿ1\H@sAeIXl%|8[&2鉒hKyoJSfӁf˞ij[Tfvܠc+So39#IG:*5EFovKQ, hTfi4ٺfszb ' ɔm)bn^XuRrR69{m֭_@n`j3̥^~!aFV /YPH]q9ekzP\l.n204jS2To"T|CDx?P0Ʌ@/zݘ*UxC% ^ VfMomsxtd*SoOq( Bfʗ۰b>+6҈/WA"-kK:K/ @. Tr^G\ˑLbl]',Oɽ1tAx?-Sz!6w~wPƨwCH] XAdʟ-4 ͓KZGѵ(E̶/"H T_V !XlUrBYBuT?<&bQ!z(dUz ̱}@#+?+\j]6[ pD؁-E؝˴̾I]X52J'.JgF@x8H׻ȹCV׎NO+~8 "?3$e' aHAvidI&:Pf:O~2 3}t'1yp;~}^6?gN#Rڌ2hhƎDFt&~%Yv[| 1auB"5j%SrqCyYIb{;"x J*b{D,Phd< sʽ!y)SfCgZIsL>cBe!孳 \̖DnKT[˭Xڟb%Q5y\sQ+mX\(KwH5ϾaŖ#jNb rm  δRkCSK-EI?+y֣9Y,|Kz{F:DyؔkPd;ZJ)Hbi}zQ@uqqzښK_ ^o7]jS{y32IcU+WDZ_7٢- 곙x}5d IT\fnPS~>fL˄|*8dQ/H 2[6Gϙ sz{C}Mrॷ;"+)fc]YkDwJ G2-ߍP ILԣtg C:3g EE,ֶ'DOGJuC=cC8 {YW+#*s^4H3n,e*k5;qJFPfV@NO %x$G S龛Rg6 [EO٣[Q1IV3ϟy#IWi2n]Ӊ {z,pj6j\# Z)91<5< rQqT|ҁd_|͂*p$.C5!UmBJHOfr_L8Ϳ yIeCqkԟJߗO##d"s޹KUxyvjG3D r@́xրƖث c~ Q;e=W1F}i,YGtµ$/y A z[5*K~n[>믣O2sE@}VV@p;KS)Ӿx>VF~pkj̻"%ʚ»wh)A6rl &_mE63,TR)36R+4Zπ2`=xj9Oe7է^N,'Y3U MHX ԏD ze I뭐4i܃vo{wu}Gd7[M˾<䬋/(q3&{+僨VcYo)}DTn9^l86q74$7/cO\'xd|O+p^eqH1v:1Y_;+g#io Erjs@&"SD @֮ IZքI/{HI6(q7vO`fΎC|L23{r66>yqiW^7ٛ쁪ϋ[]Ƿ(V]./W8I`i0!heF UV/%[ռmb2)gERֺ5 #;}t0p{LT([V{%=)uC־v|@R jZ%$_̌D z΄{sm$J08֪–|b{*#+E ~tFbDt[Fjwu `Fl:qUR79+w@fbOtc$;ۿZ+y BIjE>zzܡBdGڕ)|YEFK 6pͅYdA**l$P\=Ag@y,s&3 BOJCA,n^k!x:F܊κ5[,kM+ܛE0 m)d~9nu"g[D@_Geܬ ̤zYw_ԛ~uKw~3 ﳒ9 .O rN)]f!ZBcyHN b)zzulk{r1|[?S k?eCSdt?HS|\^%-ydm T_P*/ 0D$]<(3iO7R8EshAK^-:\ 65m{ߦ|2ȸ =pXOqP/,`O^ @&B\3Sl?y@D%l'CFU\O6u<6V[@`Pqz)J>}_>@J0kT۫-)p;K'lx'y\WQ۹<@AwwхzmW(ۜ~v ̹K-=Xel$-!o}fo vN m0WJ{}t#}W1UWy5&[:*)N ~OCW>cߦ䚾y3^VVVBzPUHNi.T RPmUK !)DT5{e>5Dߔ 8JJpKK[nu*SgFI< lٰBMSU  Dr裯3m9슊, A5O4t\ʞT֨ -KAcj Dnb[)m mT'l$ d)Dc3&PHf./ULlH쩐qOG349[l, GjWx:>Gv vR2lCSVmbM@PthNKaڊ2J>3$*+WQ߈ &g&v"@AH+ œpE"([d`l(U@N Va(<VoQ>?7k2e wDNo -"2H),Zv$ĶVk:8Dsxصm@Jȋ QfOgDc>0ڤ,' ~`mtw@2GWd _>(u) @GH7w)ϷnP v^%~f 2jHNp;:5T5;xϮKz;/I(mjVq@^*BƐ1gwM!qHlU/t˹(VWHXCYԑV&r>\"6q]? &91$刷=,j=)%)ewΠ˩љzUa irw.ΐ% y^t\<{jâݛ?%\F6@u$WL7ġk5Dܦjs(3ukAhTU>݀,M)\V% 6tIp4G^Wpt^*8 b_CiS9bP )-<8#Q)SկXN5CDܓ/ ,`HKU;8n9_ ti۳A @ %ag0^[Vfڟh>(Ճ<ZɀY_G&ԮJ׼@x}. wvL\:.y/c-:l[CΎplWe*ڏwI.nְVaS,!?Rg-,J+렀8ݸl!lf6n]]>HTutmG #8GuAІW 4T3ӟ _@vѮebu\@1]Az(1 > wx 2y ' r*nAss09ZUh sum@ΝE kG&j ْ0vиrj'rRez+W鸇fmyr@Ӟ|_Yj b\&Wa8 YV;oW-+D<϶\0U/1cͰ_Qf]H u5މ`7HN~J%ֶbdQMt;Y*бTΞ5JLfRcFRSf>* Kº|I/P ̣qy,W&)Zker]+))$i\Pr5E9sC;%VOq?A9ˀ̥|:!dm<[ՉpSehܽ}HQ GMܹɢH!l%|PNΏ:sAR&N6QX|GRυNZGonVsJɈ$;we~(Ӿ2xmHE9TZNf}O1{—P{ iB5EmUl$EBDoDMŚu/Vek}Sʱ7mW{{+ml9gF'X/ӯ,P)^%DUFj\R7wIG?4*[K$~2_wٓ#ďK^s&KV+L͗BX5[J@kn3_/V (#@WDuɝ2&Is` ca+XfdDqXAuLgBk:q^Jz@h4&Ab=oS[=`)LP))f-v7^W#4.'vP$8b!$m2\`;/і(Sxi ן4ш;/`{:É\T{UbDK: 8omUwA+³3Ohjw4Ig˜$0>#  n8 =恝|Ӛ:P׻Kx1!T  y0 5Hx*{ҴI0c鹖Hhe9b3QUXZ31{%W`,޲5NNRERe>ٔڼ~i=wZS[sLL'{}١?2 pNQo=IStŹKMBǻɬ>a"SuسS>`M#$:9Y|#4(ɔ 2% wXH~jޙ-ql=#qB_+VB[kHQmomP}c M`W{0t}fc+ՈTy"*GR\O1RLXxnfOEGdDR%xBijEɇ.G XܞTtSJh#Y@\|*3 6>߁4媂"홊#=\GN#t$[+0Դl3`O{N3X O>GzxWq#,ߏ~\b?/֌ ?҅ġ}ua{B XNF$5 ;,|)س٨o5H0R)C*owƮ֓#bx{o`w<"70rjZ=OJ _hKܺ;=}B̛OrPpp܀O)h-kzNs($g+ѼgBue?.B9.*jrk^Ig %n}T7ډg8o@+[tL[4'\n){zGc5rڛDREx d id\vI(>W_;8.ׇCM\5{pFyM>5\VuKˇ.xYF!i#..PJ88ݱeFwd o:[$ʛ :rlG-1Aw0^㎘|pJx]wjJyLA|]Xp8p0_:r˼zvu;i"9vWO# Lc6b7@YlGPgK},?Y3~mzZYH{unN"Ia˗:N-PTL/{F0͋8L!h6_ LE+Am)ʮ L5)y=ůhи\cUDW·X@*Q'iҏ1Y.jBCbcC^guZ]HV`vE VJlk( #M~F?Q\JNA%:]5)i\ntkވ\?mHQ44([һ}1OWH( a\.n"~D>͚eگv!s"P `aO=@usbWEΉz]tWۑ jt۴%i5\FlZSaÒHV:CiZڔ+6dwC'Y-L1 ~5gPڥy ͤyפ H@ࣴ3TOGk}tpj;v2LWC[: K7j)S<'J|=O]4&.j9ڲO/HD(]沬 ?SC퐑GkP{jq~{:C{ >!^M/E?WYy`[HN*oyZQ`RCDuWچ$kҶ}8}4m=h^MQv?f ZQ3 d 3b&? )e|tPuK0ھ!.d52' ~͒uٿu7wm#1ۨ6!Qҫ[-\(| iH-$bK~\cyK _I~YS2XkMOdkj!vα-O".< ikIJfu|:QfɲdÝu $ξZHGP,nX牢$H { $WI99zYGm09Qul4q8i-"ou`C<^{ovu4wve"H@$īyzy$.96Q^Ӣ@:m#Q[ q^xY''-žkvCnyu xg@:+6tYi?:*3Xd=ú6in'\NG[)|-DžyWN9jޔ[$m _<1Y}Ƶ@qK7@4coTu$uپڴC.}Sszҍxe\F k~%- o.7Q -LZg?"`T e`Mm|]PZ$ʘhåxh-=VKO~Y*Fp=[AdR9r |X %(Om2|lyW߷s^9ͧ{:/\VͲi/@hܣ<J}Qvآ>sUt' ::] xzg籼Sc3.\lbVe8 V-ݹqx[4}-JG'+I6u\kpx3+șW!RmbǤ۞sEث$Idq(Z0q]ޘخP皈k!{8zl;iI\@Yo<}~35Y y 0XHFVvl揗\u/pM<}\~]ciY8#0X19 iV pQ H/1L'қ_VSYNAa "I&=לҷူ=⦿DN)z[F5VȢ,?֢{V_eVHчrhZNлW{{ӂZ q*#jhZO9^0}f{sHlPU<~%~Ns=6ND[XԞã7^\`ʍƳGN2}q[hRϻ_"̢ y>zws:,`=y"b6pM)M̖\2 _lE.t n (;~ly鐿bΎ[i6(ɂwՉ2O1rpwI&;],< u 9n;#6x+y>QCb]g/ 1HEQث{I&18?=[ *:{N]R6t<9>W*J ěkp NHVPq$KΦR%gEp*4>үm:Az>@XBαީ?c^' BB0ƭ{6>sA懶wj*!B-e,yt;w3D3 1@+=g.{wW Ş\]İ +-!C-P2nF`ܳ nrZrC];8W@۰E[Wd] |z[٤X#:zb mexJ|u)(/?;raqP~z7nwl r;ZR#*@";vmAϽ[oԐOtce%ê2v/+oyۊ>1u)_cqgtA^YU "Zo֙GsKSu8;yǑ1H QDe-fB?]I{ȂcEf "P*UϽ/4{+DcZ8{ƹ]*cq/p,R,f-Vܴ4KRbKȥ8_bzy+ޅ8r-$6gfEfjiWP9FZ؃7`]4GF,  h Uj!8E}U߳# X~` HWGю^*) aFuDsqPol ^WmcߔT=3:ߙ&$"ٷ O{sCP@1 Yjc}lKQoLS% t)"*p#&BWno pZkp. |KWʅg7r%-Υ ȁeà_P7AF6)6o$BT*Hkc>QѺϩ~AcC/W/\6K7R:ǖyR9*;m؟Id4a'g;&wf[BP9CT@?'е9xFN9|%ʲ}qy|2? #]ѭsp,5v,tT-5srcN'Nz>$VgUٝ)"O=:T._fؚ?$"T&/`)VG)@~i>|4NյX= -e}I/II0B'Gf'=V^p[~PU& n7gj!Z)P`s c쌳>-V}) ,\qu#vrb 57jG+&s8EϯwZ҉h^_Vл$('|}jU@$@϶Zam2(9饧 6ks=-NLj/=FA粀=rڏ GleK?bG'5NM#tV{![h_ՏsxI9ӤXCmZ@_h.Z'T&"wxu 8lh@oҞ|W_'cҤDbwc(<,5-9Xϣ`>/L]D!^ڏ!O~vg`N`VWӄ~B.Z|5GK|U:r!?Oe>܁vx܉Q\o==z//z|k tl{rg;1(LJjΛ؎LVsT ,[^ * LIcdCF/4/Gpٯf ,1c0j-.Ϯߐh0֡䎽h>K eZ?HK&*5_ t^τ*4"Ӡ|Eo5HRhwcBGMݓ52+>s봎 J_hYL|I' I~e?9sO XЯr2R柃Q >ܙ.^QoCӲX4U|8=e瘊OV ;T0{CApZ$LL{C u{iEQ:J>{^5VkE鸀2CQ):o:xɈT%ʝܶm ᴋYӳ`ηd _"6YH2)>A?V [DEfU5\'^wǷzxf>q<@>GGwZBzv#nL~ sU6A"pa)=R63X-jAy+gףcʲMl^?|AVKi^"wspWM_Dl%/Ɍm9Fi=fG_w]ڪdEfQqVUZ9شm}~9zB vr~OwUnj1v3CvtIPCw[PcMP9*_rw磱 O"8aڽ?'wε>mvW~5s~3O8tOjC].IKM?@ji( h/G>,wwUOqO='5k f1YS)rHdBW57MJ'#bpvc\ěh5R >o憋&1 1.my#`OV͇p%!s`t5?ť))/;|u*Qnb"3}wK"~3%n nD}d<"a'PԚa/lM}ђk>ʭ}Cy ˙ѓ*~? -⋁3[#jKi$K>;dK{Pe3q>1۱|\"rR)W0 $U"]ʭfa{Z9eVu"@iAz41% JmKױ2@昋[C^ntDz.Q)'腀o">F"m*lORSw@?!?/\8 )CE.ڢ4c똟 5o˭S(Tڵ͢# *c-@P DzŅǴl"5].xD9:tm H0i 2=&(S;>3@_{xRqʚ+.j^ZaWO_ki!W*%x4ϝh]"fur#WdD %0p.f Hf\|8 $`l`4Y4Kj=1^{KtI v?/1i >T뫎Gd[!9vML;$]\iWRI ],ijBԖ_^C q:D-Og ~x!9>@PVkV'8- n4գuEB" w ,[|cxE<@m}?I@oS7c'P)-FJɡPd|l\@*3Zׁ@1VJFTl Mӹ+Mt>Hɭqd]7 ;VoOwqpBjmfz[}2p78;GNx3ΝhWG=o z JliٮY9X=gKUU+;kU=~T8b1QYuh;OdNV<[;Uj:\w^Xmi 7Fge_SdhTnd{9staK"Y]4qkU>-"伔V8IJKr[Xc8ώI%g| w=,@V>~H#'t"V/9&)"TF.E Y6Eһ5)R,E+k_+3 0 , ϲ:{с%5$q3u;`V`sPg3nqlc9eG*DE:*~ޔR~ԴT}J:+2u:?IJygvuD./ÇvXFP2?̈ qj*j-Mj"v虏]5ئB'It&tmI˞~:%Hk>#W+㞄yC?ӒHx|J-Wf-~hcʴEm:%r#J)wYzݹ(Sl+r&ro_{"1wGlxCywdTxS\^.-&5& jw2hi5lcZē6NiB S@\QniX A{#3ji\HI#ڳG؝b#G\mz_p\ 6:p"l9be>w~'0Sc}.Dl:C ߺug|E\v@uQI<^?;hrQdD&y2d?d_=-* a&UìHcW $| CYO3 h>A}6H{"._zqM. RdNٶ{VqQJڄ<b(i8DgїJ"+<+@J<}4t"G\^8|(]|ᑡtk:6/PN֔u@)wIyzlrpp4I \ӵ:zWU.^70U™cn`-Y9Y__MVajN%.% Og2i: [ yvxb/(WD9 /.8l!_eoCŘCV:@}3 &K*tڽCSsWg=~Z'NYY;pr{]#q.`+oG9Oc^/W2 @=ݮXhNM{2x}Dab4 x)DK=PkwXzA"ثػj딒M I=,nt P`sjr+Wn\_HOu">.;lըCo$CrD$Q۪&[/qTpDn iYKUsh(/ m$k)ÇNu$vvc~ Wj=`ݢG`mV:k˻k*dR͙w9%/E0J!m F}%^aM!ӭ3vBfʎA, G+}G1jΣӉ‹>ҳݻ#)G_7e:8D.K2uAke@P풌0D\Sv7=GOZ:\=hhur;gP;".IJ~!-CXRqj9ӝIn$ͬQ%G'9KCorSfO.GY@xWZZ|ũ|3jr׀{-&& 4M8LKVF.>52clWզ59ֲ"2.P6M Lq4O 0?Dw>5{>{\pɴ LWt=1$چK @P˲kQ V~C4.{ejDD]Ǐ))qj Bp&p7yy#iiu~FHV_G, O#v+F0d=%VN]9Ff[PȨMMEi>G|Zk7=Vl㐕(6?U?~;rK`IY(;=Ӽ0KwhdD%¸{m6ykٕu.^f]@RH-!|QŅi~P2T;|qH g-{: 3ƞA蠅Y)$~ep@/3n'k•b&rN(}. 0ؾaYӜ jt\ D_~h)FTa̾󬏔A!)(Ԥ) bz+Wa-qЋ"aoGz@yU;8M:hEA>Zϲ'#j mɷWLrz!fEqEms僦RdtU[CGTq6sp $%Ɗ6x?p?g7T%Jհ^#WStay?yvנqάo7&nHkȎOp)TЧr~˥S`|\L U@se+J'n8.Km(%UUuƻ|1>T(ƹji<,b_7!&T**tߨLY[lQrywx,p CZ6zEg;+ .@soaxWv1[fxtu$r[LGqV=K*Ӗ%7PU jWfOV=Go^/1 ,Tn+RpYXbvb9c2Q]bNq %1azIޜK9:E:tarF T0ؚ9I!`TnVlKwPwb@_ܲmlGT]],V6A5f[H{ԝG]\e9O5|m m0\\=G٢-s7%3jx&]-´QPq94r|<'Fs>T,c;Y_Eaw+½/0\*(2$' zΝ0^_j! Ƴ5{()Rg.Rqx/qϥk7[~3EHsVgQ)=Yn#'r?-lW[u[%Ξ}7B7XVJ% r Zٝ ~=!S[4و\^s7-Q\:8dqKٓ>ǜ~ UKq~^kַ+."fO}%UDp$RA${շ[}jwP')k'ipxMvv ~6럦Ԛ}ZS{pRoeLN>kUxTQ;2t:oZc;R]=E4Mξ"l] ^q 1wl|zSzR\ HD0Ȣ* !t+>|uC"1iP}4G|Mt]K^<r?4dj fDͲT/W89YVYVWeod%;[)neJ$B(!w~ZLwMv}tս*z82kp_Lt^5UFAEZTNÃý,p_,^ZOop)_\/S,h8y&u@,kP | -ƇrC$-Hz"GՑE% %/ٷA.egfmah'g!)^&vnFU 9&yNZw@hE* "v]J'%+-҉{Hlt;G&\j{Tlo(.n-{&A9z @eN_ YnW dKh%A`#Ȓc~DGŚ/ E?[K?T"B粴\iJo:),|\"QzڢU*Uƣ4͵ӕfYi} Vbvz5uOouzԇ̉ !Q}J@,fRR Y)4k]j5h"f/n~TL`Հ~BUP+(o;t'rV ztJH(.@FS+v~.Vjd|;Er9@?'ך sN/mΚvhy*DEKlL9|ꮳ)4kM[rhϣs|L<͕62nճ7}^S߆Zw%fDcxJqfW Z1iQ:"dQW y [X"CeP;wNb;/ .`wi5;))=V! f, :^QzQ[KIQ1Vf}_uq>4(+cK:mDrS;J[!dW<+ '[U;VHԜ|Qj2D-G׭TK[KB&^m)BtpjYMȮSJm'&*.t Imr\6"**q95/çD=.7$ذI"bD͔ @[a} d[a?S-z-=$ }B$* `Ӊ򙟔x GG4.o^ؤ(}N |d~@cE/:mש2 r#)/%)4n/ eeJ.g8fxw"'WY@ o' }R W-~H]z[z^#2-^IbƀSsz4iwl/U=^3T==_撻RrPvl oi6#(\ךBE0`PuVG24W{H:pUpy^B \xuǪCt+[zg~_[K}Uwk)p!M̡E][WvP2?Ђyϖ@v8S!֤185AkOźv1fPu%m 4.REC/Qp+HYZ<"sLnY,V,n] 2O7]"GqoH1$:NI?[, ^Y4wKC=]T.AԉfP+Rp9tUpojaSe<!@#U?@Qq499+7no|Fa1!{ϙj#rP$iBk]dkx @`HAq^  ۓ~tf8ƮJ [L`be&ih{:':~y_|xaWMn2͡$3e=t"-RԾ ْ4"Dy>igWNZ-!-Z5\\{8H&e= -Pw+ocx;"o}vC.V~F* WɰoӊzuA=;h3)q}s/=#(xb,ex 6'߳S2L֨=@ m5g˼(* /M9O'>j +vG|H I7,d]ِmq{u|]c:N N-pTJ4] ALwYd6/$OBȌ8HǓGeS;u!bW` e(T>ĞBpTQɫ[_4+md=?XYZ)(R0njb*Cs14K;Π]w lH],=uˉ.N&)Q8 ~-hНYiwVVG>qE1 KT(PUh `T}Rb޵d>IY!J(erf$DH`7N–q: rM2_[&dhQNTrS>?pxqVFZP򣵜m+q)R԰q }~"hIqN'!d+XA_ɸgE՗v1K-h/4/P-d5UɠqSi R߷e*ßdD#] @/|ܧ@`Y՜,Zs]Z&=az<ȴj/ntr%?*Wt9Q69 ]tyB @ `v!,H]Z@\1H7UćCc,螞s!#$9lsPX'/R#V3" ɸ l=7ҞR/8\J{{aiE(抯uapH]3K ճ~5PStB& 5H}Xg!%ӵ`odO.αJlcZR/rmdHR1mn LZlS\v(ro~\!UZr2؟b{;q{^ e ՞೛+p}9{/cSYv>cwSё^ 2Salw،U3".3 9a!MmRXUa^Ќ:e<'u |\ `ZEt am3Ctz:`NO|,oxl^8A:)N9}-05#F97 ;x(rdgTYiٻTb|[3,gTcTiJaFȰrCc8D B rouL !C,~CnWVҮ[MZK`h2%_3K<Ja}9 xBj=N{%RwqmZ1BuJl־&U0}lJ }Y x^.OmG<|`!\*t=*L,4T%P̲>Cl3R^(e5Hb3RqS]e&U vI> ;{TF> ˣ {[oL.Vjhi ΌjdҶƗ\AAAbO5˫[i3 =^5QiS Eu6\H0ܕ2^rϬ 6nt~Aj.RB!ߏDq_ Uo<9X-Dˮ $RCds+V#+m yH]9Y%e 6J{K@y }nwGzi@;Zw}m$N /lb69jz)OK %SF.y]co_ZR1 ɣf#`=:)y6r7-+V üff ӬҪXlrCLsV2r v蕤៵fcP͉ T?z=Npc$|W;vDC1Y2"^$4\Տ6O2ą"65\JbNm͠<}0EJv<8k =yۑk\ {a]2} x &p]q`ivBh,pɿuyG#iô#$m ]k[pM!,s8Nی8A+ ͻmoywB+hl_"Л`8v@uGWW[伌qg t*v5V>3sE/nΊ~O`O%ThRNd ʃVd 9G#pB;ݲGX#`'A6 "؋B Ԯɩn'ˋ Y9_Lps}l![ hkoOMnٯM נ2NŞkej1arR7'~P;BJ<<@#drYlCr/r% a*ynAO˘(9iݨA͢0;y9(Ybj.PhP52@" 3B"Mߴ;;WmN'|ŋx6|3H~3RҚcٗZ# %2޻ d1l{E/y՜A6H%kS룍v)5 8QKmɼE\dPn8'($Hm?'l7ݚÉ*-̈́w(wN_@*"MUcۋۜ:~9͖HNҕ1}ݢq0pPkC,dfm_d#p:ɓ^p,4 o\2^mn=O|qT-mK7~l(}:/WTya#_ߘ axjd[F-W̶C\vѠhW-I`@u@/>Tv*J`z>e4_F-'fƯ8NHl#DŽK׊fbuWBDsO@'8 hUaN H1+^H\nRE@[? .wF=%Fq9 Ӽ 3~UA9iwt },칀4M͔a#m2&ʳC2` eY`+!wyLŸl G$! >% (ZҲ[;ˡCbl[\c宨p"_ oooObd8! JzKLS` Ejх5V7d*=m &}}_^1׶ân;)Rv@cvŢV^MY jQ$F1>lhoؗ+O%ģATi>A|lKOk?ZA~]LѠ-i-W5P@Nd`~|k\)T\L{pψ ƺTͭK@Api%O!ߪwe7M-|BHBQW[oIAFۭ`" *y*;j-}^s͖q~~$&0x٤Ʒze14 bnP}y44HAR2}ziQbMX2WxY ۯ_-6irїՀa(jr^h Y8Tvn7g?xd_5`$|sAC92 ԻtV `!DɞiY|&慞<.h""f6KFw=LJE`͸Qؗ^Cs&.v[>p~AB"ga"jΠ?s%A;pprSg!sJqy+\9ք.JT{%]bb[i;]Igg`#%99*G>=W>B__]H=jF2q~l#s19||\v-v"bLNG.:-88Rm_Cd)cZ&ev!{\OuWDٺ6/7Zb ݎpd2I@iSEkcOlsO(;T qIߡȪu͍Sڂ qnpP8lBf)>թn1Uq^O{Nyu`oBA(>u0{,C2"ާZӽE3ۚJ F3v:[zOcY}JkALS,ܴ%Y۪1A,QNZ% ] xqøEvUg"|%9"ɜv5.J;h;Ɓ,5/9[Ӈ '1:i[t9HSR` rvi,B|{YF#'jgËNZ 4DYg`Xr@VJMԝOm}ekqeC]&aբŝ6π<%|>C:{qo3 SQM#ipŮ&}}U%X9;醉!ۂ-dBmҵl(rnr.4 %Ez!AmyQ}p3WA,O'5. Cp\/r⎹N]l\:&)iN#/ ]f;URCt!‹z9 N|'f­#F0*2NrRnoŇ_qԋƎRIF4&i3C&$oC?ƑXZK]:>ɃSHo3ʏG@J1H~(_C3I͡t=o;F~{" 6Wӝ7HGIB1(dHg_Z'+uB:`̀t[QLUGI s]oLCadrfI]@.obS_WnHnF7@ K~kMwj4|2 S}Ԥ`FT>g5fJՙ(ꥑxK= Q7?B~ӉD܏NXٳ:(: kj. }cH{s',uU-6 ̈n7[3ev!ݺ 1h6i nޒ&,O~*W z`X*Đ?fW>k _.@ ?wg+Hf^HJ8J|zZJYjwJESfU~^;$D)>pR+UBIȾ}wUYsV7̲y> Bg $PZR3D71 ˅!QpǕJ,R?sLm/>!)T',ϞJ)F5K6G!*to"w%FkQٟ~M. @K<:މWAWnlXJ:k @ E%L}5t7P `C+' _6(Ʊ&AJFzRU. "%T&' @٪X.0:GPflέP>綴9h%Y]yT;PYޚVaMw!OT$~>Jt!Oo?O -1Ԓ[h.x͙!N^6=ݶ 71YS鋀79x8zU<vxrW9w mz[-xڝ<;VĠmEWg`Qb.Rf[&} {C NOS0e9a6EgCk¼eYK^$Y3>ɏ{`{ni}D98GBޏ/.򩵈2%Y8V>RP3xl~g题9_I:nxwⶺEB>Q yt {17jݜs ctcDlzb/"m1ċMkFOv8!:^O7aA^A{cg=SF eK~y H"XvEA&ɶ'B4뜕ŭ2/60QbG/1@եR0u˺*l_EIhi]x6;:\T_ZyTy\SK,/rgc! uNF{1Kyo. b餯0xmz,͋jeN- l2͔Cslڈe >?,P}Tc|EPhWӏZ_ -Hm9vA A=ݥ1D" v@I@z*!K+Ӆ蔙)t͕W*t) cZN'q݇ %E9H-/277PL{ FҘ"7G)xRCfC)0Ƴ H-KwDZPtR%W?dRY4|grh-Dk̐A[6Бmy0>.ZZQFRxϣq߻=Na7Ǒg_G7QTۭ-"$q&(0TG&|6r VRC]/AR¼ Y?'cQj/tz(oP>W;>:TBBd<ց#]U + |=U&_.]MaK6+ܭ\ȷJ2qBfvna\ tiwPgL k:l]Jg2wc(wF A?yM{4{aYUjm~ #;ȚCAa9R5wN@М( Sr7;5kOt!B`zCpn",ΉHYmYhd3L9f Ч} ^iP]h]\15WHpi V :_UQ@r|"=sVEk@+$|>]n\;_X9VvQ҈l{fBioWE%w45 B+Dܵ8\ ŅHŸ{$ }ǟlDp+b|ӥ[svɛnO@cZ+siXvgg CտJG3nιm xkh] g@䠜|lLFR`K; ύVc[L YR>5u8]3~M1P#_Ӵ($3"aѓY٘84v>E;lfֳP䚈QDHb#GU՚ 齭VE`qJ Wʞ%(+&) : ?azSvxn^zɋnt{(Lf$> Fڔv%aU@;g"hIBQ؞En$)?hOpNy 4s @S_*ΎUMW`X59gFT:&ne^-X:gׁ賕qAp5Jۇ4'!vB}m!Q>Dcm9hҳ.cĊv,k{ȷ* G4) ezѢ1q[II26| |ܓJns鹽[S)39C2hUad} 5ll@]3GdOQ2r}oWm %Fn5 Hx>~~=W>' SkA:9#9Rї"쪩xtD#BA^aRpu`(״gNR R2m B%Sqv r!h[B)5*kII&ci>)qcq/2vrfx{Kq!kmscY)a8^[BV:# =b>։Ll_?Di~pe!+nAU)X2;!aNȯIv,bͥ VA's`"6/w YL턯=5`j}t$!"M 4iBUJnh"`5!\mߑt{l;p;3@:\Y5C[o۬~x\b팺}V`=NXd)u歫fܒl8>Dr83,5CKÔ:t C%B>}ɮ[&el}_Lo&Ob!9O ysjrP!~Z۸DuE-`$_z]9[?q`xMyGeL[8+\b(90+sm]:3}QM*"{v`@05ӑOaBĮu\iϺ-}ݢg&/ءǵ2xZ`,Olv sθb3b+LG5ra'5UAHKHe~]aƽs7"`öpu,ț yDڈݔSpۥY,/Un9iW (V?y~C }Go]9[ԏKoĶƦxXH,gk٥^BU t:-ZW}/{s)p  db0ug 1 `ՅR(loP /U[̎Z8X.|⟨ywnz!2vtnT>D9+dUQٶ(j}״B9ZB^nl꒧ש^h%B޵\Bqlg#^}w]Aiu4_ܘm[ `x@h h(vG%pن!t]l~35iLrr%oY}MVOi[\smnLD~g>BPZ avįn-w[ʍW\x?VMOkI42\9D4A3UNE^mnX._qUQ:n}\UVj臨/Fl~6⍠61v r}4M+'?Īn|`+Iܐ%0 e(ۂ|+B|ߠ7G+"Bԟ{jZnTzZ,!2 ҝ%dhK!IW߮z_pZL`5v뫅+^B1K&+r'&N5{uƽy U*+DD`[ razJ@U.G86!pLJH3\ZuA{Jq%ZOn 9 G7>t^Oorr|ojeW\q]~|gxe2]OFU#ΓiҭW1߇*,8/0.H#.{ċ"L^z =MoN.1gP7Qr4X\^l\8ɧT __|%~i㙙ZOiQk7s$mh^}fȮfIlZVC@p{@}ǙDxq{M:jk$p/*(pBZf~۳#dԪ0c'$\RqƵ-6>;mCޅ'putbM/G,yNr$ wI5Ԗ^VҠ@x/m{GƄuʀH qW =(_UK*E* 2>*O&C8+f'–ugDhKY?,F >T;*.N0Wb^er@~ N`n5 XXGoۄtgu r1 u`~-)g^0N*=E_;q#TG5߭t8Hw󜚗̧CB1":nWU뎏/NEz>T< .WwJ*}sXgVV2PԜvbj(PMܲ)yw;RǴ;cDþr|m"s?<%؝@;tM{rc-xL`=7/y ]BwVuX2X P<@PZF(۫3ƭ$yzJ*+(|Ż&@6D϶ *ͣ#bd_C www 9r@"\3@I%b&.b-R5cQi#]݂ƅ尹w /S_ vN1CӢҝSbP`n6'}$@k8RRGw@Z/7"[ ?_II(v.Cx(Z~fz9Gձ'};)aˇ*H;/êã_@XEMqZ _tO:iTRwW/ed96j3r@QvUڴEIפ5eڭs+enxB–zT4v$UW]a|oJY;7}~'=`d,fSʘ@FSk^vRbe>@rnS%)plMO܇IG;fBvk[P  ] \Oշ4Ӡ#Q &{SZI* xg}㯒vk'شcEl=\+?vJHAFZQ{1P޽~~q)ۦ!ri,W4&vѐMd;*jG)'QjÍyFPXZuͲ=ES _B>ZJI3ӝ9up)@"A\.1$x悯(Peb(=~RH5H) @p2;GŊn~yj%O.t>.Ȝ$8^~] Y3?<bx[صo :ybCq2 }K}GWzZe: !XWJ7eg;=0y$U9D\S:bsQLԑB_4P^9edz4d)ԝǑ,1*NMIL& j;RUZVQemw:A|~6 -DoDm EwfTT!l%7ܥq?\zk)MQ"À'AOuL$c/k>Zꩽ~qK&)/I?#sT70B^}Fhm>-O72y;ñW jĥm9n鉼-JhC~Nj]ж3x:1d2dJskd;ϿH쬧e(%v{̓H 1}O*:!-hd/A>DzSHT^A·I˄SȱHDLEe%VZȊSHQ0:L$Ӟn ̻9RAp ){,K2d&i}Z+`C>WigIU_O{DKH:/9}vUH;TTNfGgaU#B+Tb)V$] 튛! Z}z]VU8-(Q]6ſ[ctݜ/>J BOwǢDk;bcUS];$:vW׿ʹ}篂R:&$Anah9ږtðX5ӫDO&u$^, ,jUgK(0\mtg:"E>x.?b qOL 2BEDrJݺ% '>|$փמ|ŋU ƥF6RʣdgY̶߭~9 4lb*N-P-|g*գEA [H~d+9^b^p 'u 殬`g|>{}NDvA+9]o|_Jҗ`HI}'D]!AGˋ 7muv=OGwd$O*H3M;!xYyȪy=qhsv71KU8dH^b\_. dP;Aq8lC~CE~̥ٟ^Nzhv;#CܻH$Sct*"_?}f|yyuU ;8ֺ]ΡQ)k"UC ɥ :슌r')O"g{?L](ĎW^P]CA|*gCD,sf}}3sP&rwn-:'aCʍ(/j$͛MxƔ.G*\,2 ^yvFXUc4L\ }2pMl}}M(:[wǝczy'3 X)Xqk[b @oIAk87. O(?cSr(k kMKrQ@U6[s]sYU8ƂH߯t}#Ϭ.W@0Ւ{fl*0\'GA &=>rbk>?P#N[ nbu17~d@xkF碚G7`[yj4u{k?r|m*#];ߩg=s%_;!;X;csPn~:ؕ^n!Pl=]=_[>O FBSG,َ䩴Z]A('qT~0k\Rʪ1w<>ȗknGAM*iwV]c"r9|,_eBMlIz&#qV&"hNnku"|>u@i_Q[=z8$RG20\MoJw/ 'C92E2xd8D*7ºpX˽(*+t*6 ^ b7JU2'EX֩eJAXKw m6h*3kϕp+(UnQ8ѹ$򸈆|*XO`\eiq|Fk)؏ZeŹ>)_b#G}Br k Λ9l@\<]kў P.Dσ l' \]{zHT._AHsFhۧe4&Sq+Hb*ġ \_bW]bĄW# VZ8ESuzpgLGP{pӭMa4e `J0E2m4tgɫd: D8Ԭ&c3/%6\{JELpt ?^Li N/}[nw+?!e < ⣥=ltѲ Rت v2{YRoN081rYٔZkH(6ݽ_c@S+2V_ՙVsP^{_}cnA46fڙbF?:xy 1JdoJNw|q}ih}yzfF)I~d4GqëMh#MJH`^n ;b ;va~%:ٷwn-)x\=LrBs!u$6zW\ $ej: 萯?UB5{G: V!ROeWFz_-d"#j-~$Y˼qG![/z e @b^kiHn~x%wo xO:j  @fÿ{ڔ$AFtoQ O*Dֽ!mΈNEjzL2]1/((@=Tzp]GitnA|8 jگ95O!AL*Z<|J^IuM?"hVv + u~@¢uʒ >9~^PH9_"Dz$/f5PZy9= D581BDQ/%mg!ɺPf]&m @l v-78Z/3]x{nMFtZaAkS(tY_$}Z^׉iwPONE9o%I-Hi8OѢTTІ;d| 쒧Es/ Ulݶ4B'$_*$x$?I/ydrsՒndtCӂw>ohFAһWƒ\4zFnHYW 4v`ژüZsDBb& #@<6?r*4t2He3r"C$MxZO1Iulf 49obqc+c|ʠ#%5w.b9L}m{TpzUj9'^BsH;O8cG6&{=S=ړ:A }n Pp1zn\t-QSU_(@t/n= "?,pܤHw$r`6+Ůѽoa pfr j9"#|H9=jq?Nw>dD{!cr iD¤ǏA0mTrnU}2|c**^G.х\^p>#ZZ1v7W%W=>Dj9HMKd"yO^BE!)#BJ#h-sq&$&26Xߪ"{'8'DW9iݝ\:ܤU RעF'w^c^DgZ_Ȇ~ Ap !#dWzZsX u;0rAS ò<{v>C]*ό!*jq9цg+GfЫ]0OUEq5;V`m>Zf2Ed;n}v..^([R͗"Jy`ٶӾ+||pFl @ zjW| }Pߞ>u)+8dG"vd7fv5X&5QXלh Q"|+x /zv' $"iA 쏲`C2AOihX lJs1wk%koא[ mh$[!+ݓSm 򽗈)A-\8%D66!@2PgAȖ ]^fMhvm*,1su&mU(fQX>~J$ǑZ+$m?f_zE5쁉cbos[t6v_~˸F]L#5\י+jH$#2z"f&^{S#+ ɸeǸ g1af"|m"f#߾XK2R0lLFa!$2LQkx2xM&$d,HL4pnFtWD4d,-%d)2+Q ?/XCԪDs&ljE#7yw97q)ŤZPS⊷CstlV~A{V 5sƦ|x]>:#$[FZذ~pv%bۢ V7 vt(`8Ya jtp'Sl"*9k O^Y0syk,$Ƈ<mN @y}0rwe.`@>R"Z"ķm*xDPV"b;|yEI߉peCX3NwP{tD#0+ؗH#!VXeMRm JMӂ.CݞGzlBB<2qID~6:)"P@"faY 99 h2IZaovHv!Y@=U"&~C+DJ:w-g쁦 wz'm)zTLJu`7(9s8?ZYVmޯ|&{.6 ionS\o6]q},ߩ_vLK[m;=;͐:LdH`Xבo7I'p\ K ǭ#j{%jf}鋌`5E1uBr#Jʛ AT:6a5?cxiKo9|[+!po~Rm !,R_PMgd"Q:.Lk4&WHS@!w32Jy-" LPk B@ۥ؉Yttw}6wOZ*$3VuU9%/3{7u!O䈧Y!эetBQn#pG5[T'9╯"%[J@bτn7TBAmMȗɺ,49"D0Rj_ /gK{dؐ+zWdL&o ƾ.Y"a#o!e.Rm_JP;#‰r\ģaot&͸WVwr5T;;KjDW~_seUzsuۓH/C -J1N#uF2g{!)x[a- Ƣ:Gp5>I/VEem03FP! ӧ$pW5+V,:OoI~"􃜸NO {U^I˗ΚmO,r#U]1_5[S ٯ~Fق2Ѧ j)f j05"a)fXz,d5z^t}݇\_ JZk̩KˌJCfvK} >o,ůHfL }qR7<=(l]CS5$ 1݂JMw \#ފ|Gz5 :FnZnCD-4UBqizp8/wvR"}YXNׅZgG"@0@HVX.DwQ^)(9tT`%3J4(8e3K$[PkIJ7)KS/ D3ss<%w'š}IM=AxtځrZ?Tُ"wiIh8 XyyBZ@Сq꘨TL"K< ӆz~ސVn^ k.Xe^)0 O`Pܘr>"Ju4=0X.Re11ONB條yD۴B-p|3JnBmQ_fYAR{j 5/ /=|ɈQheIz.1+ZS"/ɂo"\.z6E!#u2]aOSjS71_G2@pDM.eȥM ?6EquFXzq :4ذdQgXֺvC+Ic\,?XJآU0EZ"4Fst :-> EE*<kt5a@t0y=spJ'qM6H,@3ܽ%,(@tƉLXgl6y-z9́3 7/>RUHlKy؎b&WYHuWWzL2HQh-\BQ$'Eq ʐSv-1ر-*0B3f|It?a,V=Ge Acx|u|Tt7hbhRmf4܆N{W9/q#jÛG>CljZt `CU4覗n0cr̞Ԛ̵[IM{?$T/O_ޤc&\MwnBr37=,(Y^E&FD}Cʌw!uRR>.rRBz6V?G]ST`:Gg>s 'ޏ;r"A;f()c'bsp0שu#AHOo);,AiC4_n܏XwVE/hgbMuoN|ڇ hu!Ji'}PqyΒBCv5 hC?(T[f+;(Ģ+oBBE~ 'dD?@|u]FV6P&Ɍ!ӽAkgѾ0*k.ٛ+JXD.a~dHF_@dER_W!~_"rtC2>PCPܠXJ E2/!IJdIpEZDb|& eYŕ iź m v^$ RxeBWFlPj1[o-ܛMRQrn/#0o2%AB{c<n/cǣ c<}BK V 3i'v:=Q!JX^lPvC%P{Kň:-:D\SxWQxyVKB](:T,H2dՈdى!qa1pE~dtc0hA' mba2o@뮢Ubn{ C&a aD`9ږ#y)"U W }?]ؐ(pеu4 sc`aD碾ov!RtrV!ކoj?\ɑpr97Ev{Ԡ+LnJY DCf.tߕɐ趫 1Z3"<&k4z!!uC LF`)?d&$efw lVq46XKHީMNt,&IߙI8n"۳co[7ʕϕNVe x`RqQӡ'FL@W`pC;R6Ҿbw /#"ɩ\Nd'5.B-Ie&dM;01e n]W~^yBϯlB0|],Vp ".oF+3>.Ȃzڄ=WEOȚh"ZN;+{r)Ѕڥsg2&<4yܻeE&d ыFI5Wjf^ 'C:8T孨#T( IțZ-6Nګ iYh݉_SaQÓD3~Nw]`B( Sa]/G6r۶f;1%q2YQwL ;MU\ o(_M&]|x' OԮs!{3N{ XM6"*t2̅kdW%vŸkE+pǺ;ƸoR^̓fi+YXG.:7Q)s Zb}k IO|.zN+!0lDԙU-.Mig׎}Hd Ž|V,`CA ʕx0y:N\CG00˰z+z 8>~iIs#K oBU5m D:SRD^a^Ō%{!v(~ܾw `f$WjkR͔Xݐ%C.= >¯^ƍ̀};˾/~↼bd]X>Qqr?+mϑ#oVABHnDdͬb CFW$Jd{'H}oSnfO+؉:-jzd5opw:aw߰Ȅ||7-lz1nfN+=SŪ!jS)&_j p&] G6HЊb>g(#,,02;tݢ;p PSh3ykۜ8qibn Ћ}AAg?C ~|{x¡@6{fq}'PN:\Oc1q^pّbIǷV*z yK$h6F0@#ԡqf4mޒ8!`b}t#jl@V/H; :SFS5EZ+{Dvb!$ə''`|D"ƀZQ_n_]V`0NzB=vqƞDv`n xD'YJ,S>P0, VyJN.EWlL$oH6W*U<=^lb>BR"sV( 8nw3mྣo #1igYƻ|=tE@,?hIz5.~qF ߆%X|r_w>m;1;b6pƹD&m [ e+J@RNYc0t~V]!ְCb'= gXL*uٟmܞv ?XQ3(:/ &?$β+ЖS~Dq[F5 WoyTD@u2e7$:k "Jvsև|Ö:{ux'j4e Gq{M5pYBKή27߂|̤]JJ*rCD@-X@$O]݈In|-L+ 7BD΀ DC.^tjw9P E-QF{ĴF CGN@Bm{nHOw+l:k?͓KG zS +1YX;[OBFN!2Ca370Ԍ nȸZ׎|goBb- +Z@'PEzq٠/Ves׻@YI,tƚ[%P!~3%t*B]7bꥅ2}nb]¨Q9N6ȷBWjePѩ9(rw`N/Dw/79$xmT)X$:WX_05^/Nt!r$Q"fU&X7"SI8x 37=H@0yL[΂ejO?qRZ?)I;lPm.naF֚oj:KUHn g"3f7[S R]tjlFrO⎲Ju`5$C U=Ix\~H!fg)/ q!vc6lSSX9}I%ԃB wC!Ӡ݆}YrcF歾!]AOd hXtX$|h\T"X/t* zu1R~~ YtM[B j^f%[݈!&$5Q_bجʁr\dvqv{R|n+OgmyVwJ73?J?رϫ,8v:kCM`^_=1OOv@.V dtR͚)^>DԡVx%V)w 5َp_!+[v+Q2q\w lE@\![׌26 n(JaL/͊Le˥p$T yL5w(*D)H64 ~ߓ`gcdXue˽e'#5KjW+`\_'cF@-8?!u{R >R/X:eM3֍5:bU݂\, <~55:L\0YV=JoU=ER?床fJ2u F&NPX}Qiy{ ]-S0Q ]5$9>m@7:Kv"?t'zW»#-Rq5Xd8Wwk#+ ܩ&?!+oH:۝@f5dp,7/P&|lÍ jl=3y\&uM{@Vm\RЊ,Ţ~u7G)@41횣 =m 1ۇs=['Bua%/`c-sלoض#/읬)9o2{`"#䀐42J|TuC@ $"Hr#=2a/ IAfTX07nɽXi.g}1ܴ*ݹo[c7dܻXa!, ;Z A#g֕n9! Xx6m<,2Ţ`0gN&H?Tn!o SA¡h|8תV!z`=>wۿfm뒬I7<i3P?7W^G@D4PjLy]Y*bM/R?IFWĒj|]yU54qW텊 hHz?T"L#~)]K@ <V/ IɣV+?pf'dC0IJ;mus}d \{=.BJaZY6a@dՅJk,GF%CzU0m&\g@Mx N/)$/sh垷EVǩvf#k2dU'?V 8Ex<י_y/I? ڣWoĚOAݖ،cLNz~gs5GYZYg'h@e:SɻC87B7yKn*';w tDn оۤp>}<852 #GSM* BZ4\:)&SW~+rs+(JwzC 碇jp_\L{ƐB}ul? ́c{D` 5&Y0ٌTÅ7G UY`cvV&| L4$xP>t5Kz2P9KC'3+ -{qZmV? ɜ1}0A̡!za|w5o14ȴ/2K',>7|'J1v^|CQ+\WMC;bK*h.|.5p̒VU^C:4,X Wt" GlL('B?mVP88R'ϔNII%*c|Ro~5*&' 1e1u*5jň4p|Y2=bw'YJ"Ҭ'3Q`?ip`%4ʚSFT,l1-hhQ:/-JAz*]?-8M2١,؎c7[怿p0Ye*Q\'1vge M?hibprrDL# iI/Vaң;(ą88UmΆDC](|W:_aHst)Ԝ& vo`zNw#O>@ÿL%@#_q Br/MS\~@.d|pՆ=(a(Wd|׈u@2{/` r$PzjβQoe/" >镾en;*Fm*d`<0P$}zj 7P_Pdh [U5ˤ: θHt?~{jB)BÔ,ɜ⏞4Ax_)Y^+529(o݋|doiA$($T@\BL׉' Z'K=dd+uST ղ36R0 d"^pW/ް NR]e9jf@ Wnc̏{hsLn[n܄cj}KW .i9'] ^)+2Qs< vݶai,Ric3$RllD(>Tpu߇o?w݋vchHuY!-Eg/z`2c'yY,rF# !]= o $M 풖m2`19nG̻'QU7n wdROװ!L O >T@PeWfh5*ŖWPt Ul\ǔpб}@Ln#OPYet _~"2"F_;tK0EҐ-0J7vuZQ߱Ѽ`Ne7E@] qE0ppHc悘o ^3 eQ0Iz2ٌ=RvsO_k#2%"ܫ42߽t6-wdh'vKbZ%. q, yOB6i`o3Zk7@dlXN?Ir9'5HG섗DRC@G7MWV)t_1N>L|Dl` 4$=#r# {?)e^kϊn'53\n|8;6>BF]J{aD8aWtqޔHAk]B[ 4_[kz6Zf_وy%:%WE2nN?%.TP-9j4#!D!6-h%Q*fI+GcP6|L™J#;D{׮5)|f{׺/=E:EF亼"@vAύUӡtR*Cu'@lHֈNV319'{DdWzo55뙑 !d00Hh 1ODƿxflܞ^JlV"#Nc6Č /I&ݢMfi~)F8 (0~O]dDY31/ixVR} 9z k] # Ȫ-QBȼl T:1`g%49$)%5Sh,9m%)3R\,ml g=7Q)ZsF󬇫˜/ȗU2hN.Rɚd@ .8`92 ͭi_u!UuC:Q)>/9Xd$L5/ҍnVi|3y₃t_Z3+#w!Hg.(E7l,`da4(u>~9q Q3l HE]HBR c43Eoa=i bOI7|q,Mx.)]H!;nûdSlKQ@̌toFH pZ4vz:@nڻG\@Q!R*zT$EY9x%n$/57y 3T*6gkx';de,{]QFu*M/F"6Mktj@fT B =.~t(=N}{CqNoLI%`h7F0~+t{+ gE CG1ǷP~ysĞEkz5jc (۝IǟϗPC|"Ԭل$1L]&}=_1"WFؔ|0p8!-=p9^;BV;P>GW/hʒX"]̀5e+~Yq/4 Xezx_'\>*Jo~KBRv,' H Z8u؉[/Q `ӎ6P)acD@M2FFqQړ>KCv mQqI36@M䗐H.*/r}Nj dA9ijjP2H!7u۰N -MI-Xf0;bi jBȉt?%{^tҷݼeS $BRѮl$<8ou2'A?0ʣKxoQv"M|9P:D@XRR)1ɢsA Yue>!9uSݐ"rKb"\x7.v!I,ɽ@#B/ 4_)0}":(FjK]UWTLlr\cFUCMDA5H ܔD~[tNٓq`VĨurdIQm \RNtKIo:1oz⊍QUiX xk٬Ky(8Sca9AS".@&WMG}Qj`8_ta5k rߴjpu!(29W Lgqj{lEԛ͎"r֝H 8_*7L )Ț*Xּ΢$d՟u w`1V!3-y>m<:3ٲ d.0~b15B%]v]bkqR6Hnr[R0O)Y 3)tQŨvZ D`Y}Ld"4feJQ@Fy/E "AN@q{\uWNQ$\wm,1i\qMPDh'(tWjdp2G*Z@aCmĔ;V@0򦵞>'T3! q/^ hʾAH31`6=?^6C##/  #}(k6TjOw]`6#zܱ Uo{fV6&I3-pH{mXLw6|荹.a}3owg*E=> )h~={n4q ,'G%5f>tix H{P靁Z$\ ~bc^ J;0Γ.l5d;m*J귗p7&]T @b)~vd~Z0OpF=O iCF@cA)HEJvRBiT0^mt~I;Y4WG/К Я>D&uހ:U%nS4mWz~A"+$ Oσ.kľK")C06&69Ⳗ@_z*}X XCF(' {IˎLpU `gU{%|0u bzHz2vcjhD+"GCV!`HC^'@͛bޞNg?3)'8nY͒^l8(@èEk/Do,ۺ5,q8ˡtMYזUjf)r Smrʘi昬HTiU'fQrŻHemm{No&]߰'A}Cj}Or{T>nd{IlbjmkҸZ+aF 4WS8I7 *v6e=4'/ۯpYdŪvމq (gf5-7?voߜԧv Ke'v+]=;h#)BWkr,Dki#@{Nš2GcQ#zhD 5:ꃌW#ݪv:̩B@-, m]Dp4%Xp3q88m>V&Wĩ(D5}"=0 OC,'aK=rm uϔa5Ze)Wϼͦ @~LaԨMBp:fSNeR`4" So՝;Q1~GEr:B6C4aSY"V:ocKP^7U@^chZx[ Ι;|`2ȓ14 kD,K0tldip:&\*˄HQÄVG}R?kb ^[2Թ6-ה >v7' ٫'U˚4Pm (Vud4i)7!^V׾v s*lS۬뜆Hc R"?q5\KE8XBХș)055?@IJ BK>3]{ІuC.Z3$_D"?k&|:N, )IjdH2LZB M^.lCOi]~NV'GΠZ !^x$$\ND4Y.m昐kӴqKmCE M28 eJ !pS3C+љ*sMm#+VE?pkr6P?yªYH%{9oS,A8-W3~h'.QC =oP\;tr!:ߑkGՓ@T!C+b~NULR~i,k ٟ̒]g5Wd@1 Jgk j52Ŵ2! ע! :^\a)X&ytb8H)6Cmg\y*(tDV3#oO9 T |o%p j !C5~[/cQPo9$ɜ@d)S 2<Mϩ"A34S2oGdKJ(2v!o굀 "kn;lyslS} &gߏʻA^7Lj*nF*M^ e ֎A@UYd?SvuuJ!Qul'[p9A{<֮+j[*L\+cjF[&pu`>^411 1 o}˖c?fvbɥrmXI%`X?dk!pMx 9&~Q tBAYȼF|I0$#%N[VAJ]TίHsߖת؍]"TGNG{.xl % F븾 I}J^$p* ^c:korH!٣3{v02:I I3J~r]_m|vctuu␡/gHT5ZDgwr."N'];%C3XH䮉ܳ?0ֺ>&$:BIw\oIhjG!f#`md$.AI#\.jR/O"53*0!wkKJuFc&RBvJ !崗 Sh[CڋvMf5Q&}\>~S.Zvˀhפ Fb^"Z'֊%8:A}wHBĠRdY#d:Q\udPϪpK: Z>ր@"d=`dzHH١̔S"͎j8F/y 0 ݐ&@26Ŵ2go G!ܽl18L&-b/S/tx鉯f<t3E= |iJASK:D "\3D N3 Nkz6FTM,  :t⇣Xl"!sʆ9z {ݨ^D^/W1F9BJUՍQsי@Ry:ɻWK&ixm- :Fȼ ]Ȧ/ @ʄuR@vCfr i~rݚ$&YXtSA\%K%5T\6䏍 6?b$BF] 9u4{Qf]c,.^"K5X{tiy?kYoH"+O(F#$7(Nv05P|{>Zy\45 +qq/5RfBbk[$*Gmi*_%u2t4d#Ӄ75/.jwb󮌐)2Pik.$j -Eٜ8h`8 yD A\fd`M:D/ |Q6guѫi$e,94ke, HwsY)m~$` i}զF jcvM1!Ĺ Șވ&^*KvXcF+9 Ͳ7'n!ơuu/DecYG#p~ SkV%~ m7 oI.op\m2\)PGexV Dتb?ޭ?BD;Bn=%r= ŽYG_iQaMG3u:c !v+J߮3&QM&";Nޑ(x'7-{[vjV6*7"bGl3NKO`R[ R{B1"%eeI9A0jy% ~^9ٻӱ!$F4jz8/܀`Y2$MNHQȀJhFdkEC>/q''NYE}n64[$c`$HTJD(MH?`~D4Q(j!\`25ї]X&]ER;kNHDlz#1FmX#@y:>&=%{61r l5L11W3U4tE6y7q|4 "{$cɵLO@ʂDiQ "D4~4Uo8 n7p# u=)̊w?!VG+!v F p 3 eͲ P\_׿xDƂxv~mȳKixmHYuoM1}Y%b~)m 괋N"d53IrվwEO?& mbV"όL\u v e>wj"ɢ;8czƐ9߶o`o9B,X~ Z.>/)nQs9"nr'f d%`9q7z}y[a3,nS|(Y 4CIaQW~ǦPz5Y?%c\'|V.=%YfF,+>) z^3T䝐L5 EF̶u3H)CH:K[њe}=z&`[ӶNo:I ^'0?Y\oȷz;*7A㼧}8c9;l(mX+Nn4iehÇptF ecI:mjәBʅ}ago}wgo)Ӥ.1w+k'6ocR v/l#[,w{*2:M8~hDulGLg\KR|D`ƏOHMy.+H[QƗm,B,`mOXK cCV WYFIHfI߫ay3㲡T%ZuQ(WL"i}T5tl&wHGWj>zBZHQbv9;n#oORґ>X{kvۈbz'I.{wjQu[l|1 S ◲׌ N\ i7H%M3nA&ȂK/)KewyFڍILӍX8\3 J= 90YSTd}jcV )q@QUгbtqh6u:Sgv%޹bص'AKF*IV?dH-NweLߵk9\-7^9Ho:k-[ Dj:ETD. Z Dԇj 1&{rj6sR$-,Ci@ _"ŊzJ x2#I!bG/ |?k|K.)DvZB6udEGPKX9քnrHi(>M]Dmv ":6z*<g9)$:68u0/E%h JW?=KM|c Qg8N$$":=a"T`[t<*Fxk_M BϵXno \u͚ģٴO(uOx^GaS9b@ ;u ,r!$U5;}<,_-:4ݰA4ՄZWH}@-$LOYo. ]\jP@<BVƣ";Iɧ܅CLO7w!o@Zv7f4S 8g~"1:nI8M,fŽ@ʣ+ )Ҋ&a3ӽP`ٰ+ <+QvzoI$ aVgRIɽ4Z8; 7صֳ?Gz8$ͤ EIdE{%!FXmuJ0t|gb$a2>NnF/Gys^L4$87E dB4CNnt^a\!RH8.bڵZ6czvK0LQBJĴb_,#%!dzVɷ@"iG <&`fi# SO{H)K^ S$j!%N?YtYU'd)ID^@eZ J)#aisqd@`SDaKfqڌd5!UYJb!d*HQ$:YpBSnW#]Ɖ2( { s!t|wV78ܒX&dϯh"ZL % 'ˮ; *YB>ɩڣ$soIqP' n[OM=̷}CӲ0,sԝs,$$N40^nDuI[)3YX@)KgO o܂i&ѽ+x=)ik|'ޗ6Z-+^U'nqf:>pE,엡vޠ,BöWYRXlcZui=7aČg>qmBP#3[,s~egh@FIE!d չb)l|GE}ا.ZqVwɨ:7h 9My!v)>sFl>8d^=Bx3'ƊК9;'!k2@4ӏqZZyBjuȚ2!u,H3 ;3(vFs# m+:JE‹jj(7k|H٩ hEƸ_ggN5=\Dce?ŗ(8tϮ=Z#ZrhT;Zu>0_+2$ fp_֕+oGdz %DB@HnsL_4)@a_B"6#8 A|#H^֜rό|xc}e+c]ld!{Q= <H#yE۴ΚR;7[~2{JBkrܖruK>^d@BjWw?1[ "\et i:H}4|v}jX΍ӊ3 %FcR>#:[ B$ p_;@(S֨&oaC"V m\&L= CT@֪ywV@,#{gChE';$fn\$'ԗK!-W8H21^QeDz'K/"KSJV幖qDUˠAڈ#XQ ss2V AΖIA=R.!5'X>6SMiV \ڭ ~Uŕ ח _O>9=5 c+Yu; ob5But`nj>')]^)E~7-Ӽ^' *)%R;CnHkf.TArYn£~c|9=\G GcΓ֡UtD-zvD>vSuK,G Ub'OkԱB-[ %.f#s24}dAHOyG@/OIkVO6PؤS z}k pB^BiTYGvA+]B \l5`2j z/o5e_b= ;;.B662+:M37eW?:u-9xlU {iwwgu- RN7|wic˰;=ca1dw9@ß#!g4lcfvdXЋͪ'dEkqTF{z:1;edD0}} /\8+f~L 62M/3?Y{1?Q"!b;po4ȧeu S XPVea?k5vT@5[9C|_"?hI=XH9ff )[Y6֯5thps·S 'A~(8+]DީV^6!5#XKJ if!"wɂ+ڄE}DDޚeġ)-R@!lj!)'E=ΕbLpGܑleziRS6daƒ@kTHYyk%wh/GmO-B aR~A)]䐠;ܘ38t1}gIDij <U-kJUCl#Gd%.ΒP$-9E2{,|prD..zPe^wՐgQܔ#?tf~{鼨/r:+$4u{XHoVo 5! c&_GA$?d@7q~{ZK"td_xؙ(e 9[.BU."cV )LP?3~*N 6.Ik`n kL΢oYs% 6Ԛmyڈp! 0>!ݎ-~vCMb&fpw`# "׳*  ۗl~8.A2P 4duEnBPAIwpD=ӓqi9P8vC~WzbQD0+;s~XII?C^C&1xY;q2"NBf< @KH [I ܄G!T@2Wٝ2x!eޚ=ZH8gLx^Ge:⬹?,H>y䛇ǝs5p"#J1j3Q'8 ҧܵyL /z7PL.($hYyC" $^E zjL"ɳ/D:/ \BHIhr\|!ah.>ŰF#L]NXOugBB_Aqd濾f`׋RPoG cRf)Co2kSDliLBE;i433|׺&Y5w{*~&j϶13s#l ^yڦidM8hhta+Ewr9 ~- _Y,A24wְN=udC_I+P}pկ@Y)Bw/&#KJVH_!* _oƧ}w$$i#C:JKuب4k~/xK,r A!Ux 6"^L}H8lLy,! cB \&G)Y*xpFf$#zn5xV^G[ hat W;\IUJ\O5fuF^Pg)Z/K~V녬`etS@I44:~ǩBM~%}ȹZA1En{b$n8(SYl1&B3XmY-8!{@!2THzgpCb3[]ר -*xIm1udG̗eqQtGTmr D+H[{/J%aO֚N &@NULBk&B)aC1"H(?։,Żޟ?S"J%?OWFH]HOHgFfv0Puf:odzr&d' ! dVH;~;ߕ qݸңَvUHW΁zqR7"L\pB 6}w^`[H]=tS4 GywD+}awGqz>.c%(P+ u>[[k"9M IC' F@K:2324!AsTRB Y,7Azg)iæcRoD ?NB&$u*SX3loCcq,]] Hٍ {< U!*ڧYfl;0A^#~ R6I@>qAf G؞6FQ[ ]I-/<3YaXtkH%ҨYYS1r(n{DtwSjeb%`ݠM܊p(ό΋<I,L[,&d(WcGN7p {.)iڄpJ \.T-<v̖'nTbL:xf$;?z<4aẕXA˪w@n IL4S|Ub>FVa Vͮ 92>H+Ąys1!vNCS<$ ON\fܰn Ϧg p@[k.>U!Ti\ @Sm 1*? {Lt>惰_ӰW=3}T7qhٜJa*OHkk|y\-&:quAWNw+F03(o̢L׹AЖ9!`knH~eb %^f-DZP{&LN4R6Ss~DKqt(אE=&Ú);f3 ɴ!q<} Fcg8oV7FĬ|V ڃ $J2A}߈ A\')nhe%ґIy& OPeK>d.ȴlWB[B"c:g|LꑨiYSY#]ɕûNT4hd^,!.Ғ[QtSmCq}lQ$)ƪN,6Ãfđau.a*iQMPjs@ 2x9ҹo}03DU%70mxHbZp'rwj]Yĝ1ndⰦmZz3_Xnu2xts+WH蠟?Ǎ̕.˚ArrgCd1ˋNץR"gG]e3@"aBF~#H]f1Zmoz)x"Np+K(e{DQ֝JRYagsi1!B،mH3!.<B)jwv;65tJ\ے@${f'dW̝"XuL<& n`a@‘D5Ɉqo97wӍHh X"į05I37(GF  @.:g#ٍzrܐ}PGY[ sdk$YP;K~Bm$2LbL֟ :2pf'- qtBʸGTvvg6yJ3IWHw(.ľtLOgt)Ne~{&+h;#eҽBq![bdY'6@H @Qq^/ҕw|ȅP.Ա;] )Q9|l{;#B\Q~Nb 07*q@OY 1zΊ]׾vrZvGBwuSחݚ(GĐBP"ߎHa}]7cg<FHf>~ck5l=< & }| ۟K $3IdF(<%sbP@$X/FY"S )%)6rX ICs~m2p(9u/X!vr~I<wŹY٫![!ܥw#4Qg dj2} ߋ& ӹE̝ȓ"m}Qzqz ^/bׁ` DxN:> 1pv1s7 2Çӕ~e  t=oQ(DV/" >0^ \J8hqslC 1 ȲK+֑E=RMeN_g(م,`PfU^PcUŏMsw]r?EVh~Z %- Hq]+n)o` 4se79{Z0Cf9mdcd1SF\R8GEɱ){Z UsiP fm7C5 /Z%"bwpth?D^Lu96u6ђ KK0DF]>؉|̖[6:g\?B4$DY-]"~ƫD'*ۜ\;JvQbp?w2h EvhQ\-D V`bN9||2i;G!OP1rƄt>Du_dRSI/wtYT<3 H9jk dnF*VmF:k差u0g&#o-]ۧ}k.ޱ@hp+WE~ԗ_,5~N(rs7'S 6.E厍S#?˽ goowlW6e-w'ao̽"jI~-l!09T$ȵO@Nܷgv[2(nԖ;_\r#Qb0Ye# IQzh(ym+E($2ͬD*}I!2 <8T~6k#@nD% \i.OS$vVV*\WU7d@gDp͚< }ʴvͨmQ˔4?d]K̰71#}nxBxz*-wםwQd .X_vFK%8d.0m3sD WhKA2y?/<.v%CL#L*ϳD#Eb “͗/3aUSEcEؑk3L&_94 R96@Іr*,,nd@tӦߴ`{݁xo8e'~GV!$y&nPw:eJ5 I+/%2kI( 8%rI MwvJτpKHcM6)}5&N%p~##SS Icwƍh]wZѓĽQjO]^H MT-=kZ]TPq D@(f{'Yi6!S)2a'L)bjL̫:|5VugN2eaIB+vsYPƱ9GAD1XDT./^7==;nzo z)7g%֊gT&͕{x~(tMQr| T2z԰—5zŨFA p^Get-_΁9筢3!݇ 9O)'Ov.BT!pf@Đ\s-.e ٔt4X[GM8*]눽kG◖4c.Y9M۫J򵗍w &ɲ"2\@ oԹOHN"?(ZVX jҖw]f/n,T.]OE rDĹ+#f/~zF>HQ3%p\֍5G4ݳ0 n`b$6Y՞j] ސ*(wY<ƒs5q.\۷Ek:wS9h< 6_<B$xv܆61lѽ4Q%'od}diCD!2$4,!Jݷm$F rcRq/kpȹXקB p7Džb_ߩ(H3!*߇>rx]BtdK|Lm%ҿMFČ/2(^(& wS҃@6pU*dTR*B`3my@H.rJz $)FjD,z/n'KqQf#0|.ٻ"?߆<c D rd}jȪUJ7J⩾x!j+YțtГ1@rU=')ƻȐ^55vWJj;R%u_J]\H7uc0>1VO6Q{PT88cFn8<- &cT籠=jHVq)K)B:y!NU0π]@AWݳPSy1tJRÁPiG+nt[fG1 ) O[u %&6="_lHgnQnE&khn51*"Jqg/ k's[6@F#Kl@:ؚ:+S{Pg`1?*2`OIi  Zd[~; 'kA+n#l3繯rY~V`iXLb-!ơ #&p$'cFlُ%lo}5.MbNB0Ҩ1?hYN38Y9"*n]3^OgQ9,%Y.6M.;>?^6f֙ Qr&_t,tc**v i8NP$"Nfx$A`DF~JoIxD*xd6}+G-HwʄG\P !LPDNdKLrHX<_#Y#z?x.Uk/s瀏s7Œ^%H #|pw 'v*h-mHQ׉m`ӂ}^E_@5j44#}+z==gCGbcP9txCU".}|S&J"D@ ad<[+[FEF^'1fd쫂lG|$vUW!?rdQ(G7]L"8 &'dr=gMuPzQJU42}mf9MF12"_J^ʕӴBgqs/dhn8{y*"eO}%#mV)MdK xOdw(n9-|&HDo dh@;FFJ[V^κZຐk6#k(A--LuFPr 9Up L>s-: !*6Zucȡg`ML S7hp7wAg8fnA+}?̯g" dzHQㅎaህ};eԁy^&i 9/K^p'}Fm 8[` Y⊿X{"O ~!@vv⋨.@s*+ؾ +_a)vknrX #W21D FnMIY6qR\K*Sߊ#<&5ّb[ ZO1b: 5~Q0u-TpSࠥ}{0eߝٮ=ǝ.m-ơmdfd!8l&{?;yZS1ϟ3 5vqdD0?'KM+;N28$ԉ`λ5?,sn[GvvcDMj;{ǃsyB[@ÿ,:KV)tu,3*5z+2!֊OY^'>$JI./C kd+*D=,-DdS1HSȹACfm^٢u@rr"lfI. V2OJ1qxMJQ8'3Q௩U]L٘4'oto~AƎT{+G-e/qk!gHP#fՔ eG5[3#A"%~dX[@3(Db {inv|{KH#(.oSqD1ċ:ATw6R͑9`Q늊4k91KT΂WY C͌Jn> natGⰴӢ;،m0~\4K)۩#_3ҎtXߍb4t# UK}oבYId{WQH#-5tiE0m=OjfK3~*tY BDL+CPk i}DKNPa,c7)ܾYxuv=J伣k]ˌ._Fϸڐ1J:s]nj !8@}H ,r Br[-Fh%VMs<NJ@>{WzuQ[F4 z!VMmƩdv3g!6لA&&DO䉼 Ii5,4%Q[HlFuoV ubh,^G 6)'nt^ņ& 5ѠN\ ˈ?Q|8"B&SJi)Ck',?Sow!!~b69i9F˖e$l3{V3RSN|+1O}̮ ry9kAo`2lLNPE_H/w?&KR>d#7ݻb ޜD z7 1.bR9+1[1IWCݥU1kX<ћrt?v+ly@i[^HS5r| "É>Wr.BMXH+Bt6s1{ OL^`NHTݢg/00ޜK)Exg^&acDZ 3 N.,gUwlZFGBM*WIBfg )DxU6)]3_lg93ȇʏw/5 E q Q58^o 1Mt[#ʨZ@2%mX1Rn; !^[)餬WF~#TW$<+"O>\/ΌSY5 ih\Œ|Î, Imy@WCL'&";M"L4IJR'eG da4]pjui&P4k#Ep^ح~ukU (;pP H/7Q[,~p\_V5zFMhY5tH9tw7}FzE!Zk5Nίk7&%jX @ 6S+k6wGfItcxpw<+260Z怂r7Ehm!ybٚǐmu.Tlc}b L/I\kr0ӓDw/|jb`s׸ÝRC$$sŨ 'pS>b;@-^7hӅCsy^OP)nFRHїIɟl`;_@Ple*ȉU&\,Di@ИϞ=u~[$"Mnmw$D.JQozm,;!Y`kQ{&A+JC,Y;ȽF}"&8,8plN*`dϳ/™v{"n7OMQG:Y*v:kfpjTa8uƻ4Aqݡ=E>6@6Ɯa9vfV@zҪ\`Û,Hy(M Vh#9,zӏO`ZPo*2>]ʆ 98 `gir  +>aߑգ\fMhw` _{ 70ԣ4=:H4\^)mP,m/`%?V_!SY/@8XI4捂lo !~ l1e oQ(>MT&tPm]z%3IX&e߳`Η5d`sumhdP׶KLIwG Q$񙋘ն ,(;|ɐNSowlumAnhOAȍ)3.`҇`nskk6;qs36j/7%ljmkEB^ 985:VJBujYW)!Mv{/m&bZ6qO\pga3ėKhy$1 fRq&e룽ˈAE8nH9I3P_+#{1 5:sANq4Ԃq9I҃^u&\ 4pPS`x^`:+ڧzD!6vI}VpA&܀.$d@KPui$]5w5\M|X;vÎg';Gݚ,>^'4-6}[?fXvG)ytRF#֡]v~M`r$0SfZ]u:ݖA3h-Yo $Fb@Aq2TlY( Бqb8 6n,{;Ӱ eT1fWɯ&vkX pRxflz"BT}gW#֊=W Aspu攗KnmnVcP&*h߳|%mt wx \փCH(OtSqG&7nD㬦_da ^W?-GxNc Td4~EjAqhCҥʭb뒴x&Jj#K&i FE?qr>{:1La q+GovowA家`~mC/E1Ső#;cdkafvTG l<Y%#AEi2*`<7Mn4Urvy׮^K]v @٬D '^GD56辮Vo !KK3UMHtlyEG׍W88mDI ? i-ؕ~kcx,R6/ (Z L{ PMU,?-Ǵ[G"<=Zt~"5yL{UdVcurcABy:GV%a<%K*&j[\; ӕM5AaC%4;)}f=`p'ZV\ !PN%j!:颒 m00>\oՠUז%$f)8癏$M4Li6nd֚\ `,_^v#oj2u{ppL|8 V\M4PmwuFp/W2#rߕ9O%/E* ^L  C.8]J Tcq@dݣwhW4VL*0WyFg RFrc) Ř,! 5pVMOA t}X 0iY)ccW"~fl}ll2HgLm‹jDQq"e>נ$>W^y >D:ܣȭ t'ӏ W6)v7hoCU@0mU&bĪBɖ:Ժ}v&J€O:}5N03`;B(iŖAc)=ykP~FIFc$Nf̽31dlن&Ժ? =S7lN*=̖s־Wrk'+>:Dž50F_< .d&2`n6q0pN,ڛ}( e]<&-DSi^S%JtCEӻUnᝢJj^EқΤ\=&Iq6j~qÍ `# iԹQyU̫ie2"D|q@,o]&Ґ *J-ֺu[UXtGd~6;w5Id,ؐ(9qmMMgKB9X#a$rr-[AyN0|HoK4!p. OhO"_I rD#CڟB$8.*8X l̏(2O^.~ʺW.%\51+l" MGa,NSkb7e[l&SH:~wBDW+!_v, UwҷS8䆻Ki-8vXD9/Q:%.ʢ?jjYMQ"LˡҘjСɥ4s?o3+\Si"|l:ATy^ݭ>ZQFj㉵54&d؍z+9G_I0z |.{0=/bX20g{#Z5YN0+`Du ͗F,j^c2E1cו 1)q! ^L.h[IDM2[$]PlcIį1 /HU[puP1" 2FA.?JUTxpŁ rYعtmAVV^?pWxxjNpvW:\&ǻ׳GUSp~3qg5Ek2m~]ЭmNfO?也鋪FlBgr@YlxHMI1Fy߽u{b}7:7R֗ρx;H+yYdY96n# /'`l?aḵKPp&00M`nUUSgz_;dHVIu ʠfml34MiW<y]0;LjeYJiVd9UM4z {GЌXdvO+Qk,$ְp(Ì\ZLIlUˆ8|y$ 41ֽ5H1Q|cKFf^D\Ѓ&%{La*y2rc=dup!G#SOCΙܣROKa vKBH` XhbDniԑ0h'J^dJj=Q\a1K708̻>>)ـ|cv.L6+eLpq6D?AMuOџUR՝&ɤN31>X6޼O>R~*aYBt#I@QS|@bnC0Ge<4(1e!Tr7mlS}d]8ߌ³ogqTYOL G&GV5jd.oa '8\rZdJ22W*aky1M{!p2}` KwYNѵ5 9&2Dfg B D~6aY>G{J ;kg (eH)&5ubJt MY/R"rZmtL :vhtA:.աU:>pTE0.)rѻ=$':!'KPm[A*}^3* G"ۨ߄O#Wc[tEaʮڭe- zyfy˧ 3iE,ԘH'*TU`yR5 GrYKxMS\A&( +#@qZzM^{u@@%F h.HD?@̇CK^u{=o*E kwfTθaڍ@ ݤce\7N~S!I%E٤GSw#Ȩıptdq3cA]n%:k⴬dxy[|E`1lyd4}~Wɐ881P1px_6pŨ)=τ]@uL;ٖ|IՠQjMP2QM%N߫a=l˽ a̹ `_qҬ ̾WJM'l&sбh=k6˧9P߬HHJccۮd&V_4)J&x 9X"jl-~2>$qoCg8qâlq7m(\qFeM XVma i }r!ovy (`@ Ke[zWo z]k=&S0(x4q"ņqcGƋQ gggLiKƩ M .9zCkSݖHn9qmbI(4Q\_? s{q&c8d[H4. kmEuRPgJ ؁uXCvZzA+eMpnIaLH6`?^$LM[|7_ɑ9=<S$ԁ v g1 w7 RZiMm]&7^IhrR(K\]gu:_t>ds.WJj'׫el?; k#w%ԸGq"R#L*jJ8]N6ROv> KȤ.yHTf|%hjH~ʼn+q(Qqwv_|l2.V^|EraZ€1 ĺ|k4v ^(=.$"9qѲ>XbOTo^&,njйyN!^ XXuieY/Je^`)xېк-Ar\ `ɚ*̏HikEQ #'F$FX_.j!o&v1m2ݯdX'L5_"a"_9ORy|ԣ?1lc3 &5L%./LX/AM X:-c!#Akׅl'1|esS2Z_\M&ΚVԫ}"T}|hO엽ow}:7GWw;1)Fg K).`_fo]D+MĎI9ڜ_"[@1)؝h֠,?n,I'ܫ(E#.X`&iX $YUUoM x mbӏ1R=1ηC[9E2_>E(Zk}CTkW%BUs 6QA֘PDŪav],Iw.rrLw U͊JWI(DVFq 4PxINF=sj#`ϯlCd\y>`c\P8(NwO =|&U_$[<⢈޲0v[Z?8*W$1/t4K'dZr֫#bo뤀'g6$8Qmi}%7"TiU 2TJf3Y9_J׃Ilk@qMi1̯P܌l>݀8O^LbdJf/K629qb)ߠ}.1= Mi#jUQ  o(р= V]F!j`Bh{u:2-x9vQp<*xuױ'9>M?iF00-;F?j^(aq9 %6P[rIT&r:Ǝvs6LOc/ s{o3|,:+*}j OkyYM@ 8^"N0o%M/P8]p)r}~E\ʼn,ɇt 2E.* bڎf&괁E9 ш]MTl(WI2غ2'˭#R1N`rY7r U@$QeV)-ѽ"}0K*H7Y2J%.(Xem77 %`c_+0; )?4(vǬ ԉ7 ^@ڀ UB#\5DYE$Ҕ(ӛXG@G,:84Z׍үyre6qOE!h̅* A}Xl! D4DtKU*3]츬@;d10evŤ}׹,@A9jXˢ/r[ T15of D^d^_qST rх RPkJ @a=(/wJ'jL;EA͝c nk3Sѹ:7Pq F09\݂{UV|r؈_JSfHGu^W smjFkE>wQ?Ew7dfȎ3R6IlJْbs#61w{9FJ-K^y30ruȶʼvUֳ2a-uE{"3u}AžTMnvA-.u%)펰@a1=3-O<!Q oo BA+:IWWH(ē^'\= Ǎ]p$b \,ra.rG,h pR `'IRnICJXc"cq'dcZ@ KПTQ։kgOY8csLf<fC7mPkBfsxb쨽&NP gWOeA$L@Eul YjPcїr&5S,#\9|1'xL[ܴ uۇgN_x]~"b^6l#+F Gh'f]fKI]T}D"7V{a"B~ %vFIQ)zhlgV2t,\Y̞\4΃&׃ė}v Ι~xmѪp¯O=,ԑI/& "H,L5!r'T6?l=M#GgZ> C o>fH=Z\-`ݖBCZOZ}JE +tNGn$ޟ`Rؾ\ót":>@]nlQ֕ ڑl~>N1.5X'*räɊX( UIKNm (Spn @)WP|~8Kid]CXF T':F"ݳL_ 6H/)$%@=,1^|NdyE/ ?OL8&=6t*=*R[OD'GP>6Ӗu$Ɂ<}cc|> j%Di;IW(H0fMPqJ)6?`Bׁǥk>Lƹ.tOI2WQu#;u,2d0☭8vUϻ8s0;0[zkW<:iz?r"sb0"wݕLlSeD!Y7QpH*c&i؞PYn3~]6{ ysC5?%hp11 ²W3Pؠ{Ϊ;Wfz9 e%=2In +sY BV伜م @TZіqSN[4n4*[-8d>meUuT˚Z`nh4pT Rf*w3i3Dmgž-]T_Ɔ`g~ʠ[`8qӔGf=Ѳ2\bJFII :c*dRP`<%՛c"%D>)4/}O|&Ky(*@b LA qt.4$!bh6[R%," !oaM ը Y j)+xm )RyhS)ٵ∈ iv 5蓨8d6+ 3iM!cEb8kϐ ECxgDPVNWSƜЇ;H\lJ1r!Z>9V[s:7.3śRZp)t/L@% ϟlBpWuRZ@Om$ѼߴQU%>va'U!UyΙxŴz+`9ؽopEc$(ꬮAֈJc''YE'y1@NFUV륊,A56I([5 Ʊz6Dd5wEn~݌=F٪(d=1 Yaed0ZSr,$vocRoZk/1>wz>74~]Bx@+u2uV'DE"5~lwQPZnAN9ѝCP;lҹ!;gJRPdZܱ&]'z7shaHmƣt'K ^hRu[n%K]', kt">x9̅oDUQ ˲OB>^dW e EppJ? A>iG,Ʃ߃l.%S3Ag*Y!W1dPe1_]E[WkX!soBȝ92(B8A`Zo@YHF,F9cjh]1o#ĩ3mE\ZdVt@fj=(xePݑ7A?&('=ƒy)"'!Ģ3I!`U%G۹\vtd ײ ՂG; Fk);=g7>bO 0 lHp<6î쬤gh."1:V=w&h'hS @IO׮+9hRN 2nH.tp$UD+E99|/j`b#[UY]nw3pYGc=]֋E[ߔEc:TuTR.'˞k^kq҄;OFS'Yt|Z>;yQu)=]CR<^fؚD# CH(J~<%M豀J olI|AL[d'[:VotOkr CƇ 5z%uNȶ:W$8LD/]Y:J/ ͒2 <$m@W"6+k`բƍB4#29d=7[[6+^@vݮ`yߧC ni:zS}UGX08pJщ{Y~xm@^M(Z]Y^:@P0#P<@pߓR\3Eϟq*Ej{ J?,(3. ) O?6 ZTM:m TN-uìzvВW08@>aj̋{r859h ,`Uwlt$2/oÆ[f֚9JzeNpL:\3/Ο>.ִOIT(^3\|{M⎲v):##{ 3خ#/T+-HΠfMֵ,Гd6:hՇS&D_@$|B-m\3򘭬4 %Wc B4\ #J/zEg~ixs⥿nˈ"fD&tYy1W|7z O/;r䓚@1EGH'Qbwx+~VnGNj@`|4V#æF V@0L>M{6&5wڐEHc\Ut;$\׎UoεY 7GѠK1-&,ۄ M l㱅jE)Cq;׿vnn'{#!sOgA^3ini ̍qL& LlMD[lik Acuíjcoe8iZy">d}nd 'vOw1lʏoP)t{R q#20Y+s M m{cMPAG4|W>_{OUFJ_#ZDtk'>[8Ɇ8Z6FC< WHKA@71O|'7yo!Z{AxZQ `E)qV(dګ[r-H.rlVumdiD =}RFܡYQ.>.% |g |Q>ڑ?pKy >zrc0:mBuJ̏0xJ@2d=6;MGrmpӥ}v [2ȱ[f@LazyD8d$>RtSpuc=hDĨ,l_ wTvD$rn%="Kl||lJ'ΘRmWt'U=A'5 {uO-٠౴葵Zpu};)-iHk+d-Qޛ|>ĬJ9Tg2",dH7|iH F?f__-~Іn2[^W`$ ~ȀN J#5=*5V*2K z@׋4;0!ȗ^51-xGVXi(}ַrV`]bJUZJi >Gj^R3CHI4xb!E27.3W48 /ŵBp o Tg{.1x/5_p~^+'S dyf(1_5uڃC,mjjLԍѝYQ$}(.9q&Yk I3Jؾ|wkX5 ='g~jrTn.X} FO O?W٩b7~ atIaÁ +_OBkJGNѵ3`YÅ<h$60:Pভ@.ӥSiG9#D&u@%|Խݱ&$Ҵ@_~o4NVb~̱7nʉiw;'J ;O`QSN*wdk|\UF Sd8}s B7IkA_yAݘ s^3fYq7#&K'c do#gi-|X97CڕX ˛zݟHv Dh~"TblIa|D o5uQJn2:oZ6RU73ߞ6#СO8`vrO\i(Qa͌;?vZdL-)0^<4r~m`c $l`Q9sZ>#z Ae~랮(`K! >a򹍀![c7"ӝo W~3> R-l\ǻ^w2,ka}{(WEO=ljj2dBuh8ua)hݱlUe}c8NQ=2DߑXwwԮAjrʖTf60Z mNgzU[ H#: Z44b\@/m fH? I^]"-ZzAquaf `Rw/s& hFXhD+ \bBZ'Wu5<əT9@]Y DHo]+ȈkL |ݚ q$) kkޏtd[kI=tW{b߮ztO|I3u;jNi׸*<t^[F%QpiwsPFؤ'Za7>9xۧZ[[KhsnҨvVX{;蔩֚0-WN' y:_`?ƛE_TlPzݝ464\;,ENvT3_j+-w׆=TҬ3 *.0W%Z-賴ﲿήnx?>2Gt8($0'V- O촅FzUJFc\t[!EɎs镣~C{\eI|H\`7:T22`3ΐrtROAh#ؒ&;w0o fk@һ=dd$@ bw]3Rm=tSܢrfFU˻Dw6eED/; *_)+:YQc|Αx4=}6kD!z r (uEX&cJ*v,42iPͳԧxoQ}"==`W%3 Yt:*}zn„Z MA[^|9GOz uNf3w~5qnFJ'[ppM=?(n ~ރR)l_x5xW=+w7dJӛZ|'~UTS^w{ÊZHWꨞU-w+C?4ig'=X\fG>^k".eE4{H3p8FO*ŤZQx|k}(^Ophopւ.-8wlgT|6[=}%\Xqx=,}8nʁz׋'.=,Hr'ҫK<5ՙP!WRf2kSsNuxA/oPedk# .xkbٿ|Z' hko`:@ݿɳ~5eHfơ о]RI5>!C4u{f$)@ N1'9o_ .;At2AoeAYzG|ZhF.w9?%&Nicjȁ41.R܍Cw/%kX${ DcERhէ{TWc)S>fWfodHK ƑxJD6:$Ztb/$N-z8sʤTc{Ns'=Fl@ [}_dx- Hw4x^evHy'xyYF$ٚW$HQXK8BL }jueBvS! y}Hk2uB X~88$)g|E_p\wiCp\q^`HfθS)K$ NfǧWH+fk?) )Iq08-rLNS'7n ,O xhK>q\)##i'TK~s \-!)U}\C!]؇LFc󱧺Ž^p'\&,l鸨辚++9I^ŁCv݌3xk;s"O%ML'U emc}jKw:w8-pfKڃk|}.?5ʆDʰs )SO߻5qM|.F~*u?#)k #}>(1m1^VǨ|ǩ#ÔyuBv=m` /rklۛp",lC;yuOkx>Z&A0# lᶰxN_u}r'iY FOv/Uɬj IGlmËOXXGl($ LM8/,w )ކ5\j;Wm㆏qJ.A|4!w=K'Jvo0,tcLMl8]7H#4Y[ ߈{HI@gKnaSl6*zNbrCYO5pkb]dBp#,&÷e}|`uS~5ee_`W\,$i3B~8m*ω ȻCcE҅F QSW#M_مgc=j>%ۿ:T 10_6Ij ~~Xhnkfu{%OsjsPمl,?ѡ"cY^CW@V}am~RShNeX:gvu.$Ɉk\oL ~:fw 1ӏkO^> co9 34pk=VUl}[ "e /+@.,~Pel*NpJ4Ps7f(YfU xQII[PWzt+χ̀ ?_uBv] k~mq~o/5fG#EY;Ec q9f{?W&6v5/0re&r%#\fS6zĻT=8d>=T=XM!}*E'j¿bڧ6{1x768b"jq$\P~pt-ov T[+oO!l =}X$[ӗD>ߙ54*'Z{_{ - "m;3DG_||bg Z?Z:$qR9H reA2.o=yP]뉆oB!0RoTV1è-E v2H62)gdq)*n]L+W+Kڢ}Ź pYh I*q=嚑=f?/. 2U=N7,'n1r*ws)0Lc~]7൬'BAwh>ԽF sK~)A]A|~#'-LOR*1%avA]DY ɪh ػ'1  mEE`pL\kZ[@JV S/BbJ'Tz.LMa:NZϥRTAR? w<5OQ\o|udX#tjB+i >,U)>L@9+bP׈DyOzZpAC{)␰7?zJBm`y}G@z2._4ef N*( jƲPn|W@UFb{_FS٥jUB ,bt k9c'DW=&shCД92+/(^yc@~!M7 {Eldm>퍬UZ'Uo_O%޵Zkt*pǰw+.Y O"fmAМ7LGI+'#:XʵLAs훠XW"zSA(+sW5Z*~;&m8WqFL[q JfΝ]fe-Sd\!,_4w^#mO$ 9LUʮw T^͎ yXƵd} Gq%0C $ZHH=w&CT UJbQ=pZ+בd{/3pM(u}y;4 & bim)g;Wm: 9:^e040Wqrl3?.פ]R:yԬnŮ +@Ct DMT%%pۉ %'(QS܌pBKH\_˅!3@1Kʅs~^se(9ޓ㣤,jdNRy#dM#򴾡Cຏ"'|!K0K# J'S'#{yu۔뒖ܼ`WrK|gęl\@(0(GDر҄3u c)T:Йrmy:qcwYt_`: LZ[=ӱCA$ADtgMpyPJDzU,ut>+LJQMXtEG[sKST!j H6CЂ,ĬPn%DV*lbPNdSոաgH?k6eOT,'rfGҺ$tcDx y)݋2爒@EK OG9d=|`HĒ3[}j}\8c$=tjV HRjM!G•m d()Un3X.{Oo pW}1d#V㇘nҢ$w%vErUKv%Q&Bym3uS}5n*~uyq㋄+>kZ,Wۈ@dg&OOP:!%%}0]VC"3qUߍI1*QW:FvcӬddde(kz>qE>v=}y)dW]M(ټ)CEiAo:&wUn/%c9ZNlM]sZ5Ҳ7QޤzCywB|~138Z]x(Qjb;/()g1AlTrt>rRxKM -歆PCe%ґ\SWf]jmDOhc]{RF2 `\ ' Y|xv@9{}+|7hTq ᳴_Yj8Bx&<؇f޲¯h 20bt+2 z[;6n~pCDgŸ'k0>W {9=J v0`] q>#]d>9:^(wwp('7an{0i}~Z9> ^JMl7I"ܟx¢סjx?) x= Ì<x͊I(wcvRoi~=@–eN27Z%U+&: \.kJ66o|,;=;VWȬV+]4@o>$L#n@G7OyU?U.(=INf So}]@d> 5/+@ɦ~AzEw\7CZA[b㈋Eqq%#ck N#Ff+~P^JsQʱ5ؽ@.?G;ϸ d!NC3$=3n*J[5 |\lAH$Yo H]ps:7mh*U޲W X@]k"W0߱ F,]~F牒U| ,_mz<ٳV͚N Yn*n|cM:ZI遮|YD#Բ a 6<:O>{U aOԒ|?#UP~sc .N[܃A3O|}b}gӃ\wYqny=w\N-KҧU0&\$'qe؝ʃuկ"dsr~H ̬ʹ$Ȭl^~7@㜰| fۜ˨ȳT\ V٦^S1Y곽fSG0dZ#M.{ #@DBLa&: uT $ohC_74Ghx#Yݱ/X\ H1x?4y2q @rE@qWV`'Xۓ^i9ECJ]%ZtxΕ29Ӧ~84)>cVui4@ʅd'ZDGSiu!y:ꚢ%4XBxY dk{|bj;OTc E~١EnEPwDw QYv xZHlA;o(U1 v?s/!ߞhYD<' K• G- Xm7B! -fD)*DHc3h֗`En}^'? dajt [ I[4YFE.)fIi{8j" y@T y0L#o3#vpQ3l뽋"7b/­ 3orI\EjyԺ%7=A{=!acH736qd:qt=&)b*]^Y}J&VA Zbc!/G%-μVù$$`Rڱ׾3vs4LXoBf:kj0qx/ qjv#')*PF׌t$$GdRͤ1n"x!򴬜P5F#eP>'v%RLЕ/Ign+sei.c`sWD|ȉI`meD;\4Z:" W t9}FH;Q.D?2,1~w$m>;&jV$G(Ɍ5ntv#W6%Y <&'#oW#C6+)AE*}s&je 'U/R|0OJNV|8? k&oD"X` !=s@MK'TG%8эƼVC$L Pu#nv+B/3x=)r#gUx UUϏDNo6T,>Y>Q_J}CL+^ *^+b3l3u`5;"N>{Me8KUHL[F_I3VL@I3ÍQ@oRp#(^እ<ȇoۃ.D뗘:=J ݁tUg )٦t.,9l@"EW,G>,_mrW;* ١}yM$* ͥ](re6ײc]2͸ē"XES*2(B$YIyS^"mNsKNNIF7&:9G_WR#`;hG% gK6NO8ۘg?P{3jօv;5(r-0~#rU{Û&nkqP'B*WXU ?}+8 *T}E6RkOZIr Pۯn\\-@:Sns}in9KS |tށ'0 9Mk;?IJ< BN?-p7Diql?풔ɓ }zMH|ߺиu!.J[^  t\'{N{B/3=`wqBy|X&"8*XhsIgYv3vD)#a&*e=mў. lp0d3i5q%2WC7=-{azE'`x\9iv Wn˓pwn`=|HD'9-}CӊsTzFLIr 'ۊHK_z`3}:<"'p YE n/}fy补SnΤ\jDzg$|k]\{f$#i~aЗ¹lSr(Tw~Ւ΢Ƨhdd;+׫l6Ԋ Crۢbj91H8ȁ 2kwpXiN|/b97',JmI]s[L ]T{{yJs( C}Pᏸ,Vw3kj9}R ʺ5QMa?Ш"r3θ̞Cq3U`81r!⺺,B/BN؂D"qadqo&%$-]YLqx e'DFéU88,ysH"~6I }q I 4O]u0 P Ѿ"It6-h#W==tn_r+;/J'] )&Q9r3Ak9+:{Ms<ђz9nE{2DMLevߢ6[і^ i HgztJq1@Js'ps(Vߘ :3*O( $p֓J3q75 Hx Լ *cI:|{S3SEzn3IYE.D>_683KafI*%C5$ i Jain m eתFUwNKCGU8f&xqH~.z¾nM1=wgW{E(GFl6L3D4yRм%ONxuqW˜VG> d+~"|p~>טR.q(_2DE*5KAMiLKkV y+̞GzS6̷ŘNU |W䏧t)dL3ȶzuYM]/]LN*Mt{"g<`ym3"I7 ] -2t8I˛{*LQ(&i} IeL4H8#ΩBV0$NӒ5(ճ޳絧Id I >C/w+!:ɢ6 ]Q Zl3W"W3 :yǩ-` *=n 3*>Gxt&m>vyET \}+Dڨȗ:% O휦`KLvB%gHZ#ФHmEoCQѬS.ٴy:kZp&8 l/] zՃXG`Im?vIQTuנ4D=iWZɳݏ~-) uA_u煤J³Tz<^@$RK*><*5oS]|R` KkUѥ7 4 $+φ]jz>Nd׫HFd8q4vAG@d=)/N$ij[b+[K5`+8O s˃ܚ*\ #[qi^\%(Dm֏#OR$\)}$W I^vBox1.k:[~.>id.t3MD /Q?(v ($7j4$l6[Oc. 7ZCu\V$_ή@4]II: 1`NJ\֤]qGb3u$T_-K͠RljS֐5u+jҜn}8ͰbOwf\rpCjʥlq $@u~v?OZ]vb_BpS[&Bf\_?;^9UY4 GM'˄`R8ٷ=זQl:w-N01` ʞܫMg*Džͩ2ǭT_88w#(`Qx 67gx wKJ; 8CQhīpLݩ |D9v9Gv6KEקV䊠| ~_/A0ϔ,Oy8΀Ԣ/qyEirjM,evJG亘3|J=\j-E8] >5`sAłg\~8N4PE26'Qjn|k POrs~?ԛ8S FtѳNB3ؼv`HOCn$ryDV#Cj- b1=jI.!ϑyY*n*H&Gz,oO|b{33pF/ʏ !ߑʪ *tArRʿHqP,'CcPK'*My B) eL<6^N_]_TE:)ThR%>29*)r.30_ hcWGFK zښb2?SD3}hAc.ƨ'fP5l{(dYU4gͰ%ǎo:xUOYKg4"=)EE͈͇p/r߾QtyG)U0P&y$ h>]w*,ڪo?wd~R>1d L+) d8OP'Q@k 6Q{&{IΙY@U%&YMh!/.W C=@"_0q1^d=i]YcXOa,BZ@V} |To6#<`k6w~YV%#EW %(P L&N 6ADW~^]Qd5U zbKr`*O` *G3b+&ڃոjTe4V=mD~EnYCAXm*}-tQt:LH(ٸ >pf0( @?1LANaGܟJ||õ>msh"pfTJvAy*,6;X\J g :!n$Po˥=nsNf-dRUI=-.x 5xq]x4ȣg;~)+eL%Mge0)ñnP6{54=ƕQvҽU#0| @#l xb |i=)} 9F9GF^Tvyof"[b{O+4קVUV}} ٞg&nЖm|\Rl#605PXYԓD*`w_\m#cLJz/U$2NՊv#p~1/OV;]HdNL(_.Ѥpz>Jt^d~NAX9]FنlJ1 [bO]_+h~zZ}EwXh]\h{2npf !.E300/x8"Yf pXlpw1wս[;3%ЄG[E# }U#bG Ѳ>a^1H-uI MyŐom+2UR2JَuE jݙ4!_DDžwiB*@:z ]"uC%'d%/;"mv5 zJſtLUi5-|9euV다b{Zm %.iH ^OC =|jj:y{)u_Lș'W+E~ c"}&xz(^I;=85yą, $~'kM{UAhhQZeM5 ^NCŚg BnJJyXmGEgXgLtz_msrvRBZ| pc@:~R qܕzZEH dx PQF<)/P>zT!cl/bKL~sS!:נ0z+FX _H,#HäHzATr<9gNc-BL Jog 3~ߖTgOڏôG9tvK,I%ՋV n.2$'o"M(=]Y36/+&O;[7?辘V*Q(B]B(n=#:GNt.L'ԇ8|k }1He'7q" ]@NE:9q{5RSǖ"#ʏz舤 M$3xj\gWzϮ [W^z݄R$M&pZH wl˦n A=ʾ 0wDI0}J+MJ糲xG= BwBMp%N;!jQ̃O:lͻ ax#J`3ۃVJ *8{XVy5~x8mPɬ=Q>&3t\dRTkvw[MaͰVۑ7_HTן.k3Bg"nZxuPx b矘j,]U1kK@*;$o G*@L^M@ci @VZ=k$z5nDrfϕgM$ w}@R9g`Ey$h95(ka6&P 1M -Lg]3ɣ{UvsF@&ӎ);MrH-@:2 ܁i{;@Ү8T;R4c j]*S8W ivEWSK*cD7^`\ x'0VC{MirdJzpQ$ggqjc8H* QqAEЙDڿpB;~q(O|Sf|%:օHj,x:.x]J \Ոwd[?tFjd3dR*iE'ǹՑٌEU:oBp`E^F6#9x8`Wg~'![]L>Ƚ48\4FyWrfJvQ4{VI2?Tّ:Q7+IfjYyw~,7FFA 9{Xذm҄i:Ă|e}S!s67:Kȋķ9PIrhlJ&p딐qNρO _)$c~ڮŖ,nTxQr xQ&TM+j.kD4, IMY@=f+S=| Q>؃i^$kzFԴy_z1%bZ\kc 1Lf+,9*wXV9(ʶ&Pn>8)  ໴"dnR8]a&hF*Tc6Bs15~/0ZH02ŻT6"XL)ZDVH ƇUIj $_-|i ^dU$& J|RxHQ x O+4*k߆KBu#!& Չ׷@_ {Ro(o**wCbuZ@cO2cxcгVDKQTܟ!ߝeqDwm+ Z8y`hɐ!dgh5\*-*cjghԀ̸tsқIX()̺w A$&k$8S+Jo~'LO\]1*hX>8*gU9=w v*Jf_l=8_Dᄡv$LܴmW'TZ$ә/5LD{xٍk敩N'(>dG-8Sf8/Ci4  u+N 燅ןx .8AwWXR1DyO1_ =]MXf+i^&P_.MU}b՘A¶+88p=*NCxm ecZ9A08l@+ ldIgV.n+uEn |Uk'X̧Q}c֍ |7o*k\ kc h9'`/ ('q#>tx}ca٢~#(LڮDH]mEzY H2_MCՁ3V@f} Zp)jwZqD8pAkW!QF6{72R/VkU%% <~OǦ.v]Oɉ^4W,ؓn/n 7M FQvI:bO8npg>(jZ!'u?L< kn"ޯ+0BstsVkh<Z) &H* ]HdW{AKXF|(p.`>s6`<*}b&Cwʯ#;$=CY ,@2vXf~\$]$AvIDdsj,dԟ P3 uHmxΐ\i=5率zy%="Gc}E^F[:߾(_)\Dxѳ4ȶ4 "g1)ö^(rc#}9V‹nwB^k7\`qxQG薃S9Ѐ=m#|Γ֛0R.,Y6zz]Fju?eRpEv]yfH',Km, 2|e6\ H/qؐzT  ϹशLԠ}(F1M:Oa/R\̐ Zw,:0:2ԙٷM'RI"lЃinsU#sQ ZGN\@\;@\чI:U8MZ>dRI<ӕc\$Rb8 ͬK,"b MwM)@䢶mvɤ|iفzU `S1.KYw_3`:|1V1}Z^؅ B"̶s^&19/, _ҥJ @7 }7Avؽa =RyYZgԸ#LÈdx1@gr|drpGwb8i򗴗)Hx#%R5ymǴD`/icf'@M3?;exrD&,P.6O}\w;R[ʨO_WXW>1{Aq,͗<&D/oC6qH3hJ@ \@UiT!P:Fq <#Ln s0#2ۺ Hy_^!DN܀snhi5tG}DYl΋H+X/db=D. L}xJ,gvodRmk=^#ltԁz[,52)ֱ"dQ eq$ bW  fz:W&|-:OwJBSpDL6tɟɒ.-:: B|e'o uFps,J_8}L{YD!% - vW[KR 2-m[НNOf6 ph @2Ņ/@w}X".:+e*]|E ~3ߒ:\IR8we㝂)/zYqfM=$UpA!HC2λdPq-G.]n,` u A6j4 Q\]4cNP% $< (cгX=@~1nƫZ5f R̬D*@gc ]lf k}Y1md;q*&LǾ1?~ty6!v(Xg$؁[4b)QJYi mB0@;4KMݲQ1pw\- bK1C9KyOxm1rV]T|(QULyz"?sPn@Q|,ȱ!o],mߊh~Sx`MTc-#0VNvUFM Be*K=Fţ `Cda;t``\5Ѱ؞\`+92ri)Ɋ hDIǯ5h6 GX(J m3x[XvGٺ|NYW?Άg-՟y V<88\/fprIڢ޳х0} 1n7LdYǶGCQM68 lFD$۶*W뚯kڋ = owߞIufJQ$?=@6/E-V"8寑u(|T90@ڇ{:;~l$1=+" =HrQV@H3'e-ց "] =)~K+5|@{VMuF !0Ǣ)3<_-!@P.qe[n{h^/ K$cx-W YSNȘa&y~VJ$*d3I,cYQ6gxgo빀,)MDIOWs{uխQ:N vmS'_FciT2}0%՚"ֲd.DčVb RHsh ȲV(RhKY谰:")$a\z)\]sIy$Qvu_eWKC-7]bp\]W`?3IuN&L}u2!:j;e}Ѥ6bI/^y"E=uW~#z3>m^0joކǝڃC"T+׫Erc"d#V"\#&Z{|@D0*MeAItqwR\ 4j{c$RႼ#-@4\"ڜZ s-y༄%+Jt XƱG7dnt*eţH)TqV`^'D=\c~T:﯏kDlq8mfZ6w6bv8hԴN׽1ydG=͞`*H >N_FfDߦ{/3S#:\|$cɳa `q<å< HG,h %s ܈'oHOr/$Ns3'iW´@f&& '"|c֥ƾكH} fEi"=q2AU%*ڨQDF2([~@uòxoDzX, XeKl6-]!HG$%+=#o\j eW)(L)ZIU8i}}pa@@@E('@Ȝ߽h LWZӗaNZ6dAm0w8+ޠG2ncoSV^/Xl'-c[jmdq𰭏qvƮ>@v-b'"7ri_͸&޲2QҠHbd#^&KIlȽǕW+Vz1H}(} {+Ȯ/{Ԏwhe5Ь߬UP9,م_t҅'Cu԰A=Xgf ]MCf+9ftdW*z=Xlۚ#H@ƣVu|%SBpQ(sBw{ıyT8pE )2(NNr;:ܨzpl<.灃Z7J[6[8 l)ALT Ո\2P_3sb O |j84AJtW}YFo:S[Lޗkόk&zfDR&c<UN'cT lA*1O\-{ _chI=]?!r{J$q!p;b~ٯ<hgK;5&@V^UjcvK`ә2~R?Z#2,v[a:y-Y\#< @@ 4PF)I_4$_OM6wusR;]a^H<]ABA"MTn mw5a5c1JllV$ i>ъv|70}_pWr5-rZvhxGjvZo @nb8J`Isrsn)Y$}-@L y]qhxpJ=@zO_@sZ+E򅜝ahs^{_rv-W pZ,$MH$-(41qD_xz6wxbL֭!1{B&oDi-u7勄Zo1&G{DMHIoy43ߎ:]5#.+@.W\BmG?[܀hAg ʸ;H2kD(׾yBiY%Z\bT7ߊ7w}>0{tU*/ /ᮦ9Fz~1V+Conr(+p^wPgopQ~nKҼc~#3|ФL%İ6EH}"?3pB9]eltHkr;t뵯fz/zo8{Wft/5?t䂳j]hKt{lzMlDhA x|uɫGNɤRr eo&@Ъ%K'̰2}ޥ?>3Iw?kcQ]g25N;L!^`iQ5߫"gN/|8΅l[?h}C. h9sW/FW K79pFa$z=c!x+qj{|wC#,k' p۸{u H/#_7q1jWJIղ)Za q'G-*ujvh93@D9;RA|mۍc%iJ·4GMRکxcy!85e$,v9i< -sȏ 5X0H KRMwuf9@t\7|>b}$Ur1؆9@PeP\F(lP;隔|X~=mF3];]}yF!|=*r 1SM3DT,rfA/!QOr`#q'eE5˗̌GrwK)WdN9EQ30 Bdեq<(ʹQhZ֪Oz#~Z6]ݕqvLC@\RN ^1_{gj2m_*{sIhWmϵt z(9E=ÿ\]]Ӥ[ϐH]Na]=b|R;gIW}=lpZ yA/g\/"qQB~w;Q*a˕#<2{)GGoaG6RBI]" )`>6Ҍ!1>ѪYPđ˞#zM2 2IW;]ed{]xIK'Q%3v]$կ*p@Ef T |ُ$ >< !SLV=i"!aACtk"/\Ta?fNN>8OTvбl֌>\$N+B`tu lD~v 8xcrJ&]WBBi}Y%ED+yu )i?dx5hVT@ Nl`P uL H&@LDרmw]3pB܂D$U&8)u4H b8WK#.]$cXNPU(euw))v>>>H6ɢ[i1G^\UOj7n|>8l_ =`l^+ o0<(Cj_o+ JcS&-yGʹq&7P#clw! ,d ? -+>r* {*ׅd*YubY-"]y]26ѻSk]'a teOZ$rŕD #k|xȳnzrYT `-w (f?ɋ*Al|.oK&,ȃ[(6_XRX bz"ur.e=ZR ў;AחkO7pacbl#@a~Dslv,erDFSAnt_Y?Hbj=d8:_s .0Z]a(|D6J3B~ͱD'lЧZ-RncX4ݲs?̚-Jt~5!?\G$+Va/";v:?*Nr%p!!IAv;` % 3DL=뉟fm _ m-`JH|~77d !,2@3EL(#ZF 1iJs$ @f g pq4NZ `8^NHLmPUewk`x~6h4~ҿ1zwˀI&yl#{ݩ}rC6)j!5yw"ےhE "^$d'cS&IfTY ~80m^vېzJdtv mġUQٶfhݛW;9^Wnd|bc9ʠ!vяdUjԫ#AEʻ˂"5,?C:P/5cB߻j̺Zq%e>n'CىneepXv DH2QM&k0{kcþ kurid%kZsoS*f=2ߝhGHH %t)T.ٕD)H ҇gBr  }d&qy>YBEu/V '-U`PȐQ2/IB9]AW}ޠhgᅩ.+﮷9+Wsrmp""H72:Y2Rt = ّݡXJ<~H.@E2&Gz%܄jNSn+t/AIk_òVǭ^O"~uqTu|8Qc[QyLu-jJS ,Ѓ Z?FSog|pacn}7PG8*MVZ*E&]e9_`pYaɢt@eW2*ŏu\Uĝi*b}KL~NKSʝ`V .43-^%=QҪURO^h{qׁ&Ћ]S=E*rlpXQo o&Oȗڋ7>? ,=|zRz7|v(G^8]Y1TZWwRON5҄S0yZ?ʠɼa"&1PQ!ISgKԟNح'~ 1OBQ4)q˚c=!`'^y* BVu?O@ ZbiTz-lGE!7ͲʾY'2Fb> ZyD$r&@AW~)xt8:GHB~TBPfiu}B4\ѝxL[3"greݔ>Etw醭 aTIM)UUS*PD|wID4?]a Ϭ-ԗR`t:tW @rZ+E `$J݂SH N"Y@V{ :,bjkvr#n/,$I7 瓚J~^P! ѷW"Ii ?w3Av"S:g[I bj`vvo(mm4mC]h=܅p)ȡDthzp!ezeS~bI1FEle)ۭP#4н/6V^T*qUgJkߣtrL=Mf|uN Ddkɑ>Y퇲{4>=jpiOl]>[{L2>a_[Uԭݦt2Sq;EU9cDk+,QWwMycjDļu$a]#ůpYN"EJQxb0- 9={U#wvKNt@=lW~K{P`+÷=Ť^^Vaa` kI Hҩ- dV@*s FJ6I.dܶX)6iw8}p,Iq4~3ϔMMz%DHח=G>5iOC|% =Nce*E:OARCQ [ I@%.Fmr΍$G1B1nEM~FyI;ga#>E$h "J~t]%'|oxuHW}Jd?\,"d/ %>D:сF>(o־[gb_yN9ٍx"`Uq ոLsG^@G'>T+h!@zGXp<8]DH$gs|uVNp tnmd=J4j{ZaҢ,4Sq.@؏"':~ƵGvZF7*/ĦaF b/L#[w)Dz*E֧g>+,,HHjlyӈ4 8Nbqvd\yZ'muiTz *ӝ>,ZFN5F>dX nԃWPw^=OlT$t>tCM h޲k9ACݺݚOpgJ)+e mc5V%& >kR@UZ$m,HV@h")d !7%r=b)E@w /ݠ<*5+^ No㑸/[Օ|VZc89)Vg S"& #;VY=WG{|0*Gީ'W=I0:??텵di+p}]<I.I+!M,x2_Ș|̝IOy(hף͎㮺K!&aH92v\<|Y8]Hj.GZpޕ}5-!ȷ[bkز^ ݰfНYw>//:IOwm)#) @t_zÕ i\\[OŗAIum"Tj2_~c1ڧn# KHBbTyw.%ڟwY>)4Ά:|i]+9]51kB稦Qݝdn Y*jh ^x&  ]Alj\/Smyl}QsJHcig|y{4D^y'tE5+)d-gD`U>Z>q:"]5p}[myg1䝚++>.OqqRPt>=_dϣD`YrDR^LdņUU39)rVБedlXL|C5)DB~Fx 92vj؄c i@bX4*'ѴPkFw"DX%9IT~K2S/2CN29-*&f\_@7 Gߏl|% -Je}"Iwf/=+a2C#ȼgdzg ҵΔ“B3*i&fu1Ezf'@[ERs;]AnFdoS${\XtYɸ\KfnzNBaeVҮMWc? 1n*56+32eH ={C^m % / ^ >{iBOkݹtjҌ`e}b,D+6C[oX=ژTMGMZ>ER ChPxS$qi hCyx2v0 @tR/δ;IBlFz]2n1+ v8٦˞ȅ{{ζ6跙L7S>%~ : K+?kXc+:jM1<>`},-`dj@!舏Nk6E'`두xyr,׃a,+9S fV",/8{氪׵hGxTkn#a>rǭ[䛒]ZX7, '?  \}S)R0"-JRȇC \HW 0?ͽq:GQzrP=tf!͗vM0h("yD-8= ŞI_aHdC;c: Z}Ȥ82>2*.^G%yңڇ-t,$'[R#e֪+Ë?v/2yy=&6S/6U8<^sQJZaaBC\ &3=R>ܨbR2ͳb1}H; N'l۹-eP-g87e?+0Η^\<:UFk$Fj(Uc'JB֡%{S_xb8#NTDv0qC>/gWA HRMni*"pgϕ'\e@: 5J;iW ('t'*!sW"Ȟ:뜞NJfcrɬ@Ngr1[GJA,rvt{ O`^^fK=ӽ>'+-Tڪ"{wVW'@nD{P?ֿI{p @k9v`0ƥۻ+<. s[ɩ$b+Gjj)&wsRDCr;%qIqDÙ `WoJ0ON\Fmv|H.]} uNq&-릋HQݧ%zxHWͿ/E̐k>_փRw9ꔍG1K34TEq.\>-+C Ɯ t3g-bOT~pkS[k,hV3 k *Y6dAnZD)i.hL@v{Z6+KFSȹEW=UnD68QHJV.v=hzYw)%i(!+s(?r kض!* |UjZǴ-@J[YBYҔ*]7[uc_sR "ά.QOw!rgП+'國CU\T."|J[6~G,E)3Ukɲ;$,BVgY8-_kg*&(*˖etfM|>\VloO*SzNv3j#/䎭A\ I$x@Ľ zL@Ss_uuˉٮ>ݤl =\OTߦ+culicD.E+.@ q"{|%+ ^8:f1lC]ESB f()+*ޙ]Ƣym뉏^}AætO)'^~O=Ӿ~`B4^HtABEL/5eQÜŞd$|5P &_N ^L%ܫF('Fa' }RrAc4 {b0]JT73K!W؝Dh5HĔ{fش[.3%iE$^u<,ֻ~- wbyIG-R OOF"qecML*9W~dz-T6kQɆFE 5Ȝ_': j*D0J(Фͬ@-`%6mfiղr\k^GL7 p/w">}-mzwfB!da&f=;@L*Q:q ֧{m[̜v31*lVw}2l+%T7 @ٶr$<=L5vR/FP9OFDX`Y)I?2ڔn9R^^L\x*KH{r$'uX׊4@0rDYv5H:5ks cJziqvk^pftWeLX)y=֥^XpxrTovqJv}Efo. H:ŦqeLkV;nR d&>rp.զf =unpM(u}Xm0ϾaYK`IR`<^$g#W)ҋe L+ #@h wMa]I4E:JW_GһR3tϐxN:L d[O[aAL$Ξ=_qFUjer ۘJWQ[D-q"9@^{d,9<1l_Rn;.£@RW~m;'=4-X^JBH[BR9+74+O d=/Бx 4. 1٘6+rC)ފ~݆ rYEτ հ׵gP# 9gavyfM5"#.D'q$'q/Z-n^,>k)ݥCJBoÙ;;A4Dbܶ/j[@^<$؞WёO:F‘DKsCi\2S\ͨZ>Riozu'̞VjldC{ T^K xoWG¨uRuJer#lYR[!+lW7.օ,(d @"};e7^sRGt4q|}ݘ)mXOʑatSmDMiP0CbwrLJ&E.QzQIȪH.پD[ [q8Fy'N'RD(ޡm%H=Q!ߩIMN]Sݷ. ]"i5,Y@T@fף>BSquMV[ѱWIzEh՘'ˬʇ\s)O,P',P mEX8H5:PpL9eMR_"dR{c'M .8w^@&' |fgmŹo./~.l~pvU!R& ^?x3P0ƇfulC*T'mgd}"jGUM:K8mv&3Dn`Sˉ^i-G0JVŒ"ܴI.Jݗ 7Vv"]#i4TCh*fe ZFsYueEb󑹐>^Tyz2g8{B^`:]7>}S*J@U)['5&i u6b;.ݵ~Q00W`UّxN$p>qx^.Ugك& ]]S9yN<杮&ZxzZ3Zu5AJ'˻w庎.,V C1'c`G0BuC%yj8<><&d3}qFB2iJw2_ΙgFoG TD7Q^[S_:y/>ΐzTQG]>]u6%'L !"KҢj!BRT+]nS>.e ZD0UF{"ut<:Cp/W9x CN_HT}y'^nsʰ^dNw 5y>tWu2j{a]M^6t͕k7RYOHYo)CCIXٕ]H:+Ykq؍x1иH8><bcSqWZ2U^yGck?Idu'phkDޢe~)0KT|[ڻkQM˙|R*T!u c!lQ\퉈7]\ ĄD} U}ākml{QHhf;A1Z|b&]o]g`Ⱥ/y%zvctZ>5 xU'M2dDbk1Pª,<* lh2|ןuLL!j(a[Sf3ngHN-;[ǻoO,y@DT~qʽxI@>VU[a ~=KILa#1w_LJf .}ا\/!o7V;6hJ (gzCtO]LS)GXdQ "0+5I^@\[nni?Vs hg+=Gq tiCO ?ZM}# -:5m0pS3D%ΰ4`27[Ц]QߞwW5/4Ww e> Ǣr;ҡH< |Sv 04wT'6>ٱ$A@6 G(z؝?l6҈fIv8$J,\AdüBHn:g{hAire $ ?=Yc'j }!DQK@KS.@-FE59Sxt^Tɻ/IuD5<7AE.9۸ krk·6=Ś/9$q:~[zлEW>xoRke=jkj,Zkf#8PEQZT>DeNU95F@B- #n& BZnـD@y %roHo SW+/C&;j<X|Ř7nb;&Tفd Õ8MYěA NŞw|얦ȸdZnչ<2zιKs$?D3J[ѵ*&We5g %(`Wok{v:$*%_18Vay +'$qʙvRHHx^CvR ۀdA#J\όC럹/>$V1/#2OCl$%fH]q~罙%~99>vB!7(亻 S9FTnVjn;;*7mi;Վ:j.Eϙn7Oc'lu!hl=3$/cعk Z Bg ѻkGɡjG4#AfiIw {$XHA)@RA-v*tLP5!]B,l =OH042U ~DIX|!'M|dV!X>C_6ۍ`MwOۗ%k$ v\CыZ_}>^F֝OcdWf'm}7= YMyW(֕Iwm)!Ȧv~79Pr5$ϖxAqw!x9\l|f}׎%Mj0-ӼjXDWOTLợ5nVoB |RNΒ8*h*|Hm"3Wk}&ے+ -}+[" ҧ"mrʣ;\|鑯# >L H?#JQѶXհ>;;6ҩw Ѯ9;fE24gTT;&y{ƾ(_р\GѷҧەIDL7wڗ5T@"0Y&lNjv :{uM<,2-:/t.V[CM|Vtv$;.K'95"Ϋ+`_ˆW`rq^Tw&[l;*h$'"XF8T4v owTll8Np9| !nN;v{#Y9=g{F}-AY[uT#&` -aZ}(k0bs]$rF.#6kE] Reo=k%/m`[Y{汄c^7dQ*2iycSԑr=]^sI$ 9IE\ !.b'1;Bz棊ڜJ+H. $?]vJ$J:om& $eEE&!|h.\LrnY1FA4YZ [\O/>_ m͏ IBɖ5pYE:螶u8`4wLlCC.""!HNT;3UYVoe$0kc%ӕz"y&K2y[}xYN@< lRZ3du3|jT4'{ݚNmײ-~{TK;ӂ˜KOKhAҳL :ॽnC9I7*H$s/D؃̡@<) {\giʚwz>I;3S2*c𳽴vdI*v,SG|:1./5g-O#Y鴡D\Li2rΚi5OQ[h^$r3k]R4JI$)J'f\۲W/ fQ=Ss#hb4r9+iYU }LJX Yc=6SحgV<|>Z|?bRΕEcXk/SDuWuj7|ioc#y\Œ/>띲I>H.sU@9QtfJIASףRg9Z߫ aW_ԥ1}cwP1D]QἆzfT"}w9 y~sBGj8 `Ұ.d6ZJHʴWh.yJN .])kHBzlI-_6`TM5RN^@3_ê1?*wH>)wa^Iaˆ#LJ Pv󕃅X>M`զDɞ m /\ye2 GPYK֡<7'prmV}Y {SQyu_^n1zR1˳$usӳ;eJ4@!,Ѓ b|>wDՅ^U'R^J\ŝD]O%e챥/0І$ȐokjPWw gӞ_ݢ;H+Gf\1 e| W`B H9Y'n~/ gWW=GWd40F_wʤ4ꑂDj);LlM|+C9a},C8S# }e”w6{Iw,\hkRꯦ!;y'<^*Z8^h}9G͙ W&T-zi*k/GsAýUk֢#!9Hw6C*[ 쏗 4j.dWg_,ʢpV4tu-V7Y+q_'anyMCD<|uaT9F',{fT6G#mHYԋ!4tŮhr޳!nyC i qF*=^u+PwLS!5+6cl¾=%cM9IFCi~Z^L/L"6 BIT?">HQm6!h|I0/57KhN$О#F\2׾6"K6 䳀0Dkc!Z*^Ic(.W~^,̮XՂAov͏1XtRʐ6 xd׀3:t8s::9]&.i?4;_Dmsa%?9V;ic\Jwg[zKv&ٷYХ}-NIB"3qûߨx'\{l#TܙF ӛtg!Sx/>긿_>4 ~Lwc.ɫ n V$+}$IÛ/#Ċ{݅ot%3xG@  њ!!-]R ™y|]oqa3Y50̧(.3ouO*{Z'N%e@Q5(z7n:FaڿuieHwpTٻEJd!.@|,fH貝cG5j@kYS5^.NE8Ck6Z){Dbd.6l-ݻWu7*\8q^5@#D5[s74ܥt(רcN֫܀TC˶`m!uum܂ҠNN$W~,QJJ~#G3 L:sn+a&j!#B]L;0k5-=rP8nm [IDq$%ҕ6p<\S~f iKڨ}I!t] Z:׵9fÑ 6/jj עxWoރ EtڥP",Lsg~*`ݱmҘoAavTt=$D&-?@ٺH`iHFN@\0iԕY/6rdm"AvʜԺJ@>ƹ#ST~&~H^{-Kĩ62tI8&P?rj*.:gfX8bzHU}=Ds _0ws"ṭ MщdA{e8)ǰ]@zair;o59Hǔn-bͰ i'w~I{o&Ue x Mh>=@BH ~2B0߭MN'(}cCztkvb>mk4*kI miWںrDʍY  v݋wCd8ܗh _u&$W2 E63 WL $%A&oi HP89"̏1BzZh$W 59fVHֹTw1mw3EPN_`663 " iTꛏPi|cyM`ˇ@%qi|V߀d[t.[~f$vڌyLԯ`+`ւJ1Gf$)f#UTx[@vZY)Ro܊\2m 1Ǥ&I\ ԘY(W|$5!u|X[`+k?IRB {$n7t`ۓօtYu w׊Y"S?^M-6$cMQHJFZT=@xʬU :Ah[׮?aٞAuVfp98/Ѩ,ԁKZ${piKש~js )s~f"Q5w_gHjߩn. ! ?LHJ:}> `J{@H^ (hCcu* пdeŶz;JLthLxJھpX]PbIE7znkua[HbAr U j u+\uUe2l\^X\2dV&=fXv!ic~Rb9Nb|Xy0G?Dn|Y#Fܭ|UFK `5W;J'3Wqy9wPƒ8fDD1d&^꠯sDGyО|-`N ,Bsx5)Pr sΡF+=2G]`+ ׋8nbu]mBSM!k0p5Yd4\z{L6Oǧ@nݠ;>DH N9P0-*Yآy`&=Z[|\`qO9=juI"\kHH}YdnFi 9f$YK= ^ZvIҠ༮Z]*U0,kIXowGJfAu%'ܻOT(gBTZhǑKKٸ)pL^Ct #y F49ɥ0 x3Pﭹ-$ћ#+ɋ-GEKas%b=dL$6@VAb<# ;f.+N}d (jySnvb(& =2klKqcV?OunMNu˅ו]gTOZ%2eDҍ >gH:C#d@9M"Y"s6eF]햵%W=v2,/ (}[ԝB+0pt?Iޙ W}))=ZUZ3R(VQ䡐ty\ .;K3QZ%ܶfR-f:7NOoG KGjNYE/`v42/ -hd@4w?jM̵kt՘Sg1Ç~ylr++uC^8,<^ '?"2 0Tư%%{,i[y;MjR-"w%078jXp+% W"%B$]@o# dЈ9QE.9@UZ0|zT*Ǎ~Ty><z>1I = :Xu<~;0?FG:{yX|o@yh,D3)f/f/*֨]CzH~=>sT' U5bݻq>V[!Uo]eP5M؞|TneFETstJGr|sn'wdaGz&jmQ/gyq" {Oug"׻~Xc5Z\H},YJKM<m3 64_P_]ψX#L×ιT,sׯD{5DnzXgӪm2Ek'Q"?_Cb˓Ȇ].glΡUZ֭IYn]rA"GBʄ9Ez- @ܸ>!y0wn .ϾK崨|En uT~Pdި7.{Ϻ.+aThߵ|rO6"G֓dfieˣd<ٗxZm;$W/l6Zޠ)\(K+|vU[z5,XC]cUNr FDjc9ͅG`\ZlCSn-'6DI8qO{\kq^[pQm۹N5j;wYwԏJmt8 kܜ3)%T/rH7-:MRj20"X; /g@kQ$Vn]QUԯT@b52ZEte ߹7>D? a#eK!G6=r9sT,;T읱,*HeHfka(SAJpfvɨ᳭.wh"HO\'5X_i&✷s'b8<@UEtgԞ<Qk$RD4Uuqq5u/D_L̆={+=9nY^d!/ l3H_rr /ʡåZjM)W2@R]:5j%p=R80PV+Ot"'Y8GvsϷACo*x^ \u.uuY?n1YG@ ǫ2 %~ pr/)Nq̓ L{@r ;1O,(Q;MT>zY801`Ey:ԉ )pn4u?ԧ*~a1g 0Wq`ݼ&cZ/ ' dJN5."};1AN]!1\"/\zL^\x5+Q wUk2w ۯ*B"J=Zn$@IIXR H 5zxϠՔU}.TP<ؼa*@*2Չ(r-& =o+ \y7/ꣳeQky[+#Ր~ݻpf }ϷL%/sPx%h9 k3 `-89l}WRP;p][s&dvs Lj՜CB14~SZ| a6xutls@Rt֗'JJũPzT]!ٖ6ng>ѓ;[}xWW~&ŷ ыD O` |=UDlsC*$XL@+C.A4:qqJ^U4PԹ =]=$ҫHFlYyrڪE ,j"~[V@iyLtӝkzJͽThsj|tHYk` FmxXGw6aH!(+*,)YfB(hr{Z\\Y4CWĤ:zȗ _2].* Zïa6 2pm] GîH,cuEα/B@=ܼ&c4n{z;kOߦg(E"ن3D}K!gpȞp4qQ] 9g.÷*?gT4xf 6gGG6&^ѷg2ڠ^؟||/`'pZ2N$56 ʵߠʨ_+I|d@=,JGz$N &nA4x(,g82 r|n`FfFgcEw1r΢Hw{( xkecPZ&ۡX-_I@z"?2$C_pшV31n1 c Ab锛 {9\(I9،p_lW6ClUVt(F(9f}9̭8#*RL6N$͸W{ny׵'y/_AH3@ȷlFC^yM}0LiMau'6@0 ]KSOxf0YFyYj*w!9$u7B_qP>2S(omtIZ8 [e?6ˣ+ Ѻx)pt]^Ggb~=3` \~7kX̣IRkt爙 yS63\̍YJIhN\YGK9O6zUc?IC2Isd:" ˝~%O/u0$Cu:a:e3hΰ@>ߥEgؿ شK?#l&A -k)k<L9ivg{ ^LFt]K*Fځ\r!Z0C).*GdnDe5-qjiqbоx63N3&O Iг[0Iuv\^iY.պ%4.f>Q%="݌S _TÐ6z,$N,Z5<.Ӳd P-W̒F橠Y;goHA[d"H<*B+PZͅ/nU;֎ZH%!Ё"pҀ>0nL^eO/zb*^G^:(~'ʔKNvY3D^غ/':ȋ+h*QOl{=:=nVd0ܽDzK|K]B DUAzNH>C)7Pq!_eLf-Βej53u&Q?k"G>S%C{CmK4^5;\#l!p!Xucߟ+.~˄>BoЬy'ji0Vo?o`qȬWA4N# EJkvwk&N>8͏dD݊d2v0gwB$cMmWud]-x ($<>O#6Bдԑ4"@T$-V+U3 =XU :OW!ڣrk@Ilh]=W7BxM!d@6`)! pXڞ6S9$pB ǺO=&x\)rH4WY1-Rʵj{|O^JMȂ58Cu`SF#?.0fçu{~G4$[m`ZmB{w4cD3fSѿuDR@qȎC9z$4WZin#le lpn5uC9?3:򺈑PkTHI9X/<u0`(8VFMfnf&hJƼV2'j<+bE}MjvT@,;i.g6w4=;ae'MiೱYZ|jalCoe)Oz]ޫ'GKms \DZA&} SQU;F]HaWkvWRv*4cE6vd닩<IT|)' Lf|%+ϥKDu˚G;N Rz%ABɉ] t<ӺF%f 5187bjSjujLӭwSEH]7G=?(^Er?_ w*ĜWtƼyD2!LD|S$)Z31'D~B ;4ESonC Ŕ%;K 5'ptS0AF4b|@eBv>U Np-Bo^^3x)@=^:Ew'dӴkHaB& Fp5. XิFdl11SkcUuϩE y0tSBkGt{?u2ZrE9e3RD=u {pE̹}Ԯ!UT[z ~L4FL.by y%{ qMeAwqy ~>/9$M|LTu SN4 j)zB{ q{;+#|[m"QeG0_ ݆'G Z>X^$bYܳ=.țϣ"EBާXDv-8kX7PsFur>K?szsW Vf9*wD'<)bʕ!J˿1L1/DΛ "Yϖp&"hP`m;y6Ovwf\{%$.R&zh%% tCCD9u $>5`N*(o;+d pf;m6歰.kj*T8,XyU?M` >;#^}$S 0i~gr^Yа}qA͡f>ùÄ_yܠKޡ}ʗl.Z,bnJQy 9rvGKh诌ffjwȎX}HlmܼE TPJDn=R:9Uk;U6GZ }=\KsQdFj{e@UoNمZ [VY2|}wyXٜ4 mzu6N,!Tl8JU$% o SGMdAeA<3Q^D&?DTx(9Fy=O!KN Q͗8؜m ߗ $p/(KIG+$d|> Ǘ@.T+37s$9p2#ml}$ِU%}Z='5wNu>}GZЙSNR(vBM?ANzP <_yhc"KHVgϣJO ۠MԞ҉@ᾀ'dq^cRvl׊]Te"#yz.*&~P-2Y碎bKJ\M ǰj|erWLOdP yy'Q :{ 9 :DD5Շ+#pHm4ި&c:dT֘3V-#YD5d(aKv&ϝ[רzpk"mAC }u8Bps\f jգ07%!i go,[5v?4Ruo~O̕[4Q%d hъ)p?_M [^5=&E͇ޜ/4iҢ7)J 88s52'M`qYQxL|M}㍆a"+7ʨ'6D#և}Y)qMR0Pk {Xgn#N2O/\QƠα5HuUY6 x42Um8PL; 4u8g|b%r5~t+5qiHJ`覠5JlpҸ J_B#@^DWu*' & Uۀ#3PSoRCkurJҡ 5*em syV7p)2Z9 xѲ@]=^ H %6wW ,7p\ %8oL_  UcTNWm_%Ǔ tuNLt&fg8-g99:Ѡ3⣰^2v穚dqV!*&ճyBߝ^XLǓJR6Ie 58*4#RD%;j8oLwZQN ńR vV3F嫸$4E@HȎ lB. 8xUp;yEZ;hTX S洡-ب;Mq !`d+-:zx]4_^(n ,55I0/8>odp,$*p4cۥjnb(ۯIؼ`;^sI0V%ZT:%]~ɕrPU(K&GYZWO==SGLYzE9zN VJImw&UyY]3tBR|1xAj>KB0DSX+c=}2C jY*|#Zu͝ E9G\-rAּ|bڃ^gM$7j#, gs9ݝ‘l"|jxaEbPHHv. ya-U͠/vJsupw&k҄6e*H㴋D!ޕ:\;%жg|wħk(i^fKD)oÃ8'~*\D%YA oԤe(Ҹ{w fw,Kg\xf4!JϫrMHzwWܾMs:ROvğhx@J 2 Tf$2A5oG&a#*ʦy$2v?'hcc=.Q!ΚK o$p{R =qi&s{P@9@Sgd+Lk%]i*(f\GYAJDK>=4t"k4| Տt( z,@]߃˨USA @(Yez~:+|Hu@Wd7${:XJ6 j]$.37S8%JrgU8O$BY{3 >]%«ooߠ 3,joS">|% 8|9 ttDAQn߱pZKN]LBHd#p#o @CY**b@y9rl:x DٯMl;"_5jeI@mƝ+r2.?WLM'aqڷF8yfn`lSN1fqs.wh!*H!~6IQ7H[m:Lf*L 'kƢ|2Z1d y% ,=/VbH $Ŷ}DBto:aU@=ma=(!v7J9djnd bzIPlp.]M O˯%\H'L)%?ns(Fkjr6:i#Am ,V'6j&Z3yn?7)V5*CVo8*ߡY?G|/>PEFx=(Jb_8z۝\'B`1~Nwς<~"3¸>陵 LBr'b9E 8ޑt9~,9M^-f_/)F5@stHoXt)Y~0Ꝕa'XBM,Kj5@zvdin u#.m'g3ؕ=gU@RcF%}Y ;Qz}}妘ak|(\s/ a |YgGP&  p3aO7Rfͩ6Y-+:GO_{} !aYo3ެ~fc|UH}5TvTgA*JYG+4Rnd[P:7L7d&'qlo凞DF*R|Or+*h3[sBu߹Μ At 0"+x@zL*\;^B6i  D ?ՀFWjF] +4g3BSuEERnS"@ҽ[#rך:3YRK۳r^oZrJqw.Y*6`<-ধspB`yB dڕ OLwV9(0+y8ۡvt2TT , K^`%v(*֌ej"ʿjW "H/nBV1-dPO`n; XiD<% fp{ חsߊ)W/Ȫ#ES&) Uxmau(Mca~d9v!7K\p!c** I Q6vZb|`nTasPɲ 낽MB8w|,(,v;;pܝfڼٍ*4}u$#ȇ UD~̦7e\<θ(>pDЙ!s&s?ހ̂ s'WŪ /j:j_QJ"5 uܽc 1x6ko쏖[dҪZGf !_m)@n\V9H-$$xk}\V~83&`quD fXJ\" ಀ@pvm⻌^΀A8,_%20`1CqRt@6y0*sZuVzjUvm:K@xq sħ0ݎO>_!>ѱ7R;M$wv6 Jy6>[]cDv力܀Ɨ؆^VOe 쁤PUyRT$ ZWru#S}J@x HhR]z+ۂj;EHO|Ǟd(~7^ Y6 H#R@dU7 ftϩXcI=T֚% (f*XFMqLޞo%dɩ {2 _v1ANDc`>ZqiIH4zA0\$Gή$/*9(x@ըʆL9@7Bsƿ\K8{ADSc6"Pt/ m:pZͨ\h7^@%3} c NɣdۑQm.إO0Zb:9AJwp$5I@x\V77inϨg9v6nUw[ L~_}:h>X -K>K%gK"BAmoJ{ZьP䯙Xg2{ɺNA^;,vh=H; d ^k],"P״iC}لPZ%/I 捛6IC:ߡD)O|}yJ8\?\;cSVYl+x#mԖ 7Z)HEQ@;YYzSd"K<|E8ijJh6/)}n0@ ȡpHE}#ngdNta32Qքs>ֳh9ANnMY#Qؔ DGžSc'9O5'-w&DXO6*RJ'vZ,fG?Ȟ. ShF6OFpyB 6luGjDAzU@R)MEʏ[@rl?gx("e$ngK6coײP唻nDۅ}4دzviK,N=ҮTK*&֚.tbIz#r!X(CsQjYQYVTLfN D_Pà(+w!ۼh JyWgW|Y5U7}ϲ]I+{mP4_g^şx؃_{F!HYWUu%-R>i?nm@B<`&]_ZFCm.Gʮ4ŁJ|gMG8?͒=ZjĉBԝYiT{CsMI13|`ZyϚZC_;̗3s/xˆ3!WSOKzِg7s}hy0K4>Wou;𡭦> ܍ [Tž3]ΌZ@ԛMv9gZՓ`*o@<4y~G `$ 3\kj!h>0Zsv$jhf酭ңҍ)S.=jsasw$H^GBvDnv 95bK*&-FoZEh tC*f7VqT7*-͎k o,KJ rY2J\Y'TZv!SE3U=LE>\ >yO/ mZ\jwR-؃%8+plɅ~eV\r-D*uy˕YEq n!Rfsk-tցF4,0jIՈ-Yx-وRIb;B lZV#cPz&'j"D}V:ݥ@ƱO64eg{! $pNCy2|CTP*Fve֭QGMgDH2dw&[<|4ܷn4XJW Z-SK/i>ⰲ|*SDjw'W)#R)b40Np[rry<1]d(2)qn۵ّ1 %R *J `bG/?@8X\…gnrw}a%&J+{|KgHZ"HHw.|o%`J$WfS*miP#V1]u.僶O^0wEuym(Ҧ|ދŠp4?$0'J >O=Z&T~tW,=.y$yuGbI3{wň 7QQH4w5 uR|ͱHu*h0JUC/!$e_n([9"^ZP z8i)'@Xsu)8Qgzb'VUG΁ީ{URw\bHR!O_mHT**INȊ+ ܴ@WFPŐonln$ QaBAo5Hr5p7H1~S! Ӈai釂FUU܍`Bݜ}XuXig8ww%\>hWop )YL+7pR-Fm=ԕ;g_\89V xiX)nU0bc|K_ ף K>=m98Q_;a!: 􇠴V2%c #^a2.~rs=:QL! p :Ɂp@Q>i"RPDmFrinҳD>tԢ".o*>32|;xzy?$>8"ީ^F[NW{00{@ib 9zw VG /I'~G5dVNd E[%dЄrV[9aBnCMQCs$ JԣvQX陞2 C&:徳g.ácYC 7|_ҮpVy*tt/gH4I:}>xV? ID #+ˢ~*y;"&iLwH.FG{"#V\E8NO t@FT|7|le$^-`!.gs!:4l)bFȚ+}3u-9ٗE=ܥcf =UtBg6|PMT} LF 'LLա:<k/^sz:Qn(Ez*ӎ+9ݛDH5 gzba?"0"z/},/* HD3bJ9R(Hz\|^|bΟwAN{36G:՟/8 Jݤu:yByׁQCrMDMjaמ\5S%ǻ蝸܃"2KrѦHC;.}ZY57%m\oB>%^I 3W]¢1 Ƶ¢Ur; oF u;\U9s1? YU~ptszvY Ch?;##.`k(U#%-#*KT@O}jin8{Hrond,MD)Z}= <{Z ;JFZtgnyHo[O lT,?oAeΝQIhre6EHƒ3'pUdR83>Ci-.?3-Y$6 ?*ՑoAdݦOd vi;9 T: cU{9 / Cp̓΀=:dtuZ <5rd'rwSrT}Ͷx:Tz==׎lRo\[m"h-4绅ga_opDysdh@^>&hڔ:L)JLA mpꄈ6{i⛨\/⡪~hCSe?*ĉؑ(sr>~X!ݗzDj5;fPC3d*!եAw[ ,~L ! Qe#.l_ Dd ؉ey0G*f/dyZ?xWfi&+s?ߕ"K|ˆ=`X5w K݆MR%X;i4rņ٦*Xc qNѵdtOW\:I{+ëZ(&K=9WKs7~"4O .T{^%"|svٚ:eN[KW%k η$Y[4Iʶn8ʇĎo |\SM'c͒0?I}5V7hcD2\{60|b/-)\dM/Lr_ш~O'| 3ݸԚJB{~Y:|RЅf.zO/'!J~cz }G)pbA'?osbTDH}Md}" wՀճϙZY=UZ4z:7$e~ ~JZsw!Waӝ Dm7.6 W٣IaOU:YT1&iͶ~I^05,X͞C _2\omrDxNW$H-Q_xt|Zش}7>*%;Bx.%NHwVȦە>zX1l%(ZYb@f+q$ʤ["LTɾ6SYcw,'"02$ADv23dsشk6rg#(u>W @5UMq)~b!BwNo$@",1E+VTpm< i r%_(@WgNgg쏥AWxquWz2Vfj*00+wlff-K_ tt-`b&}E43^ 2hqDq%JG7(X뵄,~||JZH/J텼{z) .\JSUhnV:kMto9U%\TK |)Wnd]bפqRiqCTɠDj4+u}2ˠJLysგ82_ `N1/"w%M |cE*)IZ+w;ߋBNFArE,㼨.[6t7ѸܷT/(]7Ȭ $TT*<~ @ WpEû]s~YX|{_W"ݪN! Ubl0= l T>~I$)rο,(Fb6HaZ D fg@ql0|;@P *3ɨ A_4Lk8bFPq=$,^䓛gl6`tHzD˷CL$k@~ @LTwT{E2UT×2}eA_} io$QHo}}[-JjVƛ*fu8 N"kh5kd:I n.>RDז,~tC4YfF~ d$^]6aEIXe&ݵWX!S=8A[@ ĺ\zIc(sX38wNT@] e*C|?tKa0 F#Wawsα'Y ̻u$$SđشE{hO$@!#E*ݞ5(`g#m Qʣ?(7 ^nBg60]"\d 9N?@4# ס /!Q x#Xw|Ț%ZW5q_Ȗ 3=܀}Hefu% | \m ÙLh6?)p- zQETgέweB]ʒWAvEHJwwi-]l9h^ˇYK}Xsj\DNOEhӣR; LjxfO^/t{>)L ւ}1.~2oL2{)ݐKq;n?$EuB&w" -/AA5l)^H!㡠1ru|gG1dzP +uϋ_dV ow%HZ-BZy<'WgΒ!&;k ѽS cɒ$/ɯ+|4,A@6yUѽT-+~Ql8IFnGhD*PrƇߡ6UnR@n_p':l'37Z/"2K4WzIXHiVUA EĿ|HcŵS-$*25H/7̆ߪ.[)Sl)1W5 WGFlo~k3䗉P`TpeiWy`qJ?2Ll%ehWGH @Xmv8h7d|t^$!|ՙ `n6-cY F΁Nw(ܩ0wX8}IFpK-2_ʛPHS M)% ƪ3TN$1mSSP--1FncVLJ}tXc]kXO-۾{(2R5Rcrkj]:Ϟ7DJ VrF訯äK#\a-X='ϯ|$VaZyo"3f*V u>+|RSqʦ/[6i=9߇4tA/D7aL#22w֣RB$4-M#;}]8gX&>q\rQXZvXM,}~` ?N%y  LoTEdu?"]˜p@T6݌rnV(d59tRB{`Pz@7N"rAXiELsMyA^R8*i)6VL~Inv N5Ƞ;Nwu91Z r6:twzr 3 Ӯ)+&;ԂT]s*9SZuw0DcQnL#ѱzHPdfm%yZ Gsh4H[Db?w;Ȣ=DͥU#^_ՋYIL ^Jz?;ᩕm=747_WV>HX?wKfE0(`]yOMFM7\o 52Ё YAnGy[~IxAGEsbToNFɧtūdwof;Ȗí7e}p.Z0;]S,`)a TS|$6N7ܨ~ԡD2h>H0\ܟnJ#vy -nߠML,g/HXqdާ]/zj{wq&w*dJgrt"ߔ/}'>8i,|s1oe,dF%y^1c*몄C>juWX|C4@j9y45ND # Ćm4ib(s"94(~r,Jnh~y4C:ྲcip"P''Jܫ>#@mwzH$5{8>t*:A8c}S& R7>soM U/F.D}&uCjn\$zcʁޓuj z45ݤ1>^%ۍ4Xb# 1t-%zE;U-NFs0#s1AWEs1ϵZ_Z ³_>;RjZZ*e]fv7/yǸYu]GS2ೝ/Pzyw}ZF ٷѻC$_+&NS Mi@Gi9ҵn$Pw-8=z>Y%Fm1bOXZb,.4H: Q^k"fjt;ԗxz5|J9OY-% Ik 2XHq-&(բzO v;Lq0$['jDp Hh")_]PLx;M"1PDH]/9D0͇DԺ`-JOjz7fk3:4ob ژh>iimh)=8!jaO |g"i?AvY=7?n}0|W!v7|5lIdirWH (w=Sջɓ#X"] p坔ܭe]!quiWE Ft]>0PNτ4r.|9"khpyu&Bse Yvsk|$Zww7U4=eYSG}i gnp<0* uCڲȌ֠}'@6r[=d sKl¨l)gw&:$~sCйWAg_!pFM:t"ӕ5 _GM[ p0^Xl3tGrA-CwgO,-i~TdX wKǦ-8:?wڠ`5n_|Emo4Ƞ_1A][σ dku;U >-J XrW7vܲ]<jKlyyk믺[ve:v@N7첁:n{\i'BBƹŝ9MQk&tC49'G}8?l%Uο9M BRP׽h L ?4 Ɲ;}W`L$@/bLM}}gO׋ ̜v{} ۗR\ Y7s tfwl 9IxJ \o'Z{ {&zTB$:n<nEr7h }XF.ܜ>T9t{ap.Q׉LTIz/Hvsymij(@5FU-sQ{p!274-}؜,meJo ,*)QA2rIE{ J婈cۢa2 h)mB<>RUX^.ZdurX]Qm.=V,K5q1AR}~O__/H{ UTjNv)T,";>)dwhN0WӮJ0̬'Ij*TɇH:Pq > +^#q8<ީ9is> &sUuF>iS҅Di:B,Ey [pSQ*+9@] ϣZ+]fV pR0>.Z1,.t|2Wm3Gֻ\g;HW[cmA/+`ݓ=)d@οe>)aoڑ] IBsda_y@҆]\pm;&AVe~#%?3Jb]=%PRaX3xW# ԩN y,%4BiR*iR F38sՍR);ZW za?[mn*F %U`c/-=0|T4=3-P'U#:mVyѭnZ60G.<]V:=jL)$xFze Qjmxc^w-$g_FD w8 l*mL!jo- -=s|, dҽ#"%t`mcyWAAöN.S8@"dY:`=ܕ@ŗ-,hP¥v]"iTЁЩSI{NE1O il"l[im&d-\Ǧ.acZ+~V I|"#@W!(|,n"@FD~)#xQqhXցu>l(ˢ UGM2{q6%kpe~זc N8V®GgDaWh#pgb)6v'IU0#3vdB)ʴR\GXSTU~`X_$ yu&y.wgM6B7Φ(g"XLS5ݥ9-ybO]C~u6}D $,; bLȱq]^u$/ĔakRHTS6r"͙=:,ᎨjTxDuEJ{l[޹/{ wK9,#km<'4 7"]Wexc3PdlZdi7cՂfu`$ gB&J ?F;3^2ڙyVFN)e3@Bx:o5'P狩^dS1PȵW^dķH)9,.mtȉU# *=qgܤ7K%.y=0MEd1'Tʮ7UqHUb +6K8KlpC?cHwӨ;ϣ@W֟|[O6:|̮4^/]Y,Dt1z.}A7bq~dYLEz*sƴs%*jb.}f_EqKetU$pD4A {P5jzuh;Utd垵%dpZDVV}|HNd#bÜ$:^s槺3jLݒ{|~kA+ϞtMz1Ld7A<6Ty*/ Ҧaf JNKhZprĩ8Vje4Ͱ>$ OU ay~{˛5[kQK7$kbmKdsL)2ݺ>mz)>. %= @$RYP(Ze*J_ vytqt7N2LnrqD&tVإ#[ !o~u\O`ϼ5@Dމ9sz3wǔDGuN) 25_Px'Ga̗2eh:24RxkZU2젿eƣO}.t]]ȦL6R8QCo8g>q kKϻibt/eZr=MHA(zb YhŶ^Cc":mhׄΐVZvcЍDNIuor ne4C*kk9ؼC]\@ .{MOllWcG10 iÙ1j0Tr'}[@L!h`lvgM#/N&3x6~q P~;adփ.Tݨލzn_yx ]_9(g/(N 9HZZoOz}$eHQr""]}иm ͥRl/Twy$=! plʳf5׹ʝqSќPD FiÛ Zu1>&lZ@O8C@$ɩ{~ =#=":cER>J Oz*zd1x >-(ܯXL9) x]R$g>YlM܃ub9tZ?|]Kَ[ݔ@h w?+Ͽ:De h&⢥I@ԯZ4zl?Ht+~e;9ElL6,@dwLSwLמ_!dͺ|oI#[_[ H)(O5~XH%A^^=qkyVP&Y[3 J‚'زCFac7 ;eB38K̝jKWh)vXs{8+!#yIUo6o?CtgK|8Tzs$mJ;ETwRaPn[j˫A:jI"xj` 7du`q.RL:E r QI}IUmgKryeզ@! Cuq2Q|_z8WV䳼~/-!/i_]B6I;m]igϜ)ojCEHxO6|E|"(ɢI%HuW Gi pKdf9 4?5뙞g7ʠdXAj;U̅N'P!+YKyrQ^ W}L:lFHs@tw5KaҸ q=%Gݵ8M&pXti]NJN0*=qd#w8A8j -9f?U_H?_bre]r1 >+8 N}Ftmr:B{cKe؏N[y {ÆS E']O^aRPK;) #[n'z_PkIyJ&` ՄO|ٻ$mKNJ7=PEࠃ/qIޕ>w-m ^Z)]\.9ְ Q ƛ9T ÄVx4JnM%;WYFJKMF9d\J&t1c u\:GVƟ@:B}1-֎ 9Mj?$DsQA`4Eۡ h8FglT}?E91s!_(A6G̿%mF L7k!]4 A\CK+㥒61-nÅ?]?vk]!jyW-š40jW)\z"u^U=b*@H"DvmxkA3:d^Y9hw}RW h쾃K}ޑ/rًH,?g2ۛ_G؀ zq`Kpv9Þ-QgW!Yct.! lz< ԍIR3sJ %-]GYte"HS;ؓ#tQN Yş+r<@.K w,{D Mr"k:RKE%3)g}4JK_|mEU{sIlmn2èE`|z}"3jǟli+?ߗ պ]{PiZYZ g3iX!)p^F{;BdE'rb7=s*>Y'Iy3+o`q9ynY`Eå/Kbد9wǞv7)H{m3N!$a\ ^dPPT|.cCd%j? c7v#sg}yP"3lmϘ{NEjK'b+~ n=| (4'Z!qmd_ߴC63t ~z's ld pdcEГ'bxm{RG5Ԋ23㬸GIUEo2;ԏ g -M'\emƮ0'\!,z992 MXZyw"DAa:@TLg7ܟO`*jZXS=I"?V $sم:"y# s  < E߈J`@͉}"PLv~XnT~bwO#)AQQMY3gt<Mi4 Hd4;!%_ȈY?h?#^;^JK_{Ddʜf\Qb҈z]q'[A '977T>";~Czr/D->h,: H@ӹg򎬸 [kGPCF{\F[+HZ+(zGӰi/ʷy1/ oAr![(#SEHP\R@HV__i)߾:e /LAeߝ 1ட@sVM^x9Dжa"# $ U5.j3]/R=Tj# k%{=I\D /_Zlxr>yoӜo'_VN%9UdQYY&aJWb;TJbʖן ؄U1 gRCj'w'F; 4Z; Az(A u%Kxgqߡ|xuf'ʄ-ץZtZ>>AE펅TJsCRϛ2Qj)M#z:= i$%̚7SlΚ?ǧѼ_] ;Awr( _Ǫ!;Wks Q* N A={wuS[0)tmdlESVBNQ{97ڟx&VP`dID6R*H!7 NrNN+mu,msdN.QĂ-ƀYލ:TC0t_$c}1 Hfj :zƛHl.b;J$$WB"3E^^_HO dJaoCk ۝:C]"K6jԇ[:J4jd|{ γ %3fV׸-}CO5yj{`I/։[ ΂`<ڧ\I=H^tg7df޻H3̉1xEv'33} ~:256z-,]ҙtJ͌$zTp/DE-2^ldmxqj !),L@"d}CD0ջ'tJ\Sz珅Yn#4p@]˲$Ch>Zt+/-8ѷYhՅ偸Vq'YH-QU`xpHy`4,)h0Z) PCҿ>qQ'!|Ydo<^$]+3={Je Rj@B Gkq̜v4Eq$5=1#ӵuB!f֐gRds"}㫤ERtYuVmsZ@,`U` K*\"E C%S{cr/rijODTQǞ]Bj?s]sId-It[~H|sc \=ݭHS j@sAIx2I6(o)/99tUDrlZlY8pq $JMeҦck,Y/1Jz52$Գi!;W8nkHiڲj_'/Y涬dsn ֐rA wg\Z/ ]MktH-߯Nv|F*餶m?"KAOh60>O=V鴞W)%л慖ЊPpY:E]oHAB8dѰU+$1 O }z;_A^Eyl܀#97O`CԅAJwkzk##25uA ~z"&ܗĺ=Y ϝNG fyҕ>}*އ[n_>[Awģ| sҫ:k`5z#hSCDnn9f(PHOxǖj RBQFVO["Bv` <;I!g'A8_$H.[BĀwt|؂h/]{1vP=HWz8m dbw&6(=M>_s[C"'WƇ1 . DoءB"ҋuH[(Yw`;)l&%v)Cլy<lR.B~A'1;đm_24>%[!-LH:XH)k (SZ1w?1J o#8.^9r5LM2Dy mES]9F)QMX 2НdE;CyB6x/(!H$HĬl<'JF >Ed~ZܣaD(yj;( 1c*!](=O>; *Ъr xe$1151,FӰ32&{7@$>ωo*NThMGNkGkD#1yH^td黑~.OYgܖKٱ\~f %cfI?7uv҂ o6%rսЊrDB2~[}Ɏ ټsT*s  At- o%gfh &=PKD8]6-[]{Y8iD]EZ(PwDBirfHm۫i}$Of0dڅr3U?RX]Nd$zEH Hh\&L{Ԯl0@H#LM@/TA2#0>z qx6 hY瞼v@iAk8Xd*|m =im|F#~Qz8ҚɤNF%:#P2Ğcs;E^ a+ Oh Xh|zozSKg!G9+Md$gJ b羓 ²҈M| ]!*<=[ 59C >297DC T)v P u?"sw ģLO㶈hZeLZ`]WPTY)PWV0OZ\Ԉ10·Y-BI8|QFvW pmM6Ee2CuۂQuBdHSMkWNJ]F݀uh$H(H*dmͯ\bjLR :!mVhiS:`"mHXz uXG  ɶ=!EaR\hFdϑ14|F_uRO&njw "n/WAXc+#X-`,2H%zABq 7iG9R e!pvs*@KaΚ/?RA(PtXAƥ9^yrU$^r@k=JsQ>ӾT}[Oɴh_*!OZ5" Bޞ_I\ ~$wЭ^ݎ_ﻮPʒZB8<˅7AEv#H\v1lҵ; 2b,S[G2Tkל/s飋F[Nc<ԠZkkjH ɇ_A AotA֍vNH:ڞ#4~$*<@fB0*[_BeޜX5IwH> Ha+W¹f9d?Mw7,޻г̿k^pFO4_;Z+COwy;z y\@ !0? +t.$K)bu"Q0rW|ʌDgE9}Yإ@;p ʸb7. Oxa((#Y4FL[a{tP UU6;n Q }&h;93x[Vs/+*O.W%~(qM½?GR.vĢo(N^t剬1~ӃLoJ|?S&`s4NW@Ոݥ k2-#нWh` Qd] λ~-ҍRaŨg=w̎ B?1 ͭ 4?mK:f_3bJw:ڏ͐?<S~x֟= a]PJ[Yl kΞY'Cyw_+¹o/Yg(T+ Qe\CA S,6wWH8HlӅ!NRNp>ܠCթK+VKsM}o ZSYNX/t/:Q-+XJk?Ҏc4MHKS/HuW{ޒ?HȝDډp-mJF=VP:)]5s]@{4 r;(5lv3Qt唵.PzT d\i"|_2>'0{+<I:oĝyߤzm;_ׇwCeI7X\8:RD /65$\B([iMZ&VG){y@jBkEfYҎJ& d9f)9'G6ht !: AY珣B9h 2eAt#Sy]y6)S&BJ*KH:Pp;S@@$S6Q~g'ўQ7RK`tnj:r˟OĄalO i t^T,xPO@|~.QVYHЂ Jo枚!%6;C'UJ2lK?h}0,F $b{wW/sW- ``r9(\'-80bIK~z*N9()3IG QE z? Hʺ14@?}/ʞr^-O)nW@t~r#\$`vie7#N?Ϋcw_8ȅ˹%;މjhm(B( - 6cKdtgA'Š")X( Adfh%Zo臧ZED{w hM(8nv)^T^M7X(D##ѸBa hkBV7y|i7T%,+!rhfuH4!j1dFO$ ]%sqp. jeKmTcwZե; U <[~E $o!("^{LzU#dT'6r/2eFjل2 Tҋ':#go"-YjP[.? /@濫ehP\$YXT-+&7ZPeQ>P"(AI>5뱈y$,^s ]ļX칤> NpLxVsvBRP3:PCěB{?{{֨S> *ArAIu\Cދܐsol5mO@@,`[@b0&{ٍcF5AL*2=B%:49@LW^tlw? DFh{qS^vs^hn'Pi\+3)Dj#ק'd$X/e`Ok"a۔Y>|qX5Y| T놪7[L~n׈9on\=»tۻa@blvY׺!n rlek #V2)ozn HϨ-x{K%Ck֋DKb48cpke>X!mOsDn HR\wt^F/O!f46~HuӦL;}b^ψTfK3l~n-ُҫTiib~,'{G7ҴC}74]B ̴;O4Z.HBz'ߕvC)/ai.pnD9"Ą )"tsa౟t,8g3o!1N Gm#5l֢p@+!Kj%P01 asMahD֦fbFl=o[)MANeuA$V$)Y?z3/ ' cN9sivJy37% mPGod`yz?SoS'BIhR,dr 'x<;^oR\FdMU,edwIFZD6b%eI7,G";={/#RRQ.fZIS3iw]1=w=\C)6ƨ.I+( iOp'҅7"q}%;Tku͜EMíj NnѫƗ@d U{[H7[˓&8t/X_~ EGCfkCʫS&Wx]ToD`|g|՛mlf\:`v/=bⴵF i45;45/v8~?v*!*pRmӃdɧK-:{y2yImP/Ҝ=o"{*c'MClsq _x#QoxnНb-lFǹ/#"nԃSJ+HEBҀ"Zx rޓ[r軀ЊB4qQ܈R?%4opV[@RbkaNdZG:"Q[j$X3>EʫZ!WJm9k,{J* @x:)NY K+D- 6:>yLӤiu*WQb֬_-58YgNR`۔O)L@ϩ)56 *TOCz$_N!Ku)3>[@ wbP.ZL䴽Kqzpr^>$W_9^dUdǣV)G@;\CTlm`9Prh Qp<HxA>8@VM")IxR΅΀h' TYΏxvqy *92A/vHC{ ;BOƠ:}P"d~91g gzN~OMBThM@snZӠ`qx"@0nz( oS 7+)5*hhGKe"#Q﨓h<(<>g7[!IwP ^HUy^ vYaA ۶)wiD@ϫcG˺cݏ"J(^eO8Nbv(#Yy3KێE&ZwvlC֧lќ5{\S28&JAȉ'lL$JH>/kCHf, B[)9욮Ǯy ?uvy] TT8ǫ&@/ Rv Kcv71rA(ZHk$5'lOt㗵jo+FۛyQZw.}3"jͪNyV@De.t)r^1A0a ʁiJ;Tx@>jm"qSuQ +\(ű![LlK$pZ}2` p<{J n.K/V0aCԢ]m^pۘ=KlDZD1]A>U;T}|=b q Ƞf]6kZ.yrHE]_\Η;&ّa l_dPL,BOPl95$7f`M>뺊HÊ_ǡ'^{ oc0jܮ Ś|AE,z"wܾF v. ϢX'Bt4[,Ljn*EY4 &ʏ}#e 3eG>Kk`^ (O/6^ZJ8reB7isF#U+A=p5 s$ttrW\څmѻ6K c-E_GO޶.Ņ:U8^|T~#-_ h*/. b^O| #YBbixjWG Թmd%F:lcM}ȷQF"ݕ.u׋m!s2.hכ156) b5%ɽa]#<ܞ az2m^ \՚#9F!:Ȃ۠0Cqg_?gUK5ӋLI1@aMM }}7[ } !HOԻ?Ǡ6Jf&^0AU9L){6a:ZT&!Dp~h"%IPQP=Wc~ٝM [gŒ#>xk _ԉV EεAۂ՝QH{u!>wL|iڗɖ OY>Po'5^1hw7pX)h@h`: "+)ET*}3E ic틨uOmyG9OtK9'Ds}7Ch3TC dJ0LFy Kzw,wM91 U 5N`qM ṙ TͲ܇Iz!sB,+6FB@&ġAqa*XP}0!rhU" JP־x/Ե~2D Kd>ʗ՝t"}[;Cq..31ǂ~dcn)|O>>̖Or I4-d ɸi PedO sW5%i9Z{UcyIb.|ΦH?t>@XEbcYoGV;|bVzvRdUt( Œ˲mQ}=7L &ĽѾAZM@좞ȫz0ʙ2Ew[p̛M`py!S;x ˰G! It"Kh+uDGu$ [Ey^e$+)mOmVs"~LɍmAU `|8dSEVwe_ h8i)bZBZ ,kj!`w}6j'dGP}6YOn@Y@vD񹌶B:IfS;[sij*ٝρZ36 t Kb8:OYlˀBrBV@'ߧxHv ~f2ȶX3"Msn^sr#mB5ǝh Qʹը0Tp[-۝X1.~XH"xH|dUIz"Qu"by92h!힀s}m-* LfBAh_'AK`Bm~^c3^ Ll/°YыAqg=x($施ڠmrRѹ>?1 @WEbvCO3SIo7[gAZ=4_j%2|(ӜzY'BM6;9a:5_Z>e$,&ԇ*Lko6:29/ZsuXxHUzz7,zr\KV Q%/UBrJMH2+әع/8:&Ovgo+$e`n3맫(*OU$y}x4Qe7!}hzY]ş %%ŧM \>{n"dG}r7jV/F\^?˲MEwZ\g_]} _H`6ZɓJ@f$b8w-ATGÜ'e#n/D.?;9f DڣdCR#َӄ;g/ C=[ڿ| LlMd+ Uۖ *v3 &kUvr@GTF )۬&x&VAN^f\pLcR#qjfEcS`Z :thζDIf]H͕ZP]{MޫDXOKD+G1Va10:!{J6 &lG~0AjknO-]H//@ .]qY@Rb|7="'ųʻdz\:ya1,=lW .2XF.\YO<j& Gr0:ӥm*űOzXd` 1 m.Љ+LlN5H#} ATCmH(Q:^YNő B){e/$p%@H ]9y@'wz/mSAY1]S ޅŠCJWym`.]"my ÁtǨ]d}ypCYJOJ/r|7?3g!"^ߧ>Ecm^Xy+[Lj Cw~t_\[G);J+ A0]ZDq*?3{BlFa"?vb$gX3Q^ˏȶ*8s,֫$-ZH/\Ȝt3pD I?ŀCc.k# ɛ&Nwr-hP̓φ!up=}Vf AB NYq=OgcAT%ȹ;{ ڌiEBą3꛹o˟:O!jΠ؁/ ٬+ɠ97yM,|}gC%% ™:[_<7;^h%ʞe6"&WO5ֺvigk I'"sq.T_4Sjķ'G> +\mmGQƻdՓYZlX{3z!mͪVWU[HΕ{<&J7ϴhwg)M'&skr&>E7SZ';57&*(|},5,śt:A[oFx`+/dB&/"r@fRV=[/ Kswo tƠȈ*AqmcЎ2_$&\Uy$SF0>~pM4  ĐsB|&t{z;Jr]R[Ӵl«.,5F+kCP4_vi9~E>}.$ASuSMWsNԦr+DAGݼ^gk m>L߄-;gQ0JuaHq mKas5omMt=98ŊU͢FHhƾHl7Vw6jWtS_MmŢts*Cިbk[ Q=J - 8a $1~7 XECJɛq{yǂtI} | #t}M&P~3[O`vdo iM 屉,Hf cbjrq.E4G;m>YD d&<7LtSXr,SAo??+r !YOZ5o]PfA\[iՂ+ f:B] }kwch˴Aq 05 U1@J@!VW %Er9Wd.͋4*UMW"9n50%0aCo+ m3!JŅNA|!#!kr3A.z-WLUy4/yC8G吩@+ J0TwĮ"Lv:({F<An෗ Φ,.4dIN{_73s୶F2>(D"7Y%w(+7J`y/Z#ʛ}7gB=;!DY簁tI|Pvԃi6zC~:{NvrD+ ,l. ?c837fU{>$\զ4TDQr-l %*xɵ +Ag#!A`#~ОP)8FV퐩Nv z2P%ޓd#iHw9Hpzf\Wk S|2\>E3+HV>VW \ԟ쳉 ߺV.n"y|4/wVa2aQNk*, r >9 alWZ\yf Lΐ^)s[+ y"[e +W5 BY=q@4bʊ Ʃ A&a^|MH!)Uz^1\܅Xb>bF_nB1!dע'8)|WIS&3p3 bV >3 Q5jx$qI*@6zyspjä ?Q~!^Lf>T ]wM:EwAB=uG҄u2wD\ͭRFX9~oD~|m1Tsʮ0ܧ֫ӌ$DOZ(t)@qx.Aڏ^ag|==//$;HYk~r\֯&C=IFF4?+(C"jX~D)7= y&?0hU v}$',=wDe}J5 r2/  `j8 qQza[xTj:㣫o}FkVҪI$"d=X+ܫoG7cӂZqL`<Ů LY"n<: W ļW%.+A3&X(iΝywR[je Rri< %sM: y\ >5piɥY/Bhz/gֿ~˸,@\,S}/{ns]mapIbEjg/I#i։/9]N s 6w &@8|BpTg~Q5;I!GZc#}K/U>Dx􋨠2n# ^sZ)h~ Hd $2 ' *"% Wq$G, f<`5J-$tW> )JRUW(R]uTjW%k뗨 뭂Q,ukAm% 9 ##Ab]HKh~.M(/mwCn^? Ȩ!* tRq[ቶ#%1v &TlLt=UmIa\. yD })̪aO>([L(ʼV? wi2ׂW,r7 Cm&.E8E5$4 ZM?e܀w AbGEM$үJs~L( vij/|Qa=GӮi#ꔤ$iWP.]DyM*zǦ(ؼ~=bք:jGXd]K`ΧC1~eB>}}S~ڽfcV2S}z7"C\Ƽb-EwY|szaeR?:m~bVd6*@2#5} B)/$>Pm^SIldxU{w>}ȧs8V>cPS.Hgc&* AuYHP꫾T}C*X6Bs';^'ڥPMYU< (z[Na.CXC`KNJլt5bGg|# -O=-?+ߍ.YUZhBE5{u%a`AmePĴ'9(c~\3!Ǵdž\6W 8H9:'CFr+nV"`16L;?Gz)%&SB`>b9]KrV":yHԗWnlE^j(P ` D0iDxpXF\@: (lR_,_9|,уOPi)[Y^3)7 ɩ'gVd%S־(N»$Q\Hj͉5Jjގv7@ l7C|&UyK^J32ƿg& O`@~ >^k=^[@)H22.eʛ 7j62+J %T@P e-1rGx}HdڽOG[ 06S}>q*60BQjrBbT 6<1RT`>Ao9v'2+ڪ]HLo Yׯ[͇W;W݋21QξJVAgۗJsT(B fT8<&z\HH*HP*ٳ'mGC^C8Iՙ5ElnuRqQNtd1W`}3?!ʞR@kX H RYw $P#Ic[;x9DpIW٤KA~)!AkwAu1Įx&>Se-O5p3rnΉ[bamB“/+5x'D;i|$PRV "T@wQ %'^9*9˨E _}A>ȹL\ Rh 9'=qN 22ndk Ƚ36 dD/eB, z؅B7NI'uVO}BjB"fe;{ q!&:"]pNfD]"eyF[|wnXh"Ϥd6- jwZ^^Qsv}Hdj02(p`Znts]Ola. DtIeQ6{ywwδcQ%|6ԝ=r&ڢ |JѺ0?J%-y(63k?5`"2WVMx=07t.nՕzypQ] 0(xNxnRu\HXB2^Þ<ҕBo;p[)o&0?-7]D^h6"'0%Ѥw3 WՍugf`j2Tg"Z0AtF-)̻dҙ966:BN<gd헓m@`܆3 [Vvl̻~ev]!ӾMz_+TП.I~WwR~I ĩ`Tv}QV rtHβLi7&s]d޳˅5lSC 1\fS@eTjuW l6pz#0ݙtrdLܾ:&B;@aX^4^D(}˗P %#>Іe[sI% ntk\e:2)eC9E3"P)Ϲ\*S`,Ak ٗ B88\qp:ٌ,n.Q 3# D,Pnzjnt8fR\r b>E1Y{ wP^*;"qh!Ҝ,w/OE5AqrfyKc L=x,/}RR@ùw/$փ_%sۼ%5\(x=|ȫu)SjTzgrBV֋{ TUc]gE8ݺעzx"B6j (4g%$ BL`Tó9hGpӃ Bqؕ4B1yNde0Rh45R-@icbCX/|u:B▢iHf/vP<Å!IkMJh+2S)XȮ9 S#GUp{y㲲~x&VgG*,pR 9- m+o9vEWz4ߑDc$Y{묇5+!ҬHp*ɗFMNQm2M,B)}RlJ4G_P?g/pEX# Q(:4VcnWslCzOf@%%Rڝ 좀-/z5$@͞=G_ p:hoo׫2:t^v,WGCaQv3v>ZS͈mV$CNLpLABi 4s "SCtD.\==[*n_Ed6<` 5>ә!FF%Խ,ZRsCp..}ŅHfm)RY!f̦RD .^82l?vzNXspH55IDPrOGBkn@_V4M aGD͏US)x[0C]yb% 90>X Aԣ;[Ŵp( Kz-3rANډ`8M+JLy6&O[VU'LzcrCz]F g!~Z79Z'`Q:Ѱ5+N>9ݣQjG䈬6 ^.s)S\  x` VWN9oᲯof&Vu[bOǞu-uyA(6I0z(^^0.QwiǗ~j 2z=┧ `PL}t(9DWjs#{v WhO HJ5:7J`7)1 ̤{K_6km ,Gz5mcM m8t|] 뗀h6[Dcw"2ǰM!Q|[lO2];?W{Ť[D.{L!8ϻVRV1p@'pIj4I#~S{p4NJ@'W&z ݙf>²k.J@l:q'#Z698D}X)@HnS! X 5MH:5Zq:koC~~2mY5>4!FcwLˮLpN雃J|¼D lpl 0߸[ mn٤N QVfPre^nt܊U!>\$Lr)_/g'Pmq|[U]@F$]J:kI!cp]C$| frm*}I5./Q%""+'K3T U J آ˾^ޅPgXZb4چp}>Q6Wu*׃:TŐ:`[BnUB)v?n:uw.y\'nF69ON9y*C = LXCwu,q GlTִ.Ws4J6N`pyC8Y._ lI:Yi ؓ”nbnJ_F{HIP+]D!WƦ&RKyDaͭRe | wǔ4J}q@_:H@ѭ^pV72/ -7 ~۬$""S ,e(~GM*&: k߀tLy )w4:|%Z#$si(6u  L؊='6 sU ~:2ٕE´pf|^N( k$ByiP_-FˤyP]K/;n3B+r~ufUmMxw'SKUB\quA\>+,GȮ){ƖM|rͮ'$ LOΣ2;Rx, o[+WײptyW7jkGj=}SG?u_k\)Qa~H[8Vm«y;͇"Zkx[mkh5L]G!Zv_ZX2!,g!+n%Y |o>oZ,>m ^3F橀d2lIiN{L.Ȭ4yQTBJȁJ4醛;@LEH 42Ä$30ÞRu+-k[mv Gٲ  zDfT|>$ J$b嫁KP8+1?oN6 &KgĜ*ڬךouJN#$k 4 㢎j9~ HVO&#y:"BKo~ k_ @q) (s7)N4v5NyZOloitOTmASUBX@+RTG@2kJ?f۫5lJhweF BjV|ň4ȵ dD&{g:ED>G<ojRMWnڠ$~O"W_o35Es?'I=GO%ؕ_ԯxY|VwʡmhҢv^$7@B ZgBMWڃIT ;U~~5Pf(wJDFP & aیm>jy/}yFN+Ȋg+uk{∭RIYAqm1\-e^'1uq+e&p "Qz =M+%uѷk2҇DaղvUkSȪX)cF7p#JNszRѬmMu犨vBaK r)~TU E.(P9S̝.+G#+sJ~M昳[)]D W?&B+1,je'5[ E &?lYi ' D9կ\ugڡ@94<9)cRR TVubtbB1w(gqCӢe;nc}آl4y! {ȐYɸxD9[ _ D%_Ӏ;CS=m#C(l0H*^_ !/s갖j&&x1A1œ;E[b)n$lUm)Q@˗KsAH&mdT ۱W,'XF4x; (vr=x#{5s79xL nj⭳'Bwd_Tm:y%A^ cN\2a{WKJHY왕Z^W.mD9HM;[ ̗Q>hjቮ~"!ȋLD4 sHE8, \W\Bw HvrYhhS׃\if%fsKID?2M6~JZE+hiLX`T'+#z'B>܍9D@=dWݏTǫ2[CqPZHu+:l45OG͙@\B>#;)S/ קv-?&͝{RQ-zz bPi!T$Ǻ')c:;;-یP9hLyi${^΀uUh|X?Bb0NpԁmHGDV5kiA X@ h|YP!c]~f![Bqo>'R{4knm!H7 ./sFq FRQn%^ck5"B&c!>;藓b /m{?E) Bq47JWZxtR[V:e974sQObG(B@zu)w"AylMlze=N&'C&-*#c >PSmH7;C@qa#Mtj?;pu83nШe59P- &A)Y%aywϛJG'U]!<ͭz",->*|P|uS}Rb})b>lBʨ4=y^{Gݓ NG t^Qy8rG+.Z R:jʢ޹@NV V0FzLKT&mDoxw(.97kDK]5BQQ)V7ފzAFXSMntP#'d"|P,Q>V$OHs&Ԭ#Ѧ=?q]6}fiI׬kY/@f[oߔMXcfhJjLb$O]Ҥ CC&KVh[ĤwO2y8USNStؑ;Aޯ+0~D(BхtoH=]  B0\g!>ul39M>j- p)m.:v|`XAf]_QIvsoNqo[]*jw i͝Sɺ,YY<4uYTݖzuH5\Ej߭Ȅ#X] ZW68(8(t!j2o^SaC: s I˅CWp~W'*&hFᮏV)"Kwʅ$]; ]uΊʓֽ=AHzN"׿=<>qsn@يE:{6r"}:žNii#ovpW,"9^#nwm#8L!o$9-Ȧ)"՚N-sfARE}XH$d(ݍ0 =, Dպ|xQ@V\$9wOAb >˱Sז*dV-i~4v]d#J=#&^GحlUE.hA+ Ve%y{b3' `(V3|9|(riS{ W5}?QZ Rc{az[KB)e ./ÐA%Ql^PsDA({AJ hH}Go07p{h}-佹tCz H]}4CܰX̣ |RddO ɗZkS)\ΠRlb_u\qe+O r~Ճy5ibL6okf#nuݶp! vaSA>cxrBոKӠ$ڿ}inʒ~BNR[&{x:b>6ꐐtFnI.V7W_y(CB\5'~򍰭{z>2Miګ9qN#ާL' ToTĵ`hU:ڕ5Pm:2 RM,R#jkNEgx;]g}xE.l +31 `+5˷N.;H"v%;@Wާ$\Ok{F27lخUqa7iȽWIG%=CGGwW`xA5 E~:r^ܨc>IhC<"'Z%a$0$TS)M+xpLR@~\R~v^ :G^aTV7 O1J[rs~e6hM^;=WvKk6ך#wU&t4MAu?;|~nsN[u-$xT$<ֈUx'x_75?Ob)Yp1)A!ȹVװl9Q>4 A/DلN}i[Q,̝,#uMPd<zԬ@}4[)N`>&."%|'S{c )},Gpc}m 었Tb:ʛR.Ks)u&>‡,8.uyvKK'E֠GM9At'{DqZVm V{W]Q%BIczS\n'U ^z3AW}il͏ȱHpgF+yN u_>ͶK !Bnpşa|R[ 2}VuyT5ۅ6S^UWq ;xQ  Z璮2m ]8mPd̖kNx,,^.)k^$zO;YTu"Ac{P񳟣[2Qj42첑뻏*~bG5!D|?]ex yk9%5+0O ,S2?iX B~dVdճ@Tl'KԱ= NC\vy5C'|-S3Ʒ !᛬yV5]O6RȀY֟ʒd7_h>EVZ@@4 )e\(EΗB2/ɾfG1@̨#u7P<YF 6i7 ߊcFr􇀻b`&тk}{ CcMd]&IsIfdsNmF[w].M4QCwA!SyldH{)C R] J댤eb. bqD )bF>9qɼ}#H a'VD[b>v@FsM$UV/]=N썤aIvuzO |e [!cЄFM[ f$B<[R# sDɉwjljM7UiG Q5T&?#XH$(4Xi]s~meR⃱y8;墈R5B IpodjU3HBR6ߨf#ɋF,1j{0t Qkr]UeO XHl? aY-|5~~fXnUӁb7UBr`1"C@vCMHqڵ8˘?d}nAA;V1`t7)'r_7 هhwE]&ĎЂf{*,o7@Q}֗.5Hr>f\-WbS$1{kzޣ~S8oy^)DrM87ߞ.Ro?<Ɂ̂'D]d12,WU&Tzg& ] -ʬ8q6Ӭc#ʹ].Sʴ4 DvO繆^ȼz񳋠Kӈ]ν27*NAK:V| ~ڟ@; &$wD+H5&`fy6T[ FQ;$<0Cbdzg"Ǥ[k3wN.+{R@2yII.Fkj~g'L5:mUۧ?L?5Y^"en#G)~+0r"(4"0;BVtwHn(Y]6Ǘ\ܞ, S#f#TfGFN-L*c& U}'D+)׬Wm]v- YlvT^􁐢_\T |u# ad7ύ4tmXN,P~z> X-@Ih+S'a'ɛu酽-U~a4(~+o\JZ;,!uExEF{#2o`JH~U/4=Ob\d-5#iQEW@f=#vžwԍZ@fÖBo6F--_-rv7"ޙr>cڗ {F겂@PWٓ6riZ6~~=F<" 檞o!jȱ'L4ފZ!)4|=Pļ&̟ J ~]2uYcr!n2v:PbZY< |{E&f2BOZyqX[4A%HF@i2ߵ bDnH)*`X2`ycHwބ:1'+b[(6o{wNCDU۵V]t:ˣ H斝дa5s6Cɫ'O;ҢWVwҫ5VѢ_/'>TRHټ:6*kGWS_9hL4@ri&fux,s_ywjAPU_=8G5!ǙsũL ,ST/'mGڂU jC)BHL?٣DZ0Y@MB -;1"_ @õ YZԋ" @퉡\Ҕ]1~TLO#"HY!mK{gŃIw̎[9 )B'^YT*[aQ N8ւk,,0_T.rz }#Ռ vh~[jq/nY@|5]5 %n(NЄE&گ xx)d:iiN¬1˨0g yӕXb#"1~[ű(l|Kn$?+L>#Ci|6BpܢȯʟIHPWuռ8A1R7 c{o֑l},b{5v(= *P{[YeG$\3 Y&U'~@,%:1*8`("#! %ӼerfZCb$$7Q2?IsrL)fzr0^3eZi  (zlˆ8*;D0]@Hp =Dzc=}"K9dH忻{ܶ)DOH8t94;= diQ>d_P>_j~e?f$WhĜ">8BTyW7DZu^Ä8jTz [Q$4$P{=?r{CJwBVNio3Ge-ȈwG1Jt.˲p(R$P7Hx2;jg*7Aub-T9U> !)KABi6մ""~<$Ɓ^y5A}/Rȇ]qG"A !T|@PVj5unSEݖsXl?3Hݕ榯aԠj;4dM(EuRzI&Yb(A< a%,ZMih4z`ji%va1SBzj|z )# cNJY#@캐fD 49WQF25D[=KGE L诐_ ܃zA}'jd~ $%/Utu Hє& $Vy;I2 K] %lw0'!!.Y =P}ֆY=(=+lY):DbŸExDő D?ˬ1g:69(ib0R2DsU CY n!pѥ(YDRAҮōr7梸&8o̕62ӳt@܏p!h^ĵlL7r ́Q*k X["A),9C 6"盏L]6[ &9q#[4r1Pq>@)껣N5BHOflaF@@k_0*g;$3yM΁,=DJD3k) >[)%IFx׆kzHCY0v##O޲/ {z 7Apy wr,j#Pɀ躹cB&vGLJPDᯭ #O3q#&EKRI-{?h(r!ÁKIEJ?ID$\ AOI|U#dqlr,Yі/@.NQ͐C5.A)$I_q[.,K'nS?{N'h}aP]ǧ/]%QAu⦧`كdotHtk%컱'm#M?Lׂ_<;2'=̽Q fP2 그 D݈TJY^7kf3eOv;,_y*;'/Ѡy^DQinHݪyA{׷}6 )xNjDuE0;`7Ĉ݊!.]]M<U[Ȕ*w=T7yuTߨ$[EY @1xGRCbGl'(T|̫p*BL|F`Gi bs)PH, ONt2[=>w(R{zO|%B \' iⷙo}py"tHf/Q+|HQ%nMT+'u]+/{&_ya7ymyz&*w.ytY@" fc/յF]Bx#]1mI]tP:HtoqzP5U!M@*F1uBu#rYhg5Q{]\ 'I j]^Y@k㊈$ ag1κb&K=V@ا'LI$SY!%("FioP򋰵 R 1>ˇD}zhc> XS G89J73%kִRpﻧ^w'!$īWUdx457}V}ERATgڠ0,~Ӣ(,DueBc猏Zvb̷HI(%CUn'H+򥒟3 Pe=5"_OWob>'OH_1;'Fi*̙-|QK@W2t0͇L l*VxB2=RsB]LhXu̗#VW#m^3Ǭ$1`~64ؓ-,YG)l{Me[^iЏ  5Jt8+_utO%3_b@ŴRR)n{ZsdRr_Nw:{@M2LH$FݯSF1QE7ɚlUXHA,5yĒu-jFd#aHɆ"ay4՛TDfљ $#sYmw(&~we_5xqbQiٯUpBp@k(/ q,kы3D~F\d 5#X bN*@f=`1gr3oU^\@SLeHĜ,1x?|a܁ko75~\Gə{@.۾8&Jujlʜ8Nn;ӼSw@zZW$G*?US_;}ey&|``㺅Q=_ywE(YmbȆ\}A VĶ~eG C7&?s Y#*@"}Y*-arR*>ɻ :啋זvj Ϸv R!!]<^]{v?Um4KW@qhA5El3& 1}xTIS1;@I>jdj=?R50t+? ow̼dϩ 2VH?+$=O]4t"i#>i]͗3/r i($SJk^Eƹ:gk}&y(>8HWOE-B{."xdh׮o@@"87"$IANLtյ.,d >l2 ܟpcw?*'# " S qx_|#]KtTIfۦiq Q,ߣG2,fe.i޾鸈[3S֤N>y.K⚙:Gj%rZ=1=Cg5*yst褳H7P뼠m};AY(2Gg4k9 5oRϡ'ے* ך0oó*CH66E#el$&JHګфUGCUED ڳ}&,q*hNpETX]!טC~s u314@!13ԨLg|Bvx's@D;OqG2əy/1B@t,;")t`;kת ~`40.m, "жRG\ ]DCV%zbăe80B]S!\(܏'ز뉂N,Zrޔ(4Q5J|Κ`TU Mk#&(BbZn3Q/iz4(%l(S,-6bp}?u`S&HuT=IqȌI?IFBK*Mp%Ey؇cRl2LбR6eʖ?&U|_E{g$G1CʒZdfzx3/|:A(nI IuT@"͎>Zj-ܭlB̔*.ٙvHRzu͚HTozwX5Q*k!b=$ =D= SSz.>^Β+\x|-bsh k hy{Y1 #'""*O~{̆{L6eg Nլu>1'Qac ]I]yrdQ$ :%"$3t`óRE7b2Tg} ^q_>L`Y sJ2emcd`xͧW%7g4D0=<7֞A̿Bs=G됏TY͙(KZ0|H]AH)TYU[ k{;^&;Z$B,@cً{FѰ@}ܚvF-'t yLhlǰhOh˳ ȶ&Z7+xm$j͕\!! ?T$BR@V/4VEUO71\ (qw\ʢJLYU[FVfqK}i`R95 xk3#[HqBf`qEI/u, Z'n:\FUEWgsQuePl37\GXQ~wH9zcM<(CFA6:ݝz*3: /׼ ܽ fsQF/!Q5$녀oVQ4xk87Ǖ,A|EU77ׇ{[+ RsR(Cw'M}23GYN !IHq 8X㉠}?W#h`. \m&535[ceidS"tVw%0([+GV,Ucc܅*8r]swő5kwkQZp^Q}tr3"IBHr0 \A QMgvQ >z(] .}&m>s}sekZlZ/dܿ-~vظ ]G(j-o-wQY$t,sCʦ}eDU4E8&+T$Bԯ vWM0Z".y[_EȻFdBm-sWhG'ӧbЀ2#gپ T57R4frRf&AYPZfsPG~NDv0Z.N k!Aor).v!]7z疙o;r)(D. ߦXnihyh .rbPb^((ymak<oO_m ({bEQ4QD 6,W5!=>!5zcr 8!#!}*kQPw7M\vZ:- T?q/A(_E[|ƨv|띸gD "`Y9I֎M9讳J 4[LBnb 6wML0cr>,FǾ;6kKO$Dt/« sFn ɣ~d{hOF;I8mpXPB@B!215O8 sr d=&0۬7Hɸ%!ێ-e\/ ͡CCHF~#hS=;x-?BWDI ?t{ YP| @" w-эk$|E;Q$kJY S "4WkP]A\.Kn"I_'h64PyZE#,V#=J y,OST~/::*4=7 `YLXf z-?Z&t]7r͙iUgM%9\BXCJ۩4jn< 2rۈ'Gef=|(_0%Bzד,z$].vY!^ 0ݴIҎgK2H$5Q5OTSr gX5g,qNA5;Wb׌{|054O^$!UR *G(~R/l5~6]x"7U-/Cihf9&9vU5f>GP+?[|e@\IElg͒hi%h[`T JXР A"lX6f3MOԆ[fRz4 c!+J/@^yYUkFǯ=Ź$Un@?*ܧ$I m]H9M[z׊{we;Uَb;>JHtPgʹxS e}Т>Qꢿ{pDYr Ѿqyc"'dH &\Ou5$gg[lHy(lqib dz4Edu2h"~v0-^ :칏}HWEHZ92MeH{q(u"D8W3;[ZO@%I@@jðw,h.ZpM\XM-qԩ3 >GvgM=?\TH1nb/S,! 'C؍Y=|kQ_(L*vTՓ~_fŞu KOPZR_!+p1Bh_7ݕ/57&O[:V|.ol5)NaPß\~ϗcT 8D:jo6H: SyMC?J[甌"}=0Y:")vk=8]IV,OEYe,5u `\8.] }k\ jxY xM-`|9KUS~Vxpk#Y GZ O.;Hd۸>5YQhd,${\ ^뢞{NaI-#|| Cdb /-ږ=2Aje<̺PyQFnt ٴY\Μ`c 'mY)Ϥu1T{g5<Y5Dy!l ӻ9EܽwG;#=z&7B.I [fIr +7۷J[0^>CYQ5p }w`Dl(&PtDR1NUTH]qWL V";8m Wݾk=k׈ȩod=bt2Ef=viHkZPlS' nP7{l!dFC!wbp{ ,K`qtNdA8^3cDn7ܥ<5Cמ Q'}so+*|&Ъ`H)m|x`49SBX7='rY.5Ϛ%$x=BA%6  uk"|,Y)7 Hy;N2yXg42=' e,+?#ޑ 2A:[HJ$0^N4myyut%{"&ОɶWog#ŒH&8,RJM#t?sXܹdJ9lH(ڪF Kܽ!+G8-A?uƇD²ڐbF+I픎H[, l Rw|+s-߿;bnP0샩Kdu#α7:)>ku[7A3Ƥk|a:݈ʆ1eȸ9jAT~kOrHJ[, Igų&߸wݧl *|4^3#}*lB<=Qɛa6igR#|LAR9F9VBz&Z\DbnmFp'68*"c􍜉cM3jrzKJZ鲲 b!PQT؏m:ڍU0kw<:Hh ;7C9q_+&MH)M@[Z ҏɔ$H 8a 1Q8p#hXkJ]JQ46[M¹(,4,Q䈛"4VXڿ",*y'$ncE[t{խ욽o"gSI lEAbNr!Ã~Z/o FXPMO/34񽑙4L"BIc!%*mXeqvk ?jė`@s0 5 ?S7A ҬT;d uj~ʽ!^y  {RFYQfM׌ "渖*8bd%^|$j tO&JsNlvWt[f"=c 3+ eTaATօ۫/8*Bf/Y(wd1^Yc4Um*ՁLzO]'eØ/#Q9"Rvwdqה~ܙ੃ ${^;&fd0b#5HtE}^0dmBF_ɓ!"QD"6fHR=ݚ@v|*s"$ ӲU11ْQ[ "jGGiK1ٓL9\Rxc{) ,QJјWU,5ݻ WV m} !*(7,W6F+D9n,N H1.faD-@bj $=(f^Y|/T {Z+0uⲔNWŶ{ӣ{p7-{Wj!uTj{엞Kg)RR"ap(@AT߄T k)KtJǵt6!%2 J}H\i\k9P)_d(M f^NH?ZgtP2D܈Ϋ8ӝI1==Ah!t構Д,p_$][*W;:F5Mt/lg}7zߵXDJiZJd$*>FrV}1@&{WQ&[f%}?7զ08洑/zuv22-@zBNY7BP (ecrA1$/L߭5C"HDε¨h`fOA@9k83tS)J#9n{𠀴)aBd-UU-2MvϾ2r$;t?ghp~T>GGrN}jqrV a=J $y3h$2%}.k>P_;5#NC9HN]1=V]'tzY7a/@$QH[s5WDcoZ2ZEqNFhT4 Emo \"tyi4e]841-:G^YΜ)TQrģ0OIEg˸t"{γˏ|%dqq8NH @J&#iϔ~3: 2:Ѭ+ҳ(݆""oo7CO۹")%Gj' XB%p dϺwv6'.I)GGgc‰;ԃDz!M)ۙ% ힳ>W0QϽ ͎AjOtDjNկ ]b8<<8!7!GtA1wM-쬘jUDs9"9L^؇se#D~V쳐p+aATFtA0/Lw)fC .d 9ȓd!yEaDlK_Ց#P F|l0 ۍ@b lFb礫Nt02 Y+瑆 R FL3Q<եF|crOzOײG2^:zNW!VMSF| ^*a\F];#u?&`lt]$VB̃:]rlV5xbD<ѵ4:I]iELT-amfI@=${Ę{,L$>D 3 땉aٳ7Q.3ֻD/$Ψ[;!8Iehrͫz4"זLַ`؀k%fg7;62:ͪiAȻU r~_jԄa= }^gw\5ۑN=\W_VPJ LHdT6txۏע@gw˜@5 Y %*iO(φ 5\n&1+1hZkvKn/ H˳EAK%o hu1ͺA<%@@WݯDh:|;!b.bPu?3CՔ}<׃(T\Y@=,?3 dl/4t2,u)!t"#?̄ Q$Y$w iL6~_6 29*A2O0%Zki#.osL єO?sWϰAIJ2= D BIu]'*v6v:K.`etBiYu:} ?c t;܏ES(nSWG}*1QQ.>?PҧH&PxR=B[/Vz1GT{TQxq ը*S۞}U,cI\\F$gS¤<}RqKmHjq@l9+i}"Ƭߺ`} Hj~N50F{fdS܎dZ"A eIZH$nTl>5soKuu92(/ZP Lns%/y= Qm٦&6Mx(oIkbbRRu-Qײ~t}l@l>\ɤ,l( (Z%7:l6@\m[߷g+ M_&=jZvk\F1t C%h*F- u DWg- $X!Aln ̢ʡe4-n@O)vS )N5'lÑu#-|}хL,'k_;mŧfqj0X}/Ox48r=0!:yTRdo&a,{Bȶ~ lmzRLaa[frW<!nTj{KdL,s†r*7[\I%EpnX:J=eFN"~؆W|K| b&t'rw!SE@fv^uy4(>w:ffv}@EF0d?!%~tЈ\]$6S xg*~-rظ,N3F @ԸiyrTh u> :'`dQa^X;1Ti85"k4Y`(Epg?ZyU@1Y OS߯^cr hiN> `y&6y.#{QD茣 cI v+J{ŎL> u $ 鋾Hذ?)[ 콛: O[u}iH3jYfM-z?L Lo-F{ 0 f?A'V 9Gg: -.*g>b)|`7`_Z,?L=sbKX<+l2HIH!l{jrR S }G UgKө9h,a_4,A%MI*<ɴA8/ nRH\h-~W_w<Zn p jz"#P"DJ8+vX L%phDBbNqs]6/9#˺ m>aoijM`no]sFI;IOv &2NiY1b7: 4:& \gRF_"5H4eSű¸-rwИ|~v&;~Bg$_`ّk o=E2;юDQMJzB>!f|^dVޚa%a4bi^!ػLNouȃH rWц93"Js Ѝ\,Al4q ׉[c*:aY69+B;Um'ZMJ"dStXTM6s@Q>qݒV-tr3KKϨ u=:@,Us.;M\ή&J_惆6͡ڧivObb.53"8X$f V[R_]!ÁpH{sj$)FK7-(C@Wс֑k~:MM=J8@""bF$DAqӹe6XH;wEWDR=k[oDo!d+블UbH}r<ӎkD5}4WYXtdKdC,Z¤lψogUT>L7ؿn<%أ)|N֣ke{%KmBחd%ң\0W\ΚO'4pG$ HVuw璈z?:iv~ @PF~Q X,*" x"E{%kn.=?f.gI^sS'gЛԛ%`>|6/Hm1g+9/b 뉀ӈu(nð2ـd? 6*n%K򄅸2Mt(rHVnGc`׶Vj|HBCP*rmXˆ݊= M!$`;[:bS@cmW,#/;Zq.o]=Ϫ|BKI[hQp$ kKE3DNҙ*{ּ5y [ Wco"ۮ9('`m&*[P1UkgaN@ Zz{[ ~SWuws}4E1Z\K+yσujJt_ rv.+RK; kUxZxlNgk| ѳ=߲ʐCo)Jv6Y0~s@YxK˥fr[VWZ5S|:":Ŋ? xBxE$tpxYp= % ͊=X-wޖ[2c+_}2l]zYyIPܾ㷌U)`{ܺK \ +Tg2w},2wӍFuLR&d :{PIULGYuP\YrLhQDW~u-5&kNѦ($I adQ>0d3 HmG2eaf#)46W{*Os}|ŹTmpLwLe _cJ{kk|I]OIgBOz"{uNKM])@2B$?@!q6'z= G*}0#Oi`Qo-]j/G!cPMhB~w '~@ a%nm[ i2\8cmԏvu%RgGSi!f>,&"@=58e&M+mL]K9&xUE&/]HE|c8;&qgP02cYRYk႓UJ0h^Hr.9,*\8CdL&pa|-&FfN&{N@Q7x]HhٲGDKߐӌnh|s B$3Az)xD`L)xB.UPv봮UghL! B8,&âc;ЫQl-j#n;zJ*|hti&!gD/LnҡvXg&T݉H{nLg.Ј1lՈk{`GD5͵n`w|ɕY V^Iq I* cNdQn?Gͯ @aǖf^Ru'i_S#}&gdD8$3hR0rheIՎ JR'ؗswF׈rv&·P̓k X2r@B[g)3lF#ע0Ѭ! p`ܼgaCs렏hҋC#=.vHD*1K:ϗVѡ-Bd=Pv^ \qXi >8Y=$Hj)ٻ4͝l3q[N1JC~#n)vm {6( @7qUn2,4Bg*%3?Gsy%+hG疳fQR#`͹˵3 Jf(5B`z݁o$wE*j˺OP3$*Q\Uƹ]EO}0`#5H(  &,߀ .l i ۝'3Wt3* pPVT%@єu҃SO~"}?2Po И6`sf=>|5]bLl ï8%[0xed,A.5~6hM* qnpʸ>׿]C(. 3nDnϲB )?Mڐ'5xP%bw6'$SQP nx?9kO(3` @m]u df@$3$IdJxXvP$3dׁxmdiU\@LJ^9ĜBz?2@U;yj^ Atf~¾kG3q2jHD1wn'[N'eΗ(Hdów͘1p.6Z6 /3(B"j@~We:ejC|;_"5\xTx3㯇7I>heR @,+A~k|maל W :d$G23%!T  %J*% )߹a\nqF;(]y*N:sAwdьMg[rF݆b+$2Yb+7A4ԩ" !s&a 43?H\ \ʪ9xa 6n^CqWl -zՓRH `T1`=[}2d[FskxPj*ԡ۾bҽRSX\)HaQ"L?eQ gJ0|MW}JaUo3BI+'E}-ϼÆ[6Z$]6mDX`+KRI=YS7*V/zcRM;(YMSal3F SBJ팡 *b;y5'pkp;0 h@5)Lv^c e:uWtEJ`^[RbٶLgf&DBSv8<x)EGܾ"[ ^8 '[ 50?(JӳqyG5Y%DD h_"r!j ^D  4Kj췖R-O4nueZ-82ւ0( f .@Rq qU ٻpZ̨| Udk?ٿ^(gp*ҙ*ܷ-\յSa/N9:sW% om@r* D5nN8]zM]"0KM^fbÝ<ν"_+-@&`)b7d/Uo?:kNʢʄO`SHc]K|wEIACv4 QQ1F!q`yN'rYuOku`/ Z(Ճ>БQOZ.[)zbuE *G k|=$uD2F{݃uBN>CgkGGGF4qڙ`6 ijC''J}.yֳ[gu6Ζ 7 A\BTn-hIdB,<$`fNy+K#GϧiJrjV/regp}y *zq6[V@8 IpW4 Be6[R`+i[N}-guk4) `<}e8@ -3%g8G+g˂譾0kiSG`?vI~ @t&.TB,x5+9Y,B`& =kǽ{8@&pfvGMIna  hx% !+XuLXmFpriV&[V-̜| 6Z+ ;w"9s; +M4<ym .ZJ\QD@لPiHJ̓|HOIZVk ^+$ԣgk%EH-S𧌖8X4JJ(dk~Rh!yC!2.T)EiXѭ$B(FE+`U2dž*}}%}:NTB g);J%+n f\sOIz] 0=$틮rX8i Hݺۧ~,fFv Gt4.BB;ffxM0Ca52+Tmf+zwPZ0̄"ACÒ(68"o-$`VyL"%9z{<5M}'/8ϸZ}!?4'PiCrˋrpLkwGٰlcEf>9|'ݧ8' #uM8,kl8b6̸}T14oI%>UTLSlnd 25易(nd-%`MwFo1|`h+i;jm* *-Qߴ728=j;:={%/CWƂN*Z?Eᾌ0O!"߰o]0pI)j'\=phʗSapIPsy ؈]2ٺ;MFP;|~NJ(g睁HFչuxU%NYyW-Y? 56lq"bhe ej ]FQvg(ĚU tSXM&H?Oa|mjvѝ0~3mUUWrz$ϙϗt?@jOA msB0nIwMn:9$sVJM@&_@t}468爆~a:-bu+vԗg(,0T&'!uO-7:1F4_NtGDM/BqER-F#KvwaxW4X%⌎' qDl:[P)JuU8{'!ґtc9-FW~&6xsܣ6MbUR'1X \j#Y@8vsp)ꑓ)$9}7Дqa;~ڷy#b!(f[S2.$"<޷!F2}FK"ӊ&Qy)- DyeZTF+ླྀ=#ɄoqN銈vg*џ ߖu*GMl k+H ֶ3̞I(xk$oBD۱rޯ~g>D%oegUAk%[(TOotnŀ2R09:? gOHL$ lTo}{qH`~Y>`BCyӷ7S};pu8kg,(&OϦ XZ/0s[_gU3PHݗm:I=#x0ˁؿW?PP.[gC* *| ⌲ wMN7b_UfP^0O>q>z;$|Y'O|.ނ 9 _&-{a4Wԩv: vm0Tl*ksc/?[bA!SZ;eQ56nyV՘mO= `7>^ zqfɩŷJI RT.–L0m1pMTwvIx$99NLFT52 dN>ǰS83UXQ򤯸n E{h9`38_~h8bͪckfj 8T56l9xK"K**6&rtWlm {$XCT"Wt\[u|L"tg;7gD׸io>+y]wQjt&Fd[E?)\q4rlqt;FIU/^)$(Ś/|':Rw\#(M2󶓠.Lp܆y*fCat[wѵr~? s^un5D;)u jaa@'nrO =:< rњ. 88??7&GN6_@Ո;边FURwX,!("B )K* l#oOk B4ZO5Uzf{ hϝu0\ڏxRu p54U|]~\e}[NF?+= :n93ڬoևc,*ԟi\顀 ]JfLr {iLLscS_=ڧ݆p-!dd_9ynvˠXChaFEQ.1ycX(Jz }cɹִ!ө͛C攧p-vyq.WYĎU;Ia]%8[aAWD{.pgZ+~9&qU_4GF)/_g͊IeJ.fH1K -V aU,Jb`7w34PLlb)pg | kE}6>08m3L-qb`E'UzPU 0B8t->[ DELЃ|-@+<5-֚4E<"2$@[]fhrkNuфh_nSQYxDa+J2D_TS1d8T0 U"7h xX*$hS}{NaxqM|/# c)( k4юo:S-bM @-c_*F;Yи7<LyN8C =:gg{NL$BLqRF]V1 Ë0Afq;XֱD3ew_H9tr(k P2gnjdŤSֿ MV2#U.{4|3]_Pp|t*3RN^ $Xq!OCz=p> {hիӚlE>i8nBd?׺nT/[j8BGvB A@7(0^WDD[8pqB("nqUҲ/! QaZѷ&贌2#a>MUf}%bE1V&MR>/>x7+HȖ܏T36>`IY,{e1) -N'#f/Y\yLCU42K :l 0> 3"aU/1o L4GP!֠n= ]:F@q+z1#&2 ]."ƭجhNJ&Bk<;0KrQط&`g8F  ڇ?a<k9 37f 464*Z>'h-~}$2]W;K4ʱ38_O ,C|(Z%8'M?B.Uns:wj/^;[w3}|W#4ݝ(==wuKYH@WX:?"X' W(T69 7;5\Gh+n*}A567 uvB`asqBܹ@K9 Ȩ: 6+X܄eh FǶpB|_:_ZsqL4Tk>;+U#/2GV~J]f!I36pgf5ǐl4ulK,aAGbK5b|{Vvz3_"hQreɢzqg ⴚ\ặRĻIk&u..wG tP]CòR,.Eɦ,WIrN͔$cnL<$G+dcp\\iz:H% fV"{%{8IQ>_W:[AJxޤՌoo6yp<`:R|+6>ш0q=QƞXaSQeО}2q)tZwu8/ۂ_⨭%/fmZ^ ^'RFw>W (sy!z& Og.k#Z}>iY{Ԅ}ٻOFN1~v4Q7Y@8\*Ѐ飉s`VG5xOXcUp;CF7 \NU7dR9_2E_HtuT@DhCoıLv¢RP QݳdjnC3gt"-MxCW81műKXX]Q rƪHmY|D0T -?^2B,T[OYaW轜5GYFBPbm"wp+5oDl}N4776i>qD}?yg:܇^8|Rd1\`;LsG@ *opDG0|z ;t-T N1P4?׳muB|WCy)ﻆ\<)?‚z?;KW9àwuYe}Ő6E|s";Yx΂&ЪiR/$;7P+B+}Ԅ|˿/أ&EtO4d7> 1Z7QDښھbI!9af5XVGːL@O'rZ> }Q#^E7ו^[X|GSrUc&0rC x9iXub' @! l >{n+3m>ed[ s=I熡VYDǜ]9ixOL$Lz箉%Pu7ʺII6Q}P7P\g{)Ex?keDsCz$ Dk֦1 ,U-X߲T0 D KXrߡڃfaat*qXkE<Ou8`rH ULRȋg@TsF0Vݷ>Z%bP~3W`.AfBV1 mk ;('u"`he| QHPɓ`UIGqLRiQyt BӒE`}XdkXXFetD^ Qj&29]g% ؋9c`654`@/ bj\@R(*!uQ46YCmo"VQ?Ft~[WKa1S,Z (DƚvS%>Z U| _ L Mr @xm@AW8WR85˂ " +BfB*Wh["u8f_&IZ/_@fVma0O3 e@uYSE:E BѺQ g''qR) &=BmnBW%N+DMYYg3zES"[ $9't$)?(l̃ 8yX"-ԓCG/U21R\ w\*,K4y*N>E<nUy?yU#/ZI: U"ocet"ё4 ٯ+5Ƀ<ϨY^dkoQڝ"S}=ի(q&^kP/i'ϣ]9o#Q@VH@4H{ EXEFx΢ WCTrlT|XC{s?Z`p<iוl]vԙ {I`g^ut̊vwDCRZ~,贚6cC,̤yIb-`h}y3_j;[6QY&A `-qߺQpaCL`1!_DGj)8ʞ]PL޹.!A>T7,wZɡWYBE3*b,/^QXTަ-:_sr0 8~HM0^wNW&}BLdu++* "wX D0+>ԛBZr4Y\J fUUۤATw2Yp)m^@_ZFIܿq]1p/ŝ;)FW, sT*p53l \^PX2]T*V3 [@rWshg,'h Uad/йY |QB {2uQʅΎ$ !({,u)x|~E<5q5o Kh(\IL^G8\pQTAU)Ңg&@fQwk P;((`zCMTT,GL+w ޒz~CKjćhI PAo,If ; 6l_@lXGwh>=̕gi\'\Kj4֡^ ΩZ0~ZXE2WX%Zj,hfT*"hMCkU,t;c+M^_^E RAsFק*C] (Epϧ5sFyKaXREgѮ=]¤2wp 3ELbDStDj&,\gtJ 0?uVy -baJ>Q|_($WX)YGdUe׈qE䲞mƗ3&ւK/_>N l |>I ?VMt;#=llOp1}!!ISs \CZ\I0hŕ_(IX,$,R/T$Bf66)BA=ut\9$ƙzs|!3wA^:Q|`xxć@N{.O]H+-cv-GŴNy 4{=EA1r28ƐH wYXE#Q[Z}\o>%QK}qd\'U!!Ū ?sz0D$ +[F[G^K} ̒]e!-]9KwW->|7[1,cq PR6tkugT?`zD;]uάyQ(!Qt(?v9ǍzDmzX0}(gt[A{w6Pڻ[әFwD2\Baar^pkOD(w<Yoc{Z^7#TB_ }SD^#(R&Nyu ؆z6$P.:mwQg6r'UܷŚ{^W.W=:zQE%>=/~ۡd;t|~*Y҉%u\ꣁO0Ј$b{ sL!H^ m&7Pݮ,hZ*hn0ڳ* _MWlgݙb#A8#vYQ3 Uz2^~g@X2dxЋ \pkHQk͈Ȋ+#'!x1F-H?ߞC!@/-7p#e\;0{إԪp;2MŦ\>=wlUv燜x0+KSP=p=2Ǭ|@!x&T:fΟ\ZJ8T j^<~F6PSgl\ @w%ulZ`5.R.)qy݄]6g4+bY+6SqjzuD{UsX{U-c,#c*kݵx8J]BHe5| F@$+dn9L:dg }2&zq/^m }@Qt'U)A$VY'H,ͩogɚVm4t)@sԭɚg5~䧒Σ=Kw ?jz+1mUa~38B\OʒySv1bRz}k>צf$e`}Yws-(8f_6n4U)Ko>1&9+6OqBŽy\a4cw2s?v)+3& lt 8`'{ LRSzr6Riki{< Ou4@o[?D:T~0 {q'`ykCgėrVvdWyss,9J`KҀ&N As0t8BJ&57 ݕ2ɾW s7 DI5 =l]fܐ3Jyqm/fdH: פk)˱D ֑;=9 n EP `.25q%wS1[L{Bk }lYLidz#QnYɌ!vHEN N[j k@yѶOUBAz?eE+Ć; l5F`q|ttEGXΨ8ytxn/Z@}K,h5@AZ{g> tV%|LZvQch%3Q?Tj& 8Y;LG"~\49GGCHXA> (#n&tPw@l9Qo(Zgj $䊶Ghĺ~ ZC'h}%hhJD^LU|Lȵ]-ljDP\4gLg&Zp<Qt.gt|KL; x&SPQ!m %~r.E΄D4qX{_1[((L+_8Qq_CnMpH/C}ZqjU Qcj2zz%(4ҷD!3}ۆlF?>J圪0};:= j\T9# \[ /AB"kVm(`w>foDu+"dp%0Dl6х}{[([&hr)=D@R3Ν4ԭgN𧴼Ӽ2)6MM1Yh tpW(%nBSEq,?ټl\|RWՄx]SZu#]>BV8@Qt`Pj]j%tԵO^[3%C%e7@zq, d,gn GLވ||˴L=!vYttb!/\ Z )%>EQTF1^V (b=)1%U⣲4.}ԊT9(`wdDQ%D,Jqa8R9w~0U=*I 2T2Z5m~La }>C3iJl!v.SB{xWe$ėwjOٻ9+KstNF);"2Ԧ'($&'NRmEPcp#mD~!XQddv}ښ<aꀘ2y>ЋFaU%c> YwuKvLRI,sqA4%S&YpGe4bb]_8++ }"NEVzq=ʈ`Oi|p^,Jyrdu^E0%JNΦp3Hժvmb2yLy \nYW q21 -d2?tY-ھÙ!}L]*MI[0U .(#6&S_j~}x`' k_6,@d +{%6.&&'$`Fd`1~5<Cݘ1EuYGb?o%ͥhIb 摿cdBd|\ #^Va91dHً{<Ҿ\d{'G `-A!0@eNݐ?_&3!)kl'A%ʺx5dA:w5[xyb6|8Ƨ@ڸ*;|N{&f~^0b[ %׋|7eITsZ:XB)KHBA =eCdaՇ7`MNwW,27W.Zy}"Ѱ`hi4D23/;{ԒC=^5IlDo1۝seڎ JPKS ΰ=J$YLhH֌W4%4t]!ѥ W|U0Vv-8pdf6dMK@ùF쿀Ey*Dpm 4D ƽbw@?ʯp(>(f|AO ޿BLC}m=k=w#b" ((Lb睊qXA.|(y3w`;x"iwg :nvdI9!tOBP`sSЏ2>A68ݙL+i=s X~ڴQ\[x؂wfK]81.+d&VlVGzd̄ENsjGM JF8-Vo:3Н̧zG m,k8z;3f׋lPVfKBi>U&d z9rx [yM6v~@F4AO$+\feXb4U=So2XM\4،("zL'N)\mb<+.`OrzͳMXGL|{AG_L zq9EYCѳNٙ«5izlg\TFLWNF%kWDζQ`3"AH~DЉzXfIwjdÚ`-|[H)8B~uʬhIr=,hO[${ ;H+ ]| j6j.s]S?qtY˥XEMGPx0 gǒDN[Hv:"q[/l~$a2E~zP_*OG\ͭ< _z1;%&+Nܖ$[|u5-yV=[1q o4(%qIlhZh^NcRd3XIRVix<ဎ Î tLYp sISCD;w9bTNЇ01mFuqi>|81%4l%{%|[kuR,J9G*Y޳&]:gIbX߶)^DyMnr8]~i'`uk˵&q V?&vxC7ǢEa8zZLDSgD߶BMT[0BVnh0>\+# #Tw=_.Wȶ 'gQqyC \JNnvqZ5/%OHߩw֨@6o(ip}1 %}RvlyK>YҭMQOC39,b[+dAe*yw)/C7wxi/jlYN{F@F3p$OSKrx`$En#0M?N.tNR). MC$~Sah6ՊKEtrЖr&EgZ4^X3r57JAtbuBi;c=7fėtbS⥘&rTl(~Ժ "†p^l n5Y\~n/u^E؈{ZEfxxjSKκHj=:jom$lk2ˊ:f*-FhI =q)$wjG ٿpYPCu+}_Q- L֊Uv' EQR_ pA  T\*ӌRLf w6WM&Lr$ cɘ* kl9OGy37y);D;z5󦭂#) < hە<*ܪ5ޘ+2?[ӺISŷlV!T 4o<-|0,`G"-b`=h^.vcQ?3o&0-uAEȷlWpNlwԜ(;wxM!VW \}E*TKMM X)D%Zг!X5{Bz,&!p^cM7/VWXK} -TZNzGڼ_}KytlC1Ύ章dIJ{ѹрq<w^>*wZJR é `JX~UV#Qh=di{^B^ δ~U*9mIE 2'$GV9t MeKK'&6$M9=Nt2@)Y{䒟qE1.N;P@Vj^)G|$8Ut[Syegb}_%f J)mկ=%U_z `ζ|#X.D0ӛ[1+ꋸ0OÕ+;DĂ- ӊ; bCh1@Bn$tKM: 3KU!C n\_K8*H#尚@~ޟyb]\bsrUIۨ,W # ;h[,vR7-)HBRN\C.wg|82^\'\U;9jHInJY1~N:o(xx]+z$zGHZ!OMT^ \F3v#0L=roZ="LX(u\Q@"y◄|5=Zrah*{NaA33%`>QdIs@CC0Ww gP+`rҰBA5߷ó|]t  w/hbO(!q^F<<4WttM>h_/A}{Ͷm[0b@anG ]3a)~T"-MK5ׁh^rB6B?}C`o"1ڗi(5x/kzHYz}(0- d4:Y:nR`50Xzsu!<;y\u!r`* dQt2=;/Cۦ!$hH? (8J3$2?S?NST_9:PsT&*Q?]vˬD k)֭ (N/pUV!N댑%@0>esԖUtȯ5}CyߨPň.d[7+΄(轲mURR/3G|/nHF(_ @ٔZ/U\ma/:&xNVipY[ f'劤=) 0CpћJUK5LjR_P<=#Ջ|9ٚ  ub sjrrkˆCE FO`-Zt%@E/[9}Z8mZwlߝ2 o.8y FJ5R4aGZk>X`M\>٠&pcx{|Nu@tsE<{k,4,~;hUI~\4 /WE^E3X16S3b!1?aF>Bl(H7vEU Jh]_Bb/eyÊg`)j.;oJwl=;N8EH58k:gt5ۃ| c nϊ8A aMiOSIEF3ӹI3:ˉ5AmK/p < ")oxCSų1v +;G 1+bC{.)xV's,i-ӾGxk|Lz!*ɶ8HPf& rЩ XaD-ѕ8-r 7W;wΤOY,[P/[?IWl/X83`cK|V)_K*MM%91 2bldnCֻ2)0أe>}2ݞ#BNv?:-L0Jl0]8ExmãUou&!=wL|ViGRᾂ"E lҥ3Cq#O< zT ʺ5P\ ػ\_Bl z]K7q!n#VdKm׊f uL6I/ylf)u) W 8I /a` s#ߓG6p}\|E&$RŤz)&T4[+:.e>s)3cu%“3mX+YZa@]4 v!e3,딢++hqqaWO`?p{me:L@6m`,e_I 񕒋| D9ͮʳ lX28p=3sDśրScT^{Q/vJXO|uX8KVe`ez @Cu:{Bj~'1:Ȁm#^m Y@ZsEoVuceC~99_!;܉QP[H ._P muj(4}]94(ضMQ0|.jt|J*ƣja WKI M\8RlB%,gY/w}'iU[=(EY@0uc!s794\S0;S;Y :*H5"6E(q/a5}jYws+D8((ph"ujPHDg'xcڌJAfa_ZYp.'j&+)lO1%'P?~=+Z^אS'Kw-(eʹU٨8~+mqoDN<ڦqtlhDb;d~!4a8(ʑ8rh>WlMپih+E#M' Peo+4ɅU_> *CY#@!|PK/#-4[TI +}eK@l1Ю:y15g0%xWtoۢP$G3jhmk)GwBIL S@󜀸wghxyg/6&l& '޷-ۻVvG6K+G6wte Ps],㨘+fj U_'brQ՝kD(,IY|hua=cMLg_T}{|Et'rH[dʷhЁ6kslْ>k(fWͽ4 *e+~=n~eE&CC 1 8}RB@0ѱﱻѱ0ǸmP0&+D[̏8,i)?_vM_7B,6J :XBpWWko'HUUʸlT2 UB^;gψ ה?M 򱦪 =6wF$뀑1B @cR<؊zJfNP[ٿj?Đh ;!+Ĵ/~٥/7lnG[fiICi$q^JЭUHZ[ tx3`c4M9B]/'Fn x.I]X| %k rbj4?_8Ws%+N? R']uƮhl^7NTHuc QgkYʜQ}fw$$N( ^4kl"R"B S{RX@Dl 1=ZL4sW i'*wOfMuks6.{W+3eTڇdD\pFl?xE eNծF}vʚ^ ۚP V}EhE "$ ]|$J>X(|l%jE:(6lkS'mS-+bmBEI~iD8lG'ȧEўkJց>E>t$/ʽ%A"WDۨN'\5l}C׺WLd_h_@wlk[8ZB,58K %ԯWQFkB _O1]H<R%O,π U}S9{ 8]0%5c &%VQ pjC}ƁT^o`ox 2 ž0>a5539;Ma3MTT*QDP^{x < Nl^DjwMEDչ#|\Jt@ָ+QVS*-3, 2wC-8Lhǻb\q;:SAOv9>,*)__mgv"ZNpE :g P!Tl2O FSgW(UT!Ƨ>ߍOiqB?/搶2`=1 KW$&I%?D!7@\U-Dl׾:-s<&_H{'Wʂӷ3pH"JR'@AeZ8eݭ! iQ>i&#l,7{eD(?X0qݸ8m-y~}@A;qk33<ѩ\%) O ZH;ƨʘp_~]7,d(@IR2j0";^CZL_Mȁ|", {Zd_K(~qҜ;˨AyP\A7֭6kViH&(YE݀l_;9gY5Ŧm 2HˉJ AN4+J!e^XYn&'p\>+ѾtA3gX5 ƲTȀw+%oNt[R绞ᦥ@G5& XN̤Gi <@– ʛV/ Ll ~ޛJd./Y4MJl:!]{Sh;8$zַ:Wɚ}1SϊOs@0̞=b?zmh\ϨD( `"PR{g ()"=(sb˦Y0w5kn9=B4 ŠSTթQ"`*/tm.B֩쯉Ek&XTҁQtJppL^П h1PЊPeE;X2:RQZΦ^h;B{U ]oLXT4WUϷ1CV<g 'HG<sfsl!$҈<{r'-b.J5;+r\^V~FEgj^ =ɬyu2JlKK @w%eZ& Sh%G˦ 5?/@?`ƼmVk"^O1Fpf6 ;bA[&/0YͺnS$yb9Wbh8ZǴZ BW{R|{"ԉ()3߲4Hg]Ā´gnג.6PĮ EIxNkw:M3/.Vt@p&ҕh y{xe=r6LPԚ_n`W8kj<)G|<ǯWpy3<"'?7U] 1GM-%b*|EC_$ :/tQwIDm}_n9?-C?d~!P0+aR6gERbl%RD.;~"X|_fu-Ϲ? KQpTPAUӐl޼G ӵM>ݕn{cË/u)d:] 7eT8UplWbjTo$ 25PU#nɩhx C cwz"WMW+]qYڙ(tf*F4@\;*C*N &)sCsL @] ~`ZQ7 Q@ZR0ag%a;0Aaj3vz$B=gur8wWRRAi){e_ w=_BqӞ2Va ȓu:Bw0ց`󏔆e$cplwtJ]Gnr% yD m\gDvʳpyVLN-Jh`4?QAǤT6Ikye#mx4!ÿm HLS៙6ojƠUݙiL[XX؛7 ~>cyN D@iw҂5\@Ǟ%*Ynj6:ۄ^-ou/]|zȤcܢلqM،9ɰ}1 j&Z-㚕!Ny#SBgD Bo t4֨:YGbD 1̢jߺ ߢ5RYJ{[3]aݗs j T{쓝8~Ƃ6hB#m FLkO" U뇈;m0ᖆa @aoڋPq=@z5G^j"Ե'͐B>XuMtŔVn};(iP+5Ma(Jf(G@Q}CExA ^4?EM߇j~F!\"xjyLkIPҟP#FT@t2ĢoZofzFVp]Y}Ej$s:*>1H5wi2N3'?d;x( ıq$Ի4SQi"T#u{J ؿY[nX(ީŋ𪆬x.+:ŭixΩګ&6R4%6eAsPϝQ _ó-|u:ec5u-Ȏf,9oIFJ/vƿ[NkS,cyDu8B&MAnQ4:BT^u@_2;q;;-TWP3l U`jSKy>{OJpojmMfN\biz  ljaҟ&{3q·nUf >5cOSɬH ]N#7ғ<%)Dұex24D:8ԙse f n:F-gBO0ofxc [Ouj'YL"̸d}_fe "Ƣx\IR'W a.k Ŀ.~sAxؾNubzHn(J)vxT8nBvz_ :I{I+YQ/֡܉7AUzl.Ȃ}e\:~XHowRr *>`Uݓ<SM?US̖>&x ސ^ R17ԍ_M*BD`t!eGR kň0܍3L{?0:P+MAҝj۰jZ@Ar0}*U9ji H7Ӑ:!-TQ]/oaF֌IզD=  iLe6eִGH /Iև~csU :)(qLW7.j@ 8Q}_v(Ef;fGҥ߭9AbPFu!OMMAI0):.1`頌!g)[eΒѦ@НQAȕlQnu.5b;lKcA-aVM]<,O !" RSMivؒCH] bj? Mn @=x^{)?[%`/f޿gw8rB Cr(A/}/iՆ|"biS1R -.hul &JhU ]H6}2/&rw{A1%[8-]^2Uh^'Ϧh[p~YUϮd7| J.z&lD IsJU%@"gQ W _DF `ѥ\ǫ0BsafaUXОǪeVPUb\{y$;o< 8D6}pj#^Iv}K#BajD7˲!O4 $ϒn1TSBb6'5胯ͯrl;LHihATR ՚!ĩTËR`q>։s…sңݷD8) Cd@~gIȠC?w6!w0w>LV}od5| G9 39R{ Y}fe -$zM,A_):w!L.vfdτ{Qˊsu݊i(4<@4^lC E4oa2|/!uHm}C &Dı\f9cnпqޟ6M}I)ofF?ƪqoe}m}&@Qcs+!k\Z+@C&s"a) N#~IÂݨpml!) !v*A8Tg-xez"oYg(dOAPnrtEpE@4mb&Wtjb;M'׸B%|=~h JQvGy^6,\ݓJV-2_;R5nM.FEbi کJg;09>rߗƭ8h_ht~g1HsBUۜħvEaipЄiKuMǷ~aK,U?ЕCa[L (/'BVUFHo愳FQ\b%kT%vX :%v9h;nC֪'m7q*J_~ڀ0㵻Nƿ(}m'|oG L HSHiȆ/x\S V.;#J$)ɅTe#q)f`)pWqC| Jb(ѭֻ }%' GӴ1Ww pi+ G'\ nVdbA9f~Eo{!p&.hHvnb?{Ydy Qw1-rU'ЭUi$^(Jd dէmєir$ãHg:`sICk7beX 8wܭ9R2ӟՕkU9 FubK3 3MvW WygDݱ:Eʆt𡂸63D{`M .bxg$6|&B'^o 9:1}̝E>r$, \"B֞>۔+vE VSw&@lb]\G [>|5 u2kϭg SEu@p?c҄yľ ?@r:A;X̅Oԥ7C?履NdK  ,1F*nf?{9Z 䤤DOwP|~Zh+{5Em*.R>iN9]ݐapɮ%\:T`<V 5\2C7" [„qv+v#Z/?V (E6[S&*R6_]bu4.in N*K/`u#Gaf\n=U?Ql{53RrГ5:BmEg깢HpOψmrF*β:R D[MnV7FxYdrAb~O)Dz'F[ EktJ|!]🈇LQ:b&@a8kv.#}Ua"ZYL@."ps W,_Ÿ?ԫ5M#sa˦-SO@#$$1ϴ&ZJ "Hk @FP/`TkD|^eB}^`ǃ(1V!/`*O=gЕ LŎ 8]L*%_HlLr)-u2z4Pw|OHYP6{TVOF=-,]`uɸvSgd dWMu}@hT~6dl ,bED}~dHKL{|QqG-/!EE~!Xż="{fDMv[H@3&;$§ȣ)CRْכ 4`3D|uE͢MCM7>pN)("-2 ,=REjkKgg<K+3 JzzUЉDèxT*q̎ʛJ=iR3KK=$*BN9C9-{^](z^Oy(< G&<xH 9,qA:\wTPj xB3S/rX_1e+ZRU|$}>#aY/ |edol4|3MdXrfVQ*jКb'{E3;Re%~;X/4έRe}> UH; sC/:N3 '`3M{'"~4я 9%$+H /)Hi!l'"jay[I`r$Ln=k-&S/2>5sCxtV@o]Z GK ^y0eM2tV_MRrD_Npe ]RRZ1otTɜrgmAfR= tkɹ~nlxXIv.Ӊ~B!U1&iǜtW{Tm %QM='( 烺Ul`pq2}x̄N5{ɀHvj2ݷ) 024Ȣ笌U1yR^)"X`mAHV]Pk%-љX͌F__ VM_+0-'J7K:Mi6/|b'{őv1 a*E [ r:)j+4H)+-*D%G@0X{]=#A zxbCq)=OD՞j,6o"}eU[{+"mRbakҚUycG'&w@7WHL,4*50N\G@.xgE GԿҢ+` +2q/Dw*oefO\#郮n=SMP |aZ)}凼d:wxv4}uU^mO`=/ V!/aFN[ [%)AǼJF&z*4;X  "VL/4z_#0HրJGȖbr&fEM@ј+pև߳l}]WQi2,RED$}h!u4[Q?uP3I7>&Xl*q‰w0([=f(\BfߌYۉ&Zq\`AI*9G^ěB,K%I7,2e ԌOm1 dp$$`.Ht(gr ?wHj ^<{w4WZkJY+~ ;߇/5CL}.,nz^S%xiַ: VO̽GJ,E('t<60(@덳1}Jo"N%7FQ;dUS.Wc`EG")Z҅_m;-] >^|%w=/-!]vH4 wWjo8*Lphl6\JwAL9-}MYF*V֢Qv,;TFCJЖ꼺AJ=7Lk6K7J1z,-I-:> 5JJFM5خc")pdDLК.vG-E,`@/1C>C`f=uZ;ozx]:BDt6 Yyξ:]ڲ҂7)]iSB3CPڹ @2 T*#B k$aV(>f#nd#li0m#Z摀—Y L6^ZV^^f_4#9N?-_ZQ!WZNN}I;rt{DPS_[\k㰯#֒At^~ZRw[W "<4 !~6zCuޗMHk"rg3H/) YU)@ ,RJͅraܜ 9 cV9zk4"4Xgn )\"#T ATL |0&ta ܒGBL;?~oo?=:C#ݙ"T}lzo>F!$l ̼+Qbɖ"jI(<]}I[7z-RTFyI拓i?gI.MDyq@XaXʹ$0 E9m =*] }^;0H4xLq[=Md皋{?0=x>f" c+߂Wm)c zQCg'y2va;MN $'Q%%=z_9Si2!'q8mN]ip(\MO]dFȇ\߮ &o?]D9B=U)(?Z8q-$׷$w_s3qI-x 4LF})To \~C?6EcPDH(΢))ҍ*mHeR̄L.2_ Va~pM_S fٛf= HQ_u0QWU-C4vB l.7gCJ' _qes.GL[jvEke$&3Ǹp-&l!3XM/SNz}0oϼDdp6 GI [xm5mkbkGsRSK roAMWeooㆾ~ {*a "MASi%@c(dۓ'݆cqa)!.8WB@Z0Э؟  }1h#0b`QiZDo8Ne.%*{eAfm(s5~DPJvtfZ2zoQЁ˦mfߔPz- fܼO@3@TTO`l~~C+/4'j)Yo/WYȴKW6`eћs} 7 gs!AX2~4ihW1&ݗ]i{z8@4#[ʊgvOЫK<쥋ٿ3Y2҇]j &}Qu.Cyus#҄&Q=u;tߺ־86.陦~z#u*͍b7&|@\Z@A}lS$b%jV_gx(D0 1˚nfJٯ LÿkhJrkM8um { H9V()4}bY(rHb,SutZ]/X02Ի 09:N{t J[ؐ^w8TvᖲF Rbcyqp3 J@n$Linӄ<-ac"/^K2Ʒ\F;q>`Tۀ !}!R֤A5xϞpxs?}KcČgf1 @b: %vrpF&{@Z-S]wGbop^gcr &f]~5If`~mE&pݐqX?7mFVpwEt5 b9;`)H+ͮ4}*ZX#ZGTi1󸙕(O̻VImC3b!1v@=iv}e̖~ #<Zx譣筃Tzi3SpDL79I-AҰUZk2Le;Dk#9JĊh Pezẗpoe$Cv ԙL4tΞj`De70z@ V'4oӁ#JQY/PZrRA;b{$Xz?mjyq^g6 Qb3`>IlsϞE "m*V'@iS?gBL/_[MfJ*&tdMZ=s\wJ35xkSw=wOžl oó~TI_ +V:ӈi-ŕҕtөMU/UI!=OS*ftGh ۖFd vq`X÷BY8?똨ܩzVnAI}TnӊnDJZ',3q%_dE[V u3x1rSkeh|Ц~K*$5/+mM|jwEԎ 7.@ľF%-^K5 e_L E yT"U$^jkw >ykH&1 \+F%Y(6;EÜ+ʲ ˷: D^3L&"'vAaԷnbii~`}W̳]=[]G຅iby+ <amDEbzbיFaaQ*EЯÍu'<򨶧1D\LKP(뽴9?lb`cuA+ֻXTKȈ+CfѸ!kl @0u)ABXT%Q Uc]Sr@cViIi& u~u0I["i[e*Q;@8&ܲTyԘ A (=1Kqq.#Q|b?vs9zֲ(STD嬋T._@I0v#_QR) = _Tp> V~v?(hqx k[ؽ"v! CN̊4/?&l{:G-G7d4okh+tٮqyI6 zefЫٛ< G:sLxrrܮR/V<7.Ģr'0m/2uL}=n9e]zN7 _Fq]pMV`2.q5֔+|$gsa& Kw!~"FC@jSanAqBqg\vƞUDi],ce!JCPj]O&OıS&Ƿg*09ǰ6h1 %(EQwڒo6NNM #zGa]?#x؀oVLfv~WPn]Ƌ&犢9YA-7|] nG&%9ew%$1[?xo 4L,]%#(ejkpQ**0Yz Oa6Y=hr=/(’w ªfc8lZduMi5sX UpV83^=0 gfOSP$Yt۲Afdžn^&W$ENFɋB7%ٜ)\{F4vŠ'QX[4]آ:`}d{4ݵyY({gyZ} -WAD+ '%3dK3?N=N."@+ `[7Ad#w0 2DbZo|PS~h(i0N5ۚA+%cߺ GDZQ-1vq xOŽTo3H.ps2/MdL\u#٢0дUg^hoQlAvravUeտ"(et4l[EFɂmD޻@w#gZ@faOk߼\uA=Ss6X٘++_F3xh\ $=dw[@# u^Mi`B:pF>cӔusXGJXD`oIGD 8rPbňk_|u>:X|GsFzkJc[Ss;CgO8 @H/dwk7$oԲ}]֚tC& ,l[֖i<܀ fD&tu\Lt,@YlL 9$blundq(NH1@˄l$S åf tW^E954af|ywg.R{W,/(KDI_ճffCliF[UY 6`!.M̑ZR\XCqA4ʇtz0|%J, LF7L~hUfHF3v Xu7iRW<7$NӔLY A<"$SdRU Ĕ܌LDfu=l>"v.W(~0ܸ%7XPPg}Yg>TWSh܃ ݔ!2݇K=nr` ͜ P"[N1C4ROs<=mDḢH#Ys'*B4|=QYӣ ńeg_Bo8<84c\"~&0:@4rsA4sP}.:}#rpjOL"u^_%9m7PLCrFCTU7XHjx YDʾu"vNem!Ch]tt @1TqH(\/oΖ:Dɗ  \~IӒGX2Q"&}eŦG\p*8smbgL/RyNvBd6nIFg[D8C,F7b0Yjw}(lԾw\Q[qCɂz^$<%F}eTy^l|R =k %uik͛Q(,M5wԱ1mḾ3䣅0y 9H:< -Ods/IX9:$/ >>zWaFHJ$⊍(DZ\շ%'EG`vGi@'4׋NE&^=޲"Lڛp3m"lx.YQ1]w wkUF"~tPPQU.A&DpBnjB[ kI`fk!՚0> {𷻇3շ-8]d}7$9LF K+^n٫9LB wʢ] Һs:(ۦR$ȫ`zH=HW44 L0 4K朞=hl6KK ‘s=HZ NB߯gt} =[r{ygpB[Ēco8bŨ {dy&S9c$hε M+moͫuw!( 3~Y ٩^YDV\)]<6ݏe8 *QD?H%ڃ d ?8ͨ3?[EI@UaI$N=)^wXWbB{vH?A0gXzw#"x*f qb@}m ̀Yd93]&szpn.ĿzP䏞!)~b J".H68WypWM@2 歉b 8oe^F9 yeJf^Zf'2l7 -RjsOg {CsձtXq^]0\`_zkK @mE;M;Ta | AmgwN8!~gV\\Ʒ]H[5z/ĥo[ E[o97@USF^tA%!0ſ[?a θ@d6- o/BJRH^$Bsޙ";uh(iOE;&hLtIl5'7Hmb$&n:̌|Şg>̆ه =ڗ NK6Q:60:-{Zm.PlTѰCu,d҄UT@6HGskߡ (5ދȤ~ &V3~:=G0`3jc;`:F,v-.nQ3CYuB% dZE::";OlFGue7r[v}ѷ(Ihr+YG9P eHo%a4.7 Si $CO`!(`uIR[FUL{!HX(ɗ&]IN7Kp5, F9PxFOËkfےDu=ۘjLD9_*>]{VNAgHDGNgIL}r32Ls(8y6/6Ȃ | yw!%):AdgPb{F>w- IV?1Sy:2xY_ǂWU}"W@I|n{"܈س5 }#.jz2=T%D=M,ȏŽwLˀ(Mk~j߶M}z_S< ƾ5fLv3gHCro<>4GtRF%ɳ:]F?b ףK"n s"Q$8ԃ+3[7<|F0TfdڧKm(R3 )=o71Ixޥ`A3hFzf" `һT D*56BYH[ga$19q{cTQI7Yʏ5c XlA0jWq 2&axnL5XzzOsuS4)n6o=c\ h(:Cv I3@:$ʋ ʅO'~_m ǎ uI;}겐Fdn̹6u.Ppve=YEPVXk)V'Ñ_6=]IR{ ´Nx(pϛ] cu7JJ&_C>Jtu=QzGw3|G ԥ^<*DDu𾠈ypvҰywq`3:mEHT ǎ 4wѯVa dj{@С lljYHv 6H‘) Mgeh<sca¡+M_1$+eš`7CWUwaq㯬_$]D(  .1J!)d,m%,U2M, Lr 5:87_\? +8 F:"{=szQs=w::C4a,D}|:NŪ3h:`(d7ĮylwpEK%*N}r^a{LkdN F+SG!"3~(Eqۧl@׏b+F/DH$:K\4S8W,n G-}`IU|=@ɗ7vV 40~|fB?]d[D6! ѻօJ9d$ʚ$97$nheKi46?$G8owMA5;ְt^ȗ"F*TUf+@2 ,R{[⥡+vKf-^!0V$g~$l >FDB;$U5])cq:jq&_t[rVQ6YIݹ3RBpg zkg<_7sd\'2q6kJs\݅ ,es2$,vʼ;|G=JN]OY/ B9Qfhi CIP j]5AXV~@R\5{CUe!t|$uhQZ{'JiϷ"|'qї"j(6kl^XydM[ >ǫqWDE{r]8r8ᴬvyأpiEwhL***w[5,GPٕET\_՝tTNNN58}_r n<H1[Hm+UŭB^Kv:2 ?&݄sCS= DiH] 2t>ZNM&ZE늮n̪̎A:}c{Z+яKIyG,`T |{hٯ]cۓQNQݖZ?Z@w#k( 5YWlv@v&g] i Q ]ltIL!ٷAbQ43W 1X.ݑ,gpJF@ʭ.DA+ U<+45G36(Xr{k#|lA {җdR:SBMD}Oу8GHi 3׮琕ԍ/o/0_`Y~ܳ @pI5޴aC @Kǽ@Fd[5#iH8ߏwޖ^0㾐ӟs0'rmwW3{{KI W"O("vd8w9@|/T0c49Bo-*ĸ ;e:EXb U VJ@zUs%]ͳ]ó ,g6nR5IН7H2E:} )[y=̚*l=%K.G5Ejϗ\cw xz{:&KD_PyTjDYOY OZ9`-s㻌x24dJ-N>3#&36%Nͳ`JJ怺B)Ga7>kӈe},{$K\Qě`ه$injz L<-Ȓ1|Y|u4=+5rah]Fx`+x]*AԌ *Zy2m`kop߿J%}5b¸Ѵuw˙,m1/n܂Ǣt9p]3ʜ&Wz^nI򐵦cCƛ93mg˟|~8Ȑxa&t%x<+QF+T'\O]2gBֱ_B=6jEŞ6B| 70=ȩ;y,.ل<EHG♽M42ͩEfy(|D jP`P O7_쥳ܪ(:_$^ՕWSoI8ٽ҃h lN|Y_izcD8o]BgQzT  d3-{jXZBL=٬Q#k*ϊ>JבmBx !W—/J^ \S}C n˰j&=B AԪJ~u k]wmѹA~Ρ?ˈ3r-B|"u(feD^t*In3!.}!{:*ĿflӉgne^2k)J◪:XnF~OOetN"-twh΁k#<} /wYSh$(Zj&}\#Qy >m>oE V GD.3nDb9 !9?)d:E;/RohQ9eB0 s7z!<\x"tfeLtTϣx|WDdC- JH8%CTUBfn Nm6@O~X?Sphvx?mtf/2X x BiՌFb?g%6#"3*7!\w{7.l}/B6b |g,w+c^g#^t $.dwԫj(5`ul5 yj=td`I:$ne.ho), .) )6j)eFYDSaApOQ$tk}GqM^kd BA߇:q0Ş $+IzA%=x>Y~TqþiB/z:S}ye:0N1)K1s,2Q_j,$3J.f"$S>=2Lk󈏎e!V{KfB(&HPF$e[ZDT_&V9^ˎ׿.BA,Y׬8ʥY೨OIGS3n6~scƻVkq֠qk+v\Ƨ腺qsSRr1VlHtkDZ{A(~¥DO47ؿsK77s]  @tWj \[bsaZ+x@=sܺG]-kHufǶf0Th^N&!H{d3ݶ_lWZbLD iuUQgZx=:bMݷ`!oG?$,,&"fx,:>)p2޴9v|:/j In 5@@OUzS4n= F?V+myRޗy:uQڑsטʪ)RsU Q߯UL 04(,s'𧝶XFDFߢN%+[d]BiYf_thn@dzHV!{lmAS֢Qv˓KSmdRVTfsY%UP!odIIn>˦B|^JY%]%L,$2|(&o1MTo k ᬋ /=$0ʖg-{OM`_ȧ Jo JΜg :?!JΒ>qWoae_qDs㺼F*~fų_ \k }ӒFYTgr 2I̅]o0'*@5 0_`aW#<UOUySgs䄟 ٹcG5BS\` CJ쿬SnjB^Ul]6BY2Əwz:!@* K2Һ!ݫQU4x[8EۃKpHNWcPȥpk!`Ŋ^ !">;7r:Rwz=~ Npd2eRUtplehf>(ÐV 록3!d/kO M/ze4{,XoZC9p CթknHk:1x[&ɏr|l\ @L"ΖL&yQTM,-hA/$()myZhtL.VZ.Ԫb[OHY̍'bߟքNϹNY0<>܎~& ih SMX_򢎌) \ad4eQĦUbݧݢ$`e7QLRƈH6 $Ͳ wCMm'ǒor8 (Ùe*ֲ_Bk*:펷j3̩_&9oȊAI_@(r.uosp'fbdHjq>(tE¤y}4>cdžQ]@v[p:?(L{{%2/WiaWJIgUut[QG/KRŸY%zDYEC4.bJh!eWR8)efӨ˫}]ze}?dSo LF9ҟ;/cXK ɕ7pWT-k"l6Ti6>W&`aܞi6pQ9o`L2OGx4frCࣸ+'v1@(\[@Oˊ+ Gtk|<0xƜjIi|UQ3j̯F+@b$+ ٵ| |%'K֣BH8:2mB{_G2F%@2c㙯 yE]h9O80ZDqHB0&Ma t.`JYijR&kX /tAg'qW _10MGKQ棙ƾȪ.o)l_>ݓRر$bv+,J Eunj05&!L+/9tZObdN؉o-_?m9)#/HdDhCnCekRyDfRR4̙`m{WU(td1\{ }\ŏZeDJK_e1,)/(y9^X>YO'Qä;k<"O؁ y(XYG؎dk1#?vy{}6Sd;[$)e a|\BcCW>={tNzNYr^N\u1zlN$9-iݓbwv#8;0NBJhu5xIoÂjz%k73CT(m K+/y||wxd?!prM6;w99ኜNm%S -q ҟnB!qO??R!H m㶖Kjp/K7}Pn3CgaCB>)rh <a}#gh? F]\+i;8Üέ•n^/O zGTVwyjI'BWdyǘt%NKQJ GGfURDٝ^9]|gMy;#?[{ሃ,Wi|V&!Я@VT! \5h- 'gThpr”ߐjsp6a\ 'SgqVBp>1Q*fḘ]J%<|SHm){FJu7 .mKq1QٵEue? , _zF5xx\G0 R{0B}jJu0-w=P;MڱE3 5w>xPǏ.&5PIEaN9+6A0/`-m҈rlPY~fr؃e t"7єXTmzv/ r A?z [wZGH1`RdmևnBuѴUSG#c]%>iJw [wsڦq)|Ce LMMݞ m4;c:]V %܍ + 9'VY{Y{ u;chbD-A2+qCB"z)vu:9Z eJvi>)&=2^1|J.܊m"Fx`o`-m hN&Qr֜Dn|2p؁q3st#<.'spkI0\ gdBE3ɷEaiVml۲w1Xzo4^~cwގY"ϳJ 76H% \!ZAs&Q4)_fJ.Fsq( }{,⊓\ŎY>#)7poYt~Exk*kTpSdA%fHJ8 G6j*C p`&8pùF`X> ,;vl"f!5fro6ux/)WLFo@ fZ{&|lBd8ŭD kg#5< DDaK =S>i|ĵgO\|pn_HC Hi/eI_2ep^ K @/ ZħVL>T#`V +ñDҺQB$Cs ne&̊^vekH.֪h՗av]xtA x%d&]g6Q#Ó%Ѿbc6m 3Jh j^l(c92Xg%bhOF_n8:ᕩXWEo-Zh<"pd9V<[LC n{lKA`/Ί/ U!XnoFI: mtIl6yVRG9Ԧj,TԂaKeeق!}[nML+Rw{AX12,B7Bs+a /״&;X*bCK3f}vQ+_M4~-7R碛b?cN#4.]+K?nRAK Dì\S方p0squfe# b#R@Fysaw^L~fDPj6J[4%F^\Hl3#oLg;o&3z H,zShn@W@le}sD J[Yk4ӏ ${d4>' cACPe>~õYR5fWE9ߌU3Rf7 /`1n$CHQܺ ZFLlڂsݺh ŬF(s\uEHL@xdpxe#jHmPϴSdBf6b=A$U0A)֍[זDG ;g%:(: O!E80BqܜA5~K%!t"e}Љfk]RNZ&g⍝9 1 >n~-Rj&`ݠUWb<:7$nl  (vEhA™d(i u pBMkYbpɑ"r"=0lߥ!/' ^WQ :}]HGb"06;qW*!R#Q/Tb5Oǁ>@CtYb9ۜgo<5vrs?_n0f=&td)$mUv͚n/JG!^x.C6OfxqT ^P*]!3@\] N=0 @t"IlYpDXm8 g$P6ۜ sWD?Iԧ[ZAHGfge+D4뎜ix@^Auy|*\y<\h$XtوU~ *?Z]?#;VGK{& `}ElNIɜ{ }0:T6k$ˠ Yj^Ҭn-/2Xe֍KQ?lm(v.mVH"@^C媊`P'Kdg2v]~4*jHDQgΡ4]i@{/(|$ ^Z?-I@62I_&wIt Gm@z+aY!j HZ_,api t9w`x=tq7xh[P2~Se STȢSN7))hu֖8QbNܘV-k5_>ѦVbv^EF/dʓ n@6EEΚ~2n0 ;fkaG;%M)Qt:hpl 乒ԹtU*U-JzJ^쩸:]6G{Xnz.EMsZ49[ui)-Y&d$dy}yl:T&ؑ+xL7c:X«fk:TBg?4n'hEGy+$-!FTǒ5eg,u%y"z9(9!ٴ$L-14U*>kA5XL|Co Aȏ KS ʈ,C5_Z4o)@%va`3sB0Rc#,^Ft"+ӤξbR|4-U"*7k7K[!4&-ӽ@ׅMv!j2!hoGR$6ݗdԃX@}Y*3Ua  KO1H$01T&|@ Vfp-Peky")?q7i$,H6.ܽHmHb ĬH[yHzdl1n,^źәue t!ˁNaрq[if!!Ff~Vh!(nLJjS(wiBE1̟ևQ G\Q]^߿a=8IG@&+Aq\dCek|˲^eDι8]h@8MM4 }fE5 (BSKb <]& WיbG6@=yC[ DaLL Ͱ5ejP"ԫ*{uBu!`i+6k~4i#Nh#YJ)o 97!L[Z ,ٞrP5'*9dVY~~LCcrAu&Hj WAyB"Qη"@(#.E?][CӃyO8w' Cc3 DL:uD%^LٌC+dܓNT6%h  8&E 䡭ї~6*I8g DַFA$8{tNFZgfN!]5'*GC#IGNEńw ^͋♌&!~F| [لX#ʠ($]@_վR3#ڠU"j&%TMtsΐ+o׮MD)DY MB|gvv ohs0( U}%4,98 t@sZmCF݇gh m"aٙnHL$rф^ f{D@L(͝,;29Trχ@~'|AOnv" glW@IO3=% 7DzWd]/ cF}w|Jñ'>ah6"Drf$VYaԌeHd5p vєg8:81$Xk^3yԻKcHB!yjJA+7`d?};tLe9SWNqK?P$X9h޾L^N.g|q!KU83'7YDx?ԣt}Cnþca!wqκzlA&rn` =yP޹-rDuG>MJK6d"*{䓡Cփ1?l `dra'O4|KVwWNk6eVv ʬoRh9~^5݆|Fܻc]؂` 8r#;KQ7{VDNM}Yaw gۘu ݷUrdBP蠡X'EsSgAV0S)Kc#bR.+H9WPl%nAOӎZЅF`BY5T#]3p<wRlvñr)*T5LG/@9FB Mg$;Ӷ^+$EkvתX?6[ÕD$٬~i3z Mc1"@8@aA-֛KiˆSl&˾1!8"x)ڭ X*"ײ2qC6m`keD-le0Cg)Lq-?{z\ʺoYaafY.aŎ rCh2"84{OduN0w3u[QBu4F,Sx-P%(Rо%[`c*H{3<iӵ%6(@oH` 7z19I&'} y ᤚVO )d*C ~Iw]EazlXw] 9]1jS[Slx|tx.۝45DPct, ͪONK϶NC~CdjA4K^AϿ4R/vU~AXa^mi>`T}B d0SQU~Ϟ|z~*Ԥw&F>Wڔ6c߻b,mFp]Wj*:xڒ1./}*-Mxs vB#D.7{* EL0\vgCDOǽ ${3]lYd+x\mgl1>}=.{vwq Kbs?onxD24m)ibq1>N`@LHkd Gw[RiBD^ʑ<扫 E%A)QK ,7_KCT -%8,Wg8f 19mrp0IlL ۜn)=g/d{/ƽ:Zҫ(,1[wk 3с}:@?N6c y5>P^R=ʸ2.vg 0{f݌"z:K:]aÙu::恰T71 TVt@/|?/3E7'cI * !GeÝXʋ|] aAE!YG1UcTt6ќw?وfaW0G1HLd}IeVka:нfo@hdMCI]wCro<qV56ypd܀= w[ t#XQ,kujx\֣;V@0id?Oo&>QNb&iks/I\'Llar)mEÉPNjc? ػK忀e*<[(H)zi,'e 4n h$v"rHNC}}ye"~(ֽ"5DyVHF!z%pNb"ݔ[v; o-`R\CLW \v ^cjh%F4K'䏳30iD-ZgG̍Sb^vALUΕZ"i,9 %j:H;z$TT|b4Q) C'ՔPDѠ<`o[V#Cg@ĦN2W$99{E`O S[ ]|h"<(읺]-m{u\D"ջ&}u %[HQlDe HQgO*.Z8g]XGF6mX\5+!}ܕmZΉFM?h(9߻{E'>8( `{]\^=HPTrsқSST%JnXq]A/Ԙ+OL?Vah6sNe>0zFM @aUNUeQ1Qlb՝„$a LY*cڷ*CKuBs5Ùl7{eV=%0^IӲETH پ90 /`mhLMp%3%D&URh`` שJC!( Tk<'Zs m%6R V8mǪh~NVN~5=k,$* FFP6pғx5Ģ} 俿T0iIPzgcޜ K=\tNo7Vq(DjFfVY ,0@ಚVʤ6hcmM0M \ebU><ʸLr+}xeppP]De"Af=F39swX$_!ΧJ ؀ /oם0ٰp1Ed:wbq)zx,Q/89|S6s"/rC7lցƿ&D N۸]VtlVxJx\581m|g6H.>\|t꺯 Bwr:N_7k0!*]Ss* nG{m#!@_IhD/ԇE&LdXƼkzKu͝6j~EI}D'3|MHYǹnJ7s+R-bKMP c LOFO: uECTAۯ AbY8Pe r}ZIW=w'e2Y II"vS|o+l[/r:Eݏ)㎨bo oP@MmE=-=!R 'q!S8H/aTqIR%ῐ$Og V@ZMD/,7 gN@rȲ+Sв !vMA .c_啥# c6 `͖ES $:2C]q$L^h=~Dbθ_c`,t7rx~\09) 145M` NL_nu?Cu5j^` 'm-qVЕHcb۩)XA36u"j+ :Z39|KZpBy8 !bwYuE LPWdf}g.w.X˵bubdvhPDYݐd@Hk q7ZZ6OS3]O8WDNW2TAeU ?^v1u[vp-%`37WL,-ܚaV= -}'6t(!$5bkyQ $j<}0}K4$`x>/ (bkK ;GqE%ߟWl@2@V``{UtX2b}&%v;Hc"$jpT.?T}=0-4ީ%>;{;/u$WHi=(JN5KMrk}+Qgۓ|_^yF^@FgKk]eMdO7/%#ArM-)w×,*cam!s23Ɯr]Rԡg.RDi#Kx'Mwy|/~~ԶGc]3WJju^@dk}GI|WHECfKy N.:7MoS) pcrQE rAF9P3((*#Ke5-7˫pݿtSvi2,z+q;69f^˖uflP脱H;n@*6F::"󏇫]$[N')P$$bGjVȇWn l Mtş DrM_>$X2Kѭ"em-ZxXt{Ahb 7$ݘm =j-HgkоfIZ}OOo 4Č˳5|]IaʡlOQx @zcJ<05S+hp"<%ᰒ${2Lsi7fvwhعg7XDTZ߳Ĩ'4rC/wT.álDvK -9=϶"t" ֈpf` J!۔E^6` ی-qTJY>Z ,l ST \;3X5WȢ'[NUUb|H@pvdb黹!Fx9!c'@C/~|=v–֜IDzTe%-o;a߅:\x{`IWm]:b0#Dtro<-7y,$W 7V мO>\XдüZ@oVǗ te5L<_4,'B돬0=_;مHb̞1o=r&c+:TM0ڜK[`9: 7rZy*q ? >ZK"bp#Hjc 5yrꖻ پ 5/@s~l7#d7U>UQ(xڝz % +~'TjzM{V.&(/e*{Dr !3"F3v;du\Q ⎃@'e2NXiV+K2RBBAXs5&5Y@&/`Jh6/ 6@It>t'O-پ I Njv##q%1phz;Qy^}xˋ&lS(ATݭIP'CK޺WsJW,>h [ec {H}BCፅ׼oY*Ҝ~ߐ\mN(Ǝӟh;z\Hf{`c_SFʈ"^I&?$J \Kwsk~F2elY0񭙿*{74,ӿUE}ŵr.7?c,O%9 !(+\ՐOaеitE b˜!(1Mݲ7T 넾ÒPtҩ F{ZBBIFlCsSJfjr >4$6ڧ+X ؘAwtI ŧ%tqt ,ɞ3iPgj3PV]POb[8[zs3$Q(Jlt޵v@gNTf%=L)@Q?6Ok-- q&RM&ڸatcgˁK@Wee}Eݺ\VL;Ca;<=ao7㳛E[:uBv~L CP[T"_?͵5N˗)$A]>oTCgMh@ p-S*L}{-^JX}ń}E^ϊe- @ o xg;b7 !a7M۩+5l~a_=c)ZFM_CiW|24H7]ݷ^5еS`f_j]k{%_\Dj.]s#BI|A^iߪ ~(( [Vi5nnذiM=Ym'MvH+qG1hw {0;DC]CQeemuy6ڼmlV=H-~r/`y yS{ ܝ^ p˜&L Evѹ-^CJv\o-[񶏳A>[ i<] iGfhwe1!dFAq*_qh i.H8Ӱc!dCM;+X;.3 eY!5. 41i։kN Wsٜ".mđq][cjk޵8Ƹ9.1q .#30V{oѡQ|ˀh)9'j3&Ў9%b !n7C<. uDkf[ehKtL8u\wC<~Ɉ/Ԣ긼km-fyYM|?3Z ,4eƣgdf<}a@ުߪ;C0j @vXnkG[`M eM{> 3E;_J04-cjblr}3`O7hx1~1]NP%ZJspSmО |UH*ͤ/ԑۍ~ QM-@ty"$Fg;4v'c5:LN8Sk仲ɂ[XP};νZ ED7w_Yb|0lƎ;FP_. =L&js.~YP‘ZKGbhٵ!MMW/V&VR53)}p1{7Kgߌ.Py/JIHn Z&]qLzyƊ>w:'zZ"`є 6orG_.x2ʗujzn#4Z툷^@-1ũtlT_b[Z zf4:0 APzUcAG϶6!fJĽ5=6+:øLB}?4]Vsh\v@**;33b?+˧܍^ŇFpo+XƩ?}WcknMA.]yZ<*8f_ eKZ!׵˸#"-W@zB6wZahGTE9ۼ1ӱ [\^O6P8ޏ%g8נeL;2kR6^@.ȚoӽtP+ ]@'|ӘwG @A~oVe] Ok=B,m9&b2[]kRLdrv?UM8:jڤvqE569֎u]hMP<.:~4ȀT>0MRE F\M_ Eg+hD͢2Q}D:RPFvx]`R-2٦Q~TU_ V1*f e9CﲟFHP]p|NP`Xij% pfC L| \ { {w2CIM LnIdztBq)2-⊹2]6l?cu{zxF+ òl025ԇdNn#$AbS|1qb_-> }/ݔ;0] 6sDU5̹ t~&QjT6g O E%Yro=2P󼛈(=)}q`ZsI$(R VX1FAQ52R9MB$_o\?Pc uFk=R+bBc*e,Ʋˌ-a EPǰQ1K@9음b%e!s\!ȿ m!N7\ފvyt*x4pF(GW{b4ֽިeg=Ny1FXvW]pn zϡ/CJ&ڻs;k}v)4 wwv5k ,/;НO FIz,vꦈgeW IFgz3ð7~TM&tpWH,~R]S Lly,V2C胻 u[IF_b@ pYUܴtŪ(]bDF,(!1 .NI*~FFbd+~Uid3Mra].;-KQ=㪻%$ϕz̖2eV&c[㇖RȱOCBa`sجee̷HX(84u{$)37U닇fI/'!+AT}ƌJآSN&z}se]%%wOX+Mg2cuЛ,F'=Q!STlmNa +1vg1;E<_?ibU+~[eKTi{O%F{#)n@E: fiu3LVî$*)2Ap虎cw8"wd5u%'hՈ94,æQ0RJe2ŔtjμRJa3B\*@n E\I@t${0V)j&MP 3ݐ*JoE %9Chٖ/3U7')I{" K%[n_&OGn OQim[qs' &($6\LM$uguezф~]i0߸HpV-UV՚׾\AoF5=@8'Xxt9d>Y:E<.wB{Ey0OVA{t!RvjF6IX&YڄB*= }DS>K]7El941J9)_mLkMTcO=JJt}&.~2]x".>qUJt#^2L`u\Ic/L"Bnk&B5wndmGWz;D@1Av1R,{Ue7*- }R~m!G_ 88t]!~{o{} SLRCQ)pY#* 덛B3TG]@;F}HK,9/VV{wUJr."惠D%a0HˣwODsa oEu(oG;]}bBUئ4q1D5Xa !4?DƀwA5 V`7YԵ NMۅ䇳l<8;*սΓQ*H̕A$gSm~z\PdfѬ]Ey "`itvfH 4Mr}7GDBHlE^UkǶl*seޒ\lK-mDѲsp&I 6vn}$`|;x56}meup`>sQ/"9ub./}t>S3l9T MAOӺ͒z9ew hT t)%Pd7Pȋ;+݊6 W'gM.ڤ@ȇ}I PǞpB@KI7cGRD{gVTKDd1)Τ>i&X@]908Op0qÿ Wtaa"02 k82,$hqOi*e7¶R} "¾BUrx& ?ʕέƌ|8 Q1`S36 `-R@5_J*I$"Bꨭ#oޝEZurqkqq0GFE}dPp`6#B,2[R\D4Wn-uϖ`]B ئB)T0MVT45M1- $A:p{V~8z':q,#["thB+oMȾH&S/@?77G28P0 rq?Z{:3!bf K#A%3E8lX{UGE>(㘡%kWN1i{;8O> ;X y/H4\qMxSMQ+(#S`082Bqy=AXckeEi|bm 7%:6"JTGD= o;^@bmaXSJTh\K" -e@|xryN52 3P^g,+v艿ؗྵ+Z*DKЏ*)p;"Iw 9R;AйI*bqtG˝4,n~c=׷^PW@-""LE)DjU/dФ5w`6a0 FC#HleʷnJo%\8KٮQ۴)DR"2C<cdutb\L`Si hl @lne`(gx Z4t׎S:ZLHPhvP6阞I^$*F#hOw6}ViM \ |u|G|=1|5F]h$9s ߰ߋD-o =_/F )y8pWcS]3Bof\޹4AxT8*ߊs me,b$Ѧ" 4z7jwo3%t(,p4>Gb1͇U|_l414uttK & t+k#YQeVx @UX&ʞҗ|Z{9T"Hᬨwb֠w\)NH憁›tguCp*54I c~MXIg_* M>L}|ޭsC޵R8Ōb) Y?]^طl}EK]-{\< d]}9:zfI^$D{ ,t&Q5M.Tb˪q@.Q=Id\o" CU+2  81Й\IM&0¨VJ·[$QY<_MЄV{̔t璭%}u{niEs[g&djO Zc'&A!yCmz΁n/q4:'#k}pvs802 uJ5ۢu0f !$,,EZb3 v?#X@jc =}[ͺ 0=tA37@A$%ȁx_yΣ=Q7(Q^ؕ .`n{š,練[.}}K 5ERY*ƤBR!mү <" )bU^Vk̹3Ǩ0VT~xiF#?%.C% 5хɛnO9KzjD,2@rem$u'ǭ`Nf}{MO^`baiD~&<]^G' Gim !kG-Lw8Tc@# 0=(Mߝ ]1MЗ|T5Հ&k>0a3\ vv]t@%_$zoʳšL;Ά֟LQ1TB0wNH#:5z4U˂YI=,vNFa^0j v9fQ݅2W!6L%Yn@{]Ԑ+ >SwRC^ծq_?6FhX. (l4:VGM >QNyJ73wDnux H]: sC.P70 %%RmΊK& b6Uaޭ!xŦD߭ۦ\4sⱻ:b;D$@ctrNVYb]}6QshPcb̖W@&x)dNGa*x̝muQ`&A" z>ll;*;<(M #t+VS#[Lг)K <~6ѩG2Õ`&Gaּٔ0^atjݩ.8]qta?hYlO<)͠ ]F37wXPGVp_ UJC6;VA9 ZyLE|m_)aEjof4rl ܯ$[  I^!^.JV$]+}yoC,4oH!3l|EHPM2RTh](fhex\c:(o3yMVS.df&5? Lj^<]9~? ?E@F6M~4ul@< 2&x?(!#Io' 8CvO4 e)ƿqmkE`E1oPn&`pN7> l% ]q`䤙 glBrQ%]#~ GocsdF-.2{,R*aD.~.P|9 AXDs,; SdqDw+p8-z6ihZuѦvsZELD)L u4;8[A4Pa Jㄥ ?MIe_"*M@ ΁o,+9ZܰƱ Z Ѫnҽ"x4$[bè 6j>/c5 BN 0˟~81T8P~an4W4TT!^n ZCC@}a#,o2*zSx)gtSsRhkDZF=,@sJo?)Tף p595!?TM@4no "|B7p 8x%+& ie&L` Kw]֎Feo%XfޣN$ͺ XU@lI(X @) )oT)]z̶ M|] &?  <ϔr{RS' aAC2@l9:xv 3 ѥ ъFZ5Dy?[T rFfS(d%s~ 2yc8lR0q ҂ ,7qKImi\ħk>uiEl|rTbb۶|@o;|횔q~KYx%W):Z$KNh>?AzMf8,a6t$`ړ!'~Ţb,UW ٲ[#>W߰#tDAk:Wq5IziWGSd0mjG5]x+dA \X<0Jȕ752kN/el{Nt[M'mJL<~jrMwrC,pSЕN֌$rGg1M=TVvh^!(o g+d(N"oAQ>#wfdݪioI}:Ti).FegCk7<3ȅ? =I ,02<#B7LIqn(,c9.p5v1gP`\v4Wf E_bCN6Iᦙq*U2%P]K)цj~MK|(_ߚA ѥ+y5$ "!cyk|cJ.gq({F׾Kay\JV/RPDlQUDP?\r.TBT< ȃN,U1\"S/ee#@ʪx8m7%C\-f%} (kؗ CɎ) ұ,S`:#ř9eIcQ7{K8A3Q]O!d߳#I^42M;%AЗƙdX&hFGд K•INpj(`u.@ʜjohP zYT [җʵSf6IIF@4~TR&_ij_(5,: ۝/`gl-Τ{6=$`8ܗ rh(- (y_)"1th /8C|/oi'0lNCx猉 o9M &6O/= ";Heʸ NugA`wK4|Z (l {ǹ5;@ !N{);ReD?߄DĀeREl\(2GFd*I`0҆`1:Ao:7ׁ B"Gzb\(j\6-&U j<|̗&/ `0Ld!^f?D mdPaBd{Y E9ׁue1Asލ1?{W '@1bm.A EYeU~UӂH+9RN:ޚ%D1yD܋tu҆oJQK>͗lk;U!JuČ_,Ӯ4OEwJ]8)@) YF6sViDL̔R; $Ij"<e۷8gZy- Ozѕco sv}%6pog2k5f۽> sQ5WA t ْ&d~.#-#5ǵhB( 8or4Io-71v( v.<}."|*.NQ{u4֟p*gsttڸ8|npE~VMTn]r}HQ9<(r+iN$7IJf)>m bL1mWRG773dRRRL$QY1iƐEfwhQap:LIj2~RƿIrDڽް6;p, @n@(='h r+W}r'Rrv@9?k9ZФWpGi*{uiN!s'cvtV4/ TUs%Ο"HpmHC6+_`$/ qmЎa؍KQ鹆Kʶ}"ӏǂI 2DBF-Q9/N3gZT.j!SYsY4ƔSt+".\Ϭͪg}''U&8'_r9Bx蛨T(_3a-gךB9mg7 ^1u4uɜ'!4GH@&!isޓYa\1.ŃEMu;ǎJ#;3%OE$}vfisBt;xG[Ƽa| =6<4#zcAof ٦e 4~9Y2 6! l3 Ě.-蘮hڼY8 !- _|n U22#$&&.4IA\aUN} !Bb*}68ݒy%<ۤ&d6Kqzσ1T"ĖyL,\}@ c}ZtN Q\{%y-{7} * lcdgswEKD@rn#ՁĆwLEkVd?`⢌ ZthBRvS!nyܦ]y>-__n&rBO pv㿁9j][$pY=#Dt_&=!V<<(5[7{~wY `aʔl_Z3sx9'&bfsU<]MHs"STӅ4aʚJ'f2 :dww%"x%7m&bL65SYUr|y6@!܃2߮Dn{e.䐰Л@N?igB:~p>04>ی_;͡J/WE$J )@UR-U:؍M|DjFr|NȔך(C,#u@C$$ə^kΏgF"|Q8jKY}ujZ!z舟)*I8U*A&d.*Q^+XAs9hPyCݢ`;/] \hs I=tU̙M5GڥPCe)RW{ǭ.`< YDn#'+(-,¤ODPIt-F H,!1Q?."̛bVdiu^_"^s0~,]0H'F\"]Hǁ!Ew/mۂ qQ/ʐ!  S c6T,Zj 'kUG|ҢEG)eOs,1dGV,ɉX:BJ}E* }M E -:Ń>P*l+S2zBb^ TN$=B0ʟiPS-ř0a :\Ԕ$Ї<v 2Jn27!s;M,%[_LhG 1\P`d!'2]q)xz-0($C"~:X-C > DD-j`}S|+){xvot&J Ng}:$e@/FǴK,'K2:̚GZv6wω)sG"ϥdFEBZZ6NHthk|u^aLɮgQ"kcܐm'YȀ53Gt3b{D  n=B+[݌LB{e! %vNG7 0y>. o6sgnY}J4F" #r"_CR@jvkeh;,dQuh<0+g\@{A,L΃ÁGk箏?j ;ciSЏv=n:.=,Jj|:TH۹,ǹ@81_k2ϝPܤXRWg+%d`骒byJq(j}@da\"JEqz[@D:d=?߳aNL]@& [L ţ[q]#yx!406$jtm>J>ډ`^×`i ]@(sQWً-fyM4 P81 iELUg2@+Q^~}cz :]DEFM h 9U&M]ͪ[9Bƾbu!!=GpcёP!\f&s/k< 54ryT.%EY^u$60DY"W[͢\eLT%\ %;\( z ՎLw|H$_!MޕV]h7ኗ LFOc( ܸ%:=)*N &n:G8u0/[aI)X5 Z3k\v=Sҳ}/zR `@ut d۲!9JN%/$@\S["fW`dV'8JPoN6,u;A+)vmlػ+bzXCDzKwX!}:rѸʝt'`5)jgؿ iFeͯ~һ bkc jB'Y Cesv38[j I͟pXEaqY"kYj0ێ;T~"t-](6Q%q (~z{zs8TPLO_% eGIon8.]}o-d|7Oh\%\ݮϳl2 e05Mm!.sA,vnJ&~Em"\DvJAMAEsHL+.>jE]%|Mi.4P)MmyY2=ό!EPx^]>&2k9Do5JSҚy d 24٨|w; DYk):=^<%pخ%n7hgDEN\1h3EJU[1"{fDf."s`Byפ x1YE{Zڼy<*ejI񚐺0m| uZ,8j6GCvaE+h%$HpU^!QG4uM:h= 4IR <ѯ^(dk4IPQE#5(/l &Q;sq )qdo}p}ȄMѰlWI wℳ`z_hff:[r]УaE$nPX/Fr`gѮNc߬6"CPom?˱ۜ5.а*w|1LQPvGdnvQHxdmzkwmtI2eGE375wUbIQ ‡Ŏ̄_ֺ0]@wX@4Ugk)[_Ó@o>e?_lx I qCoiwEp"spbm%:}kx84NҳNTflZM|m},4#5Y$y4S )`3$|zDxfǵyLiVtWZF4K"uPnoxtΤ=Ck WWeʳJhJ8LG~ NN6V!_0d q.L?MѲڄ& ;=9#_\(X!u\[gMuRm, 3(N;¯At@Jc S[;8E'6Ԩ%UPRSKAiNF~1זӅ~Ա0B!Ʒ`Aᛞ)[}}R$*qx*2~n1b[0\T:1N_o);t/^N}Ll0t@T"\ޚ~ )N@;CqrȽ5XVWE?[0Fb*n۵ad4G X\{/+eBs(ohXM'K͝18JAXkz//okUyt \N0g-G!"-seފRۋF6cSl+T+S?= wv?TH܆XйSMZ6ՊT<0 :>Zk rvYBvvf÷6?nNa`щ[ s? Uˀ}S(oVb@ ©0&!T?ۛ ەpb,:UU1hԽ`]4Nܯ^HҀx!3=dRsB} dg>~XOMA+; N >X?: Of #CLNCS;;JuZ_Ɲh|rNC; l`E_QxEԡ{4p=l(JsS9 3 ά@y+|z\k햡+DZ\@TJFB7$)~#ODu]Eu5`$ CtZPg=`'fR!w}f D4kA)?lm9d$m4Jb|OSu%tqޱ(;^Z ݦ`+fhfхIi+>i6虌dx-֧=V [~mλq(`b'>Xo"`9g o#Xi"0iDŔҊcG);z[ b :V& [%H>RH'm!fɸ/2ΉWP¨Oz8HS_/iYXtoML8\j4Pe K;\o| :J1&Ah_ zx( b^ Fj)c3Uf-dko2 ﮼{gql We)b-i!^[ +_[TqNʟqytH @Һf|8x zL:kc d™& Lul.TXlrLx53Wը >fU7͝ MY8,:pLVz^_G7kL`lL93L;)< U&Zy)>)'h D6Ѹ/[?Pdcrf\,^RXhzU|ɛzr8 1̬&B-Z*21`BP\jo"Hn4Dqր, wSAg&zi ʮ9le}%g!ôk9ߦFd|bc:@2Y ):s2]QB<0WU,zFKŪE,IyIwؘB0GNj2w#rʯu-at5(1X=36{{Z8$) /|l6lNȢ X1sP}D7sKy6ޭe<}C3!?t!TԥV$B[扈U,@}RX=64RT;mޣ of5j_gk(!X'x0&m$ Xڬu¢~@4+sVmk5&8mjmCBfUց>Ԋ]L@qȆMI2 tr@窥5cqAm.W[R,Dd]/rGr׼tcXZh>HA ;rN8Yk _F˴O&W;(KS>ֈZ,щBMIg!~_(_BVi061Z15C{{>u`h0Ƈ^Rb=F"26lGdsX +mk+f`>y$ dN|RȀ73M޿hڱ26]}5,FV|H"scGLHw~wAR[z q%ܢnLeȥ K[`nǁIWnُE!$giYd"-&XpiU{-DCvC4B#x[nSdbE`; )&$[Z{vW_S,,"j V0[.J enK(h[55z9K]v^[ݽ@"|ڈEROmگ( 9T_B5"fAl[#֎Srt0;:jAQޔ^a6] dfn`tE@DZqEU@EKCq1r+j-=߅/a0t_(Re=DbzKiTQ\mxPu;2DWTXdo!ڶRIynb ( m +HJb6vNnFEh aD/dF.E W;<d"$Ӱx6$r9hܯ^C.&:D"I(&E.ZTabkل*VQd-ȉ~3$<]gðM Dq(RĬ%nNʈӜ-\k"q6Ga^2|X5ڇHkhvw'$ m*,5h3LDوP0T4z7fa\Wqg<S' JZ;ߣ{tFJSGPqvIҠ}:PԏՀ&9K`1+iZߟ)ȴ5YL,w42o3˶vZcْO:Ip{No17%UM(2rF /iij3͊A=u\Ӟǩ ~Tq vmV` HK, `*њ Ʉ+cW!'rx{KLslkA H{49q @GK"=yb25,kt?_sId2ž ]aZcT.3vSF#B3H7oHݗSgg~rr$l&1S kkuўRAԡEX;o*`ܗ $$97Lcs!XYmwyjY?g$?:APqV͋Es;z1 &d\162sc@0x,P=Lx~w}}H׬FBκ"䷡| eC5;; d0^G|q~Ƞu#_{<#Uh6?@HC krT=#{>PoԅD&#I}sո娀Dt0jo]+QM_Di.i֖+y#j,$_q2V J]DR$bNQ\NQ1BZew} VZ"@^xX蕞h},Cf%BoT@oE\ACr] @udU -=@$m|F6ҊF݉-Ͼf#+` '}j}2 nM>WRzUn:ɎTubAfG$=!:Si_$u~\1S}WHoF;*tZla\n yk2xb|?ԉ )[n~dz!͎^ kIAq&}5F; 9hҖ{N=$ ճn54$0Wqdl֒׋D2}^ڵ!yABq2fQd9S`g$jNYLm ='x gSbM"Y5k2g8^* )%'ҭ"!mu2 X4pۅԉz#AI@>$ˬ:gxťpAN d( F*.TAR:V /V=GϬ%u_kz /J_b-i/+8d"&D-ԁͤ\BfaVŵ)?U n.@S M,o)e)=Fc'j*E? ᛖ5Um *L/`<wחE1K˲6ӂ\4; q3L7l$&LNWd N[$:ֿlRn^}i#2@Gץ!e 'I=D6(fNAh}6sB-`{swTA.+C9 K"mvܳF4 J5~3smL%}MEdf "M*wF|SPe ;Qn@0~,*_%}+/g#.U 2|RA,(woKULP@ri.j6oFD|ՖDG\&/ͱ/p˄hbs>,+|@Pwܡoye2]gpN YueX5oV"6m[7.!2'AgSIHɼ;V|l2G̃ zbv Ɂ0;k2x ]m Z4KRw,۹"ؤ IsRcr~Э~Ɣ@/ZGǨy>u .]hѠq Ο{Rld#߭?P0&86UT_8wV _Ѱy~ >D>YH|d+7݂9W%UMB;r+Nf $amL lB8>"I=љ-L$Gea3*E6!=Ub#JaCzTmGaR+В[rWxin -ˢ~DQ.5S4¦^zf&4er늨 (Qe,F(BYq"%F4U^@ʶbCQYH(v2+Quah Zw h2˧\Ҫt, vqq礚]_1Gk{}! zWg R}Mk6 ӹaĮTG>pf2d%\Q<({;Z]$a{nCȝFHzCx߉5)z#S'^pRZXrvtQjtd"c:u\tw?L0``lLPvCЌ+s@z e/+jTIJv~=qFz{P_A}HOtDDs0 t^@*Г!\)7 DWd (?%~kICsftʹRT z(_3%V)fY!hPÝ¥puqi %|lPZU'ԃ@W];H%5O\i"UW/?nB{m{jOr2K!z5`QUuձۦ3?X` ˤ^s[֛q ȮX~nzG7XY~hŭu3Rv@XLm,@B.hd`]AK6 !59Juڒ2sGVr `ձeѶ܏f[hYbgQ0$ $G=&yj dU!s/oAzMp^i&"hZ{J&@hg"~pqn(/OPl~{'G邛گ[GU:c[ߚ^M:u!~GF>kE!+kMړԡ-@Svdip(FZَt\iƟ*[?|ݤAzY|Dq!ǸI0/9 ,EB'$Wua[';2Ist/` g˹ba`Umiu wZm'3O)H$Aqߓ迻X&dRGVo63C:}ڇX'eq /tk()%?x 21UIŨLhn5f-({rSHiKiõf!8<;#1pj Z14Cy#swm@ n]FVU8aUsF]BihIE ?>w+iwGF4{wj6@̬pjn֥7nt5J\G[҇ [Wn 8$ ϦzfDŪ#z_Ȏ68&a>7 ! >:$گeu)T}B 2{ًԫe{eK+ˁ"kAp0A޷*¥{!$eRjÕ79 9z9.c"6@Jg 6sRzpGQ \>![ 2L AHevNCmSPT8p\QP|Mz|K7E#U7qyG<':eHvc߽ʣLAkX5G96b7V2X+2(|[E Ͼ:},,ӧiPDN?$"UM]<|`Yr4p:V⎐[F)S}ZN~ "/9P:v>цѰxG΄͎JFKH ݫ)8ECҢ:p= H 7ω@ݨ&Syk)`@CTJޛ).R`{A2xl, 998)"VDFA$KJzw&="uzX;\S/ܡ\{ꖠӯ hJ:kV"sBtU A1/ʦ9u1MFi-yS}OHu1awBA 5nNi"I~)6[GAUͅ\O{"kaf 1Ѱ7:rz;':v#KSqK x]mִ/!foH &!fN/z36K+Hb@9o'+k x7nqj3hg~6D F}z"fl#꽉BCNs?lYDӺn Mˢb((wl\Q_ƴi8)uZ_)PЭA[yA`/\o%\.L.zb=UXIoe:й^/3{cxqUU]7-}MGMA>נ++h Y.<"ZĭPT\c(:Sb㤵}RPK׭C!yqɌhk$l6`}kܷ^S0 6JdE\<F P1|Kw<:X).oQ½PbV;08LdU{wu|Ϫ'c7VDaL794:;\ղu-IA]cjXc iomqܞ0Tl5{PEv=̙fe a?oX=#0Tiő)\8u$h^&x5)j;RL}/aߊSD8m tw o>l@O Q79\"@nzkȕY:"+)m'\W+& d 6˗ҵ$h(|~.DĜ8*f?Ȱ6mGbKɅ "0tFYoՆ{mt E`7 !`޶/hͧݮXID,96'f`{ k&m@+x x5SŅS4f5*z\Y9nX02pTju*S5vNغ~ꃳ_ylzBkk] a. L\qơX-'ASWC㞆^A8@!@) Oq 4Ō:ȑmf6">7+xR_贶z$[ߡΤ&]i|2.o$kvDI]PMxI6$w/-`;hRdMGNE6y`J1(Ew-(UzٜMD*wD { ݥ:Žo½0W,{6rvd;d(L[0i8z G+~c5"'ZHqW f%Uiקyʊpdlv r \άXFMm0MH)cVbLMCB*-xCb-Av3%[Г_O9\_ ڞH1%xx5+J`[p^F2LU[GkrOf۠ҜMڑJVp=KEX9ǣIljz {zP`]=>kw‚+C;+&:Rώ4\^eAD]网7ǝ0t7*[![Y߳i޾&[oCil'8B!Q,Fr:8H[Eƃky2f(*5YI:LǸAF"ؤvCϬ@g߬]w}XwATs#>% kD3j4"EPNFH;o$g=Oa_ \r$u3}ל=UX狶y]@b":"q ~{Du%E"eZO G\Js軔ijNJ$Vz b wu'3/cupȵ~ R7YLpBSd<%QۯOSo>|skV z)NkiYa u2 uR2FTYs&OѺgRUj"ޏ왔cP]z2#lo OpYY 5]ZAI@ *lw)J9Ԅ*eWn g(tX e]Eن:%*u Ngf'cVW!/zhrqwDKB8s~N0NY16 )ڧXfQ6|Au@yM &$m0L78[k$!hOpÿ<<S+u˛["TkNKT#;_ݗ >X$V/$,쉙ו")(\i c_le圓eyLu7<i ,&D"tg,r>esJQ 1?jzM?HMz?-z^!>`kݛxx<~6A/JcRӄMiOq zcV,ApEAN&+#B%SLNՒ~C.ܮ3k5jk!jj@MT$ǐƒߞhl.x <WoP @lbY2 gL>(a:n[͈N w]GrqWEnFdE찠Ͱ1MZw,Wz뵉%'z4Q6N" "}&6їT6FҬ xdֻ$M[bN׷5'2dhg/ U.Ba=FL$,VeQ {!q@,f@׳*!3^14bS\S1 ʶ:m11qcoe?q7͂fJ%+aխ "^sF,Hͭ;'?Z"n\jpk"`r)-[i;rik?$`[Rf*d9I=O/4mAC6تЁ4뚇ȜTyv+ĽS/*8ݙ2  Nn=z1Γz))iA|Hү88~yx{L:qz<:=AM5ǴRJ]"nO$$2ϛQ8f+پF!Įd0R}Hu*Fnc( EL8y =kF= :ʈ#`\YA;RdRL]0=ad9n}߫yǣ .X* $lrBJ tdQa[jDa6ԀSp-!ngeٞ!mD!W{毮DbӷSܥh}kr=?g>ӺuSJEN{ӡ@JO8ɒ Y'B?OV-4tdخ'Jsχs}-O9A\w5BNf,H%i^yjƹ隚1l-__cY/.*N؅~y%2j:HCqh $VĄ k-F({:iFȤU?^kZD[rdD8՗,^$=/ì}3(QW$ɗ1o.ܶә;yi!AH+!OguaBD,:VqmWFܦDEJY]7-,$qu"MCe XzM`2%8fќ/rjtTrl1Q.e}2~R:(ɺToHM\؝'0a͜|:c֔]gtP?;3>L>DO }FLdڀ^&?UOkhliZ-?ށSUVF$G0u_pg9\ۦIy(HMLH{qRRpr"8p؏3-jP!ԎK/7I϶iHzr@\x_WV f|PZс~HT8F/z b[=Y2sKV#e>Rf64A5~ F왐w>uی#"$,^'BxI&W Z!,`N5GI>GٺFCDd mD^H)O4f05@i_S8{ H!\(A_Ew`X9U Y6\wW6[c}p^䃅? d՘nֆH-kAlӵLW&+SsL${ê\S\W-<tmKxPHITƹ>je522ړ$HU&seh) cc<(;o:_x./Px4YhAdp$JvQ8" 5:h*خ}2Ght*Ed[K\(˗ϧ5Da[83.8M\l|r9Da{ |6i1n ?! G83dm8[Ւy|a̧8y'=%cB<ZIbn«hQbz,̯ZC(3幾U4B>in=&2=+ ( |uDI8th1q7Fr"@]5s7}TJžf5[H^R=/czى F_+ )VY/Y]5?(Q` A{]~ L\Y {֛tvMXBq ;&F$}561 O32H&*!=L #Jbvk6F`/j҇vaYFsT;RL:63@~S7h{қtiDhpOe E)2i Y|e͕5BȘ0tҜdNS}4_Z]\4AF;;qhƟvH ٩+e-T':VdBb|Q.fְ@ vjݱfA,/`>/>(RT4rf2oBvVe&[$qZއ}ŋ'sS!3At=(YAF|gxZan W(vj@Q}f o]B[@|TI=rYK~VKZ04 _W 5:SIܹTi[xJF\ pmnSq8P4uS odڽFk zKS3SPD6]wD1bOi/^&id8+ tv3=\puE#Oc@YVɻ#4x}ƿecp8 ' gZ> ^(O@ O"?AWzLp)`g;[3㷶[{, 8:TX#|~ S4.a}xlmiwXS!~'[3 Uĺ &NDn[_SG:3zYuOp}j:@b9V̚7ՔqL-Gߡ $6/ E&åYp$[~4cz $`ճ=b+|Si~+86l0Z2`u}'*'kZB:L~P0:-OY|Jik AЮCY#a7Pٿ&XACmz*1X&Fȑă6)*jۯvg`O+ξ;U9w)j^Ts  .߆v2 `Hڏ|hvC4$&BJ!r[d!;]o/]Ee CZRwor(J@+ &NWlPjl/v_R.v,v袃QI m Ll,QS `4gD6XиDT#{T&RH^,&W`/ -4Eι:n_)"S:ii x%SmU4X3̑"j@֪4'Cz}bD{}fQof` g4@3V&S'Y]gƴDa W&.M{0:~Mq#EmshId + Qy},* HW|v]y7s{0޼UH719h,(̌Q)s~~A~^9nk+:Ô~\k^8-UBj-/2 ~)&%$t`YX#P)BHA4}DȚ 5m*Uzpb`|;7rNduV9yb*ުMkW5סdZE=ϲ.?e^7ћ۞7 SpY㊺A-j2]MAu0ٴkmxV+| Z.js ͤ`T+@PZP?@#!2iQ~˜|4ӞHa5 /Q _hJ@XQ..HI J|e6 `).d{0 $91"R-ln*B|Ծ5wgt\AܶqN/Xc&>m@!V E؜]@W. RLIikKotzn h++x%ʾf{FBjCH#iE~(8.Dދh{S{5 / R% \bYG!䥻\rOz=^(HaAԿ[Y8A)U(YM /ySXJR0WX;އ L@ԡ7zskM'AOgk*${x0k 8!~*~W"݋ FEj#{XL8xŔŮhB-pUwk+)ΛRAέx uI `V_hJkLG lsaDZtV6QBLm׵ὔhkl>+pi) y u#I[شbű!Zv*'ߊsKh1LOO^Npy|@lv'!_&hA<[;ٚ$!{^mS!R-iؘkx ,MO'{p?,>ID duž;xâ-pF>JR_uh[͏OH5QQdUdVN+DiVje[Ya t o-q|$ h.ImUgz9D J=:0 vYJ(JSپ}m$KS8;վ]Zc8ONw֩H%I;x%7<<\]Xj'p Z?lw JTPu_q4b<79t:a2w(*tZ_dX$:BluY)tm&kX>SAd`ҦuM׉&{a#wN{ء!O`"aK U^oldzELް8Hu!U)RiiZee;)DftW\vYw{0c>I!RJ(ّs;|?;:%^<;bUxVF{II+]f_~^>ۃdoBgU\q~,7H[LƸƹ9#7,+ ۻG]`CL>ncu"ᱩEeD; m cy;3 >RGz5W ,b(58@cMuP;,Pv0@rgKĻĔ9p7`jE> !„v:IkWD;83.3ݝH JF͙~"T0ZHN\1 %rkq;bY8x%NXuZK]J+ ase1+BvACh%a A x㐣'…O f@)qX,wp#>X W+E@X5xE92ANL,=,ԛ׆]C^7'JgBk8M_ϤO4\2 4/|l]_(zT_3 3h_0@zi$a#NXseDg4t`IPOط8"BÃ8hN.b4Q.(8^yk.xE<-"̎ё'+X@W jJWB5?U0P?ji7ʇy0$( ct\HtJn1 :F^2 [#6aq]a4i-R@C-(psʐ9Rz3LyG'aj9JE:Z逛^$uPۓC翀l r|KBkGcLK]5 aOƎiCΒe-"Üs_( ZCu4}BQ჈Hh>ٞ|/v \ud6?YYRkW(w )D?\\nAۻqNOaѧK9D^sEэXlH\e vWpSA*cYT>?G)}a5Za}Q$.BK {wtnpb!v# W:T",9ID qzle((xWBn]p}pKh_"IT3!Uڴw ̻B`GP.m0Q}  r4y,&fWY;st6Tw[e#VROҘM@Q͖^I*6hR+6k(2`wSV.M(*D'*L%Ϟ/0ܭ}28NPC$_Xzk&^%x5 UPT f-/Ɂ Q;&U%]h=QsD\(v/}XU>FT|0a-M\)Co+92#QaATz@PCcI{Y)ha/[NNee}`X$x*xI6\M?dF3\+ߊ|Sro~~6p}@o.fU}m<`[mZf˒_dOu Dt$#ˊ=c^$0"]%!a ۷292xj(z# )!Vj!xX">P =u`"5,G!k[\H@('Na>\Ğ$Y|e,jIj>B2 # 5B԰bSةxs{dQ?.7 !Yeo}!r?e.;umdE,L/P8" Ԭ>D6;ɉ>wr * )ԓ*fPUlsJS}-l-C3ƃ G6|[hEs!E`U{ 9DNg1~>D8,H0lql9}[oK7h'!ouSgT2M iTepZE~ ^$v//]>m]#9:eh39:=,N|OsvH+=!7U|" &˃fM܀.I4|nW_Զ/1UZTIT];]]£=iԇۂf< &VcS; j a $ڮ,x#quBEUhCXb[h u[z} S7K )(+ jlI*leGf6Wh<#] 1,r&foëEa?>hLDO!tNj^9\*ZAO!Y7b֎\@p,6sn !n(N(3&v+qߤBj B n" 4p&Gtz yoٙlk("us}oE s s7 :\- [U,Gvt5mNq2Mt^{{/!}p VN;TH#֯&źbyt#EN?̭"̦ IVJX)kel,j[=#8E=a h,1*Ô*cliIO2&z2ucpRh6ɤ{<!oRG)?}ŜH6 ⚫vP_JܫE#Og|EĴa{PoI.H(62tn?ϥi]DRK]IzfQ'H,b$_D`Q.UGYzFhQ`/jdtw*-ۊ>Ԡd2T9բ`wkZ]zD ߠDy=-wnh8oM\Wh>}hxnW螜$P=΁#܍ 0{J$,v#BD" 1UW۝|OVXᨪi5lF 5h3(kŲGfZb=~𐚴'eu{Z*7 %9q˒{>O<|Ub]a"Em$\_S& ܕrwG0L!yj\Lc/huM #鑆AoS#Xzv.)\J:!2Ct*mVB`+fR׊xm35G[D+AEKm83z C Xs 0& g{ȪuSj2K6ej1A!BD7Dm?I9!{58׬%Z޹0`!]*!p+ &+;"3fh@оK$ry;14?ܮ^U%_F~Rmײ1٦g>슊:w;pۊ/!95cwW|HiTҥ] WwQ{X<ӯwSN9ȵ4Q*Z 8!&?6_ ֦Z~o@`nGwd/ xڲ)E7LB; 1nneω*v^޽ل*ިQ3c">!@$} .9Ah7i+}ׄ@XRw#eKptєe:0(L!)~WD"69utԵ1xb߷ R.^Կ-g~!)VͱЮ$? tk`9_rJ1qFC9Z Nb{"8kIUN] IL*rg[ܼ;4P~uxx7{uYnt9tqn=D^l>4p EQ !tQcuBD%@۟, 'lKVw k_CRQ[ZUe5x.B@4{(#}G#A~M]S%Dn`:S$\"LްEY~k]ィH lL%U7jX71X{8Z-pKf'nIO#|C}':kk0"%Lm/R/"պʙ Y)ZtJB{%xUq n~G ╚mvr3 F`lDsu MjΓ?r&v[>#$x)Ц -lFnGrscǰ߽6N-8a2Ϫ{vM#CN4jP&>>W9g!T6O<85-v!崦nuX,H)0[IQZ8GfK N/YxQ[<|kNejE/ⅰ A~-<\[kQ/t&1oL;u k6vHe n讑OG2hgHS@pEqca;)k.u冰lupDN+4͈%1| (6j;s̑|ByoPd<,U[ʮYpϞ@jݓg0/E11A[C ٞ'ȲHjz&ӞzJ4a<ora;]vpJ[Seέa/y5+ FrVl0e 69R{4Q3h⛦;8`D^mh b72*K{;WZ;߫c9oIV_7:. e16VξG!^is8I35剃g={-'V[{vufm\Ϟ~?!ڱ:*ɹP+:hlYi-xa$'0$ԀQTʷˊyzOt qzw $F]QQiA) xhb烤dh^#=dB&#Aݨ !q1Ηes A;ҵ؅ݩD|Z[Fā>Oٞ_7Mx-9ɨrL8Vl:G?hO &%̱#R߈ҙCab e|NJdgw\kZbSz6;rB|ɘZ\m }2RΌx2B߇kwt@X^c%75D)Ώ^EѽSB՞. {Kˈ0ru[cDuhzÞfOuE]I.^;t'q_jZ^g A|ӜxrD^[JfL w[6cX&l/3pĩo /@JgjN EHm78%Qv5fz̴dx,7RtuFq#ͫFkaLv;w2FWuxh&yT12Z$<!ߍt!l K!ZOZ62NHD3XŖk)Ol>Gִ2yÑf:xu}&;ڶv~ ֲ6lNlr ,c-:_ 22/ 72;̽^4 b?5MO71rqؾxw7{)U~ׁfsD%"\7]M"p6e=t$hRsE. $= ODț)b~\1匾L"},&56 Je.ݰg\X da/J6/3=eQ'.8r=X%pa@@( ! R?~蟳L,Fr«XB35FFD4[ AH0z\N :rHh BNQG_$E´x@Ú8ztOP0Pv/{!UvšEmÙ6tBù[" RVVЍt*oao cFm8Ǔ51]Rղ-AÀ1 q-=&k.qg6oԯ-:MOMXg&#d@#^&Z94L%Er?h!$$̇%J wt')95XQL(76AL{dM9} a.oMRuBBWMq$C}7!P8JjAQb<'O]#0Rͽ4!r 0o)"QA˄ ېH~нqmN!V- QzJb- `lᷟ e}-Z꾀-ߌuꗾ(,dQTөyQmhVz Ndk0}I ,T~w9F\>eLAm}Lb6I/NEt&~y TT9t|t\Pd,dpwi}6e)ux4guW5-' r؍Ȩ><2`4p1ʚkHm51G؇ ZoUX*A2}jK}@ѢuK*Mل >4mj0 iDNȠ?!# M5E8hUxUn6me}笭GtPeC6NO\+#{xe&]rA g*TBAMs)DI і.ps@b?KwlVGNz#'?żaCuyD, 5 6@ lcx#Ul^x]#=t9BVpnX^эtMK8"8LrgC\Ax mQ|`/`Yс(BG;<ij2bTBsEv>B48~|/$]n p\Cs/3g\Jzlb@^Nق-9 "ȴ'5.o2ֺ9^7ݽ6~p020흀?kjh*]o \i?y;SDmɑ7WS )a&7yɒ5k>&_D&-As5E۳wfxDp8I@kBĆ, !(wLV!'Nb IB6p;f%V⍨oAF} F?H ^ ۮ| ꀑ6DHO+SE'̌{>[%kb(Lb9ce$%X0i Z]QXF"}#+"NF JМEeO&9#l`\9\ȎTg߈5~Ž@B[Vਲ: 9컢+1b-QH--DAVu1wKZEzN"iߢ@ÄdӃR>]鐵pq|_4A-m. "sƋ6{Vn~q#;"f ^3[B<,LB.8-%d^ \-|[=5[1$ H8MJA ^P6 \SOtW 4lڅlMJKW =˚#O ^qr1܉v LL⪧)6z~1ٷ3 n(.*CKj"UDT&V!Y#(j:4(?ִ)龀QL8zm )kK0bD1=cۦMݒ'2[$26cdbX;4RC Q6T5zח"M4{e0+LIԮL`"#o-3G)AyA:$j{|="@%~(^2onu l$y$!'$N@y7,"9C(=]+#Q !Mכ9ͶnNV ZPP dYc4_%hBe:2X] 2zkĪ<Ϸ]!F.gG>kZ5ll7,'&)n/lGg!JHd?@܁VkpHΟ"*U(9A!nFO5;yF;s)&8obaBOYi j]AE 7(,م&mBV_mw[@&FR&629L캚)ʧyRĔ!!͑R ەt](R Fts&SJ(0|F0`=j-t)H$nv8jmhn!xݾ1&CW. 63꾠UV'xcZ޲HE(9kLE8pw܅϶UH^jXQQY-M\ӪcO"c{~!6&%R{-$xOsI2dmc)+|I̙wQ$hAv#:,a\Pn<{^}h!Sp߷} P)>rQU%C\nA3IQ s)3GHX?9c(5FVSe@/ƒVg"}zUːm&QPI3U06a>c& Axn)J[ȥK*_8-V L-A?(JY*rݫmIXZMpWlRzp~\Sϻ>DՊ7YbZSDHKqc81]~[hCꞫ?rhäI໴8061;ՁnmgDAWrn<.ǏxE%Vc4@˦4E4f0#u!pk;F|uQkDIsήei{Eq b6 vpʮ*ZSm0j>׻Ⱥ"6 {wۣ5ܦd-[W 53kՅ m- P6V!UKR鄋Cg+&dRT%@fǖ"Zs4EdGs 5e#ܹte׃v|Jd1 wPԣFnD7e0DmwEɚW6R锩ϔéͽnc;?H-/-1ӰG1-钣S[%;٫)rBMV +`fn7GHЉ#d_I'צq#:!g4Ej]@{zY/"IvOQ۟6/s~aD^OcJqn9Z Yo{ >^_ eY10K MSi'.$;A b.fbS <].\H(hk(e%roqҨ:@`ɞڮ,W\ؼ]gq{)]3rq Z"K[w nxE⿽GH8GȊX\W35i}Bs`XH ~3:svb ɖY!]3sB IQj0VR0&LfQFGDrᘍjv&ؠӍnr("{fÐY %ڨ#Ã/ho7% ( Y \qrQ"I㡸RM\e$}XB j#!jv)R{Ad#=uj|\AGiI^01 6P ۴QtHo3dPoIC"[[ زZ!^@rYDSRK(ׅ 7h'-wm#Znq":D7 qm {ȁ!!ڂBriVCnzRnqT-x SNȨ(- +; C]x >/K؟Zhv/jm)㊽;&E 3ЅQlI ,-X9;p-U Ð BD0.rҠWdg^h\M=NH1WmRpSO͹9 7̨ƵNP-1Bk Ĭj*ݳ(6hQz ($є}zofm}-05^k>n"!(kA"mj6JoI"Vփ<ɇȓ݇#d6I9B "`<'lnM 9GEεckI$*ULvTHK R\kybENatlIm?e*xeQtfwB>6Εq6Ak5]`Iohʦ0{kN2t0c$YTep$ 7:Bkb~L!(k@m“*[>SAufXٌkyH:>*dmLXO?eBR*ZT~[|"܈nݠ•? 1!S5wqxfkfePZ3pxW ״@tFPߟ A/edI@`8+;Q٪1L3n4g/և?*jfj@X(QY㣛>gZZ!4xk-6t#=9:;`;;%?t:7}MLfz钜@V .M\tٙ/߽X7_u~aިȀ깚G7f} e_WY/E{Qp)(NKф^k@H;@"RÈ|@W2{cp2ޓixHыȦzi ѳlV3BQRlփ›wٸ"Q!b^ )'VCyg"N>}Ml'H%.UՄ?/4 FҝhįZAΔ  ,lqq\ɲob66~FVGӢ~ӋVbs@lOYF]VEn$I6Fɉt=wD 3M>~IҨ2'c,ײ,n$`mtBCzӯw͈輌 iQ1H^χPvEYB0s5mSղ;FƍdyPOaD0= eOj.LEj! MAG*阡܋$i7I[Q3@293}[Ylf4[*v,]=lnr!׿6s(zEԇWkz 4':&JN!RM=6p+y]wmqQ9!) Fq'~*T6JڡUG~Y`&u V[?Eg-(j򗬯%l)rF?{i, KiKY.52.dMUЗ^8 *,j?H` 1 /gUqn&rشyV#h 3|8"n~ʵBaRT[Hn-v/ f7{Ww ?*NȺ֒U$/Qld!f.H)^19qrW{XQ%95V>bQH!Zzk h޵>9 4 f9gYzv/Bj!2#=!bnl|#}"`Q=w&1rf\8Qr,(TG[E%}|xV=0V T-j'#Ѳ($ĚI _&9okT@0V=GcQ+HŸs~J-DxD$/k..͈SG eT-ie%UI,rd5}WO@;)i awzV?wм (D`@4sQY8n"'X.^P+w\.];o͙WuI03Q@ͪf^CY*oTh< ËA+oe$ E-Z_m#2sU!shx\Z+δLaL_nsuu%ȝla{vw 4Wn k#NX$zyk3z:m[l$0%(?rg#mp'A:}VWQl@uV&]J ku Ω Kr6 )i*ei]6W/d-A,Z7I{H$ 6 d+HlޒJn,yKBJh'{Oե Dڅ!@/^ϔt탐t,h(ʊ$I8 ?oJkѶNA[xh'ba㩉 h+zQCVe-%d} s4HS5]SvՊ$ݢYZo& dbŎ(",8m3'UNfl$W i=f[j(7̛)$9l+P!ҦͩLaۡc^>]1^Q !O#`l;'iKה@]Y0jB"?c+!zcwd k]2 [T ${f>qT(YNo'Գ Ҭ'_ k),܃kWLeLtߥ @jU~WVԏ5$[8.X_!JL8,;W)]PA"("KLgj(V[KswVļ1,ѳ#I=RoMMcVYI@dl~C1Э=I@xŏyS\rRw}MM ޳ṁ̆1Eq#iHt-َ N}4:g f e[1MLDn~T|o&k,Pkhzs,ִzso]zC  Pwٿ_F {2F![hEӞKeQ\tAH"[pdٳ X? \єkF5!3u&X0 Tk"`!-E ;kx6nf7 ,ȁo&? q =5.&{mIM7jL˽6SPeCu.B2ϡiG+X=}F.3Q >\]4\đrgVB1eMT!T73-R{PV8'OT~S\M'[[l?jB]vA}hrDRHA6A(7\~.I^jPйlbBllCگ!{ ШGG/y<[>HC<_{<ȳA ? zR (`V)Ƣ:mIw`f#tҗ*`J&Mxf}!KW}ۛėJVFHߖI/NǍ@2=x&^  sRNф)Y?EKs$&@/EΩkp6[&u p- R#WO ` SU5Y~m汝n&CD %4a $%+-5P>}1]b001"c u.hzu]]Y8,[@y,aEOqa+OpX*ԑ!3ȹRڧ;Q@Db~|<U.Y ]u0 >]fz]pBi0gt.v (.~i9Ә`죘pڕ`9-l h:F6~^]?v拈ҞOQ>!H|{)ߛEArťgOA])'j]c 7qʡf=ҕx4KT¸@|39pD@gu=8Zb_#gOj\p0pd%gyu>l<ڼEA^1=cXkc?zX?L@,[ fF =<0 LH'e.ӱx8ܵ_bs혃/闕>Sx Hw>s#ޕd[Jɏ8>3it=ukՃn 2:UԼ]&n ٌ")y|2=J𧣎衖/z%;=,Ym{gX/- )@%$[9` 黏B%BV/o[30,?dDRhԺ]j4+6 1Y'1i{y <[J}“ޙV;'D;St=#m&Y*S$lCiVP+~q uvu']'a6nןz{Cd(4+GBDܺa[~E{ ܼZ( ڏȨ7^6 )RBTBFʰP- f{qٌj2~}ʨiVah飹ɷpU3BD \f. ^>4ĝ E4m@`.^r͇wઐNbM£@ hD,i)q(<]C : ad&[ZjLyoq pQ]hQ 쵓D9()M߾цa0ߡ-eKpfξU;25ђ5%Eo L~6 Q};{=w>[Q1ͤ҉\U0/0tǾGwnEy-n`yzy`[Z0㗁f 6;"m(z6jxa;V[m]4!'lV_38 7 "iIm8x\q3#ph >x1M9xLwWڷ pYNZaIt ZtuuUo8[߿ڒ/2 P"Vd `4h![h-n,rTC9gX]]e5X =ubE"]{I3e}3\g%+}6lL@ J(&O@D"zy縝1ؠB (dk0f}8cp*׌[ɦRMuA J RjAf@ֆD*qV{d .Z4ͱg2“4nQd +gMpۣ,5T^6HDu7 &pDgZq<!T5<{Isw|N+{.jAԞZ{E]c9 &f|yofkZq(3HeVcN φ$q㓳If|Y{8ྞjRպvpR'l ژbWF3.\.7z28J_d=,n/ޝ9V4q6`Ef 34݆ԝC'sㄺ;fhoXP:4@mEܼ;Ry,}G0$IKbu_4o!>tY'$~X(*LQ}g+6fA4%S|kOhG{ǩUIDU~B,cMNtCB $^nTڒ}0Wi2;.?ç.%"ڞH^` 6g mJ9 i?dN:"$(ow 4@R|5Kx7ƭ%v_~ov 5h:Yޓ]Q%vH-,6Z3@[ \z#3; $B}3>b.8iHIol3Q\u8_(_3XWχ!Z9P4DY &,epx8ʤ!D}0DNj+S< =YPp'=8$<Ly`z_V6BN3gblZLV.Й0{\8*@=r`+%BbM.Q`Xg>ޛ"4}d`x#D׺ u 0k7mFyej:vɷwXEyoNamݨNW,1Z$T7pE oiZ)(l2BiB 3hmhiƠEC086ݰ^+E ?Cd!a}'t+>YjBgOPOvsV8zPd╀p-5 `^MT|v q} %3صFRԩpt7aDĩrܠKrF4zEL\% i^5~oy5朆4Zf{$Yv&rQ}%Pwh7b89IU sib5&=aN5A*kM 2B(s)FpCo^϶e"tP_ BcRy g.eܠ+XM m2+|EDZ53*|3Eր3E6͖c?(dglH=i-܀ŪኪC1k0.8[0 QVI&%q!Y2s Ju"\DUv@XaVK7bO\xv" xw&q<bcL<-DtBaRpzG3\- hxZ'>W@ou`.l| |&6Zk ΀YT @kp m=%TC[nt01@Xo@l׬ա^  C&+(Pn*|\z>NKRGrv$Y~?T:G3վE3DYAa-_#~(h1,K@'yf̫ qZqC )c%)跬up'"q y>]pV6]K$x&6_|HLcUa8T덝|P5 lᤋGS ZVs'bע-5B[9a6 4nxeDR5kW1V]@ځ%PJj~ێcЍ ~c{練"x5x= wd-b;0UTpk>+SҾ.?RZ\)1h26pƩ=ȳOH)~8ݴyQWgLa\qDݛ1n{3 ;>_Vpf^~w>ņb-Q]ܱAb jBI1TN ŧr{N`)XIAm6um'܂> y-k@A|4وw6xA\\sn=IAh)#r7f:G <ݏzt썎HAp`7*D #+j*B=GtL(5V%Aܝ$N$UMtMyu{|Ar'Dw|L :8Rެ uΕp㝤WwT L(b@iCK0_h%Ό (2\Bot-9cjVJvL+BwV>IsDRbl[ vF߱mr&t_+N,yAqUwY@, @Fg!m̄;:!E3gE†;HƶRNTՉPw$:.y6d~푰NJ. }误!- ޲QPyDCdtgwymGNy9zgSEBm @`<@d@u~ip1b0T6n H>:ɜTYL:7ƣlr»sJcS1}}T;]uTm0MF5Xx֥oU'_G}h]E| b?J5@=% -,Z=`ʻ+v۹`UkW:mоۅIx_?!^Br6fγm"s>rϱ+>J'JLU`a;R/@hY* K6ȟ@Q8:QlJM$˚ :ydeDSNY1`cn:U %dҾ_B垾F?e45'46ʘ<05I+R4,%.ZMAЦ#{XZ24D_Ҳg E:W L{i4qGӞܞMQ1עEmޯ$g D U4_e l!XIUOU^d_ciFL}@+c?޵(cSv!wpOۀ,_OTPg$_NSq_*HMm_ Cs@$MҠ'[\1zWn[ D,!86vc)mrR`ՂC1Qp8\@zŽ,py!&1n654@=UK<* 8VG ID ?S*3IA߂l%O1Gk+оjˇgv*B5ښeٲK8[閒@/_LlLXvv 2a^rr<e*JjLmjZ0h!c4͠ H]@p{w#ZF\Zy6j؛"|ֽ +&a'z ͳe#Q/zLZjlDQ_=~ nqfk#bSXPcO&7Uw4ī\ M}rUhl;f.)OS>7#S1+NT]v:~jDN;3%cH>MOC/r_Ug"՝Ekuw [I־eC1{D4Dh-MHh4~cY&VS^kjR`,P~8gp]Ii̩g@~{@X\ Hz5S `oMыh"F[4p>M[T{RT">SIb-D+ 0~p9>JF47]_v= nfna@=5^^1Y\Os~R¡; y Hc!9Q@\hp~Xт(]`ǀ5 O8Yq?+ s/q^.TC<\OH <飚DӪe٘8xeU@ q=tQ]]ui}]ԕ`,.$SQ}OOz[=l˪_}6 P㎲kK^`W̉y. s# 3PD7n T,+e1;0ROO@ξO$F϶nUADFڽo:}Dvvő6N:ju7ćn f.?T>2x|/$7cՐ_|F 5T!|2V7dKA+%@>Enjwyr-p5+ &^"~DJϛ0r_y>_jnD>!sd5j^Q^}H5ˮu?D&xM륀܎'D6R~l`"[G6DΛf|[jf! {+('q>pwF/%p JǍ/_7 3?=X}NRx1 M2L`"NaVyM9~(%`x5Wb/K@'j#4`/ AzA?ֻ X|\$"R] {8w) 0e fS5F:9ƘmiL]"B _1;~Slo :{:aSyBj) #Xd@  <1~k!bvY""րt w_Qԩ%gS 0n ,/A@z['P,)0|EJ?`š8B6iQ\ (Xr+ئBH)BFhQM &j -C h=qA@P0n`6?Bn_kF,m/`odbQo}]7gUʋhr4Yv\=4Lt4RۦEŤkBpH_伭)@!%c݅B|gZjml6O\/db~Xr1->J¨,.t5D2S )0K b~)Qi\(Z-:7 ^ecJKRˋYj˟@/E5JhW41-`N =;Qr|Ŋ֡n)Β@0lEŎmJ u@v߳^8WM2<I's(w8Q`Ě?>9?.!ǚžQ!%&V>7-'E*퐹}f] $)qڇB1&+eC"KYd,@Z޾8^~ѣRK>(ngcè|%|L0iQW 5u$ˠT?@yu0 q,_)b >-mX|n,_Žfy+kVcGR㯽Oٽ4%H'IQ}&X3Co5%r Ei E '葅/oT_Ely_23/@s+ 'o ݗ,$&ʺ]UZt?=ahzʓ$i O@AXh81Fkl`z8j tA6+$V cdTx7gdc8-`Y+U/Jn-*xWĐjg@>O | *VUP]P (915hђV]:#*T궰#RO! _^ !.PMu~QfߝБ'^ wX3S$h 16V W7o#InX}> 'sS >1P%b.ȁހ%,rj ms8WH0X\ue@V@8Qg 3IX.fnVIVP}#XK/@20aL66˒b2C 7 E7J= j/cJ8 µCp@':XV{>2YQYIC )R%%PW|0ܳ6, 5󣁒Nmjfn<Φ-IA#w{#cUDd|?7N26;B=wd#d M;û/汤':RIZq6fOO[znpZ%N1}v&yФ z,s]}@4F.ڍh~U4}<1ot[QXۚ|2@fr{<(νr 䐿quo [Jd_sTPLٷBEizߒtefvJ`@7$o:C]VƒJ p-!1*6 +~@uhV?źG?Or \T,DaU.ϯ+N:(ۊۂ/#KhD*+^5&aJW0Ȭ֕dB!L~>A/J-\ gro;OƹYȡŠq2d 6)W~7"edj`_O[T9)\偢M 㸈IΗ?TSf-a0(ϗWam]!E\tsvvuQ@!v!Ƹd=$v~Fb#PE@>yԇ%D :ebDe OESGVUmP=VO]%Dm~OH5(w=So>WZrävƍq?H .S"093Jp t &e b9WdLjo :7#H/33w(Gz(WrXǦz$BhHwtV{Ґ__"ު5&l}08JT\&zY*rU(Ilr nL=j ܞY y˼I\y(hD9 p.%ł |&[I<@ޚqleB.Ol>n"tLxFqӛvY:/.(1~U` C2Eacټ&{ɏ=8pPtqnهa`LEu0 N Q }fKr%v0>‚#_Q͆2B?UMۗ@_7cH9z5,nE&^;0/ŗco`5=q!%+^1؎dh%4/ 3*@{F/U+j5.] E9NqI^x,Αɾѹn˖nS0R>X&[&YmOK6 $oK8<mv` x>DAu\ahxwYnnS[; v<`$.=l`[ϬB=V$\vZRͭx?蛻biN<-銚x0dy뵗ߑ:VӿIc]ŒP{0xLpsߖ-);GIGSs~z瘜|D{sD*y *VHjn-/z팳eObf_|=S?69B4 m)qVaGp*sc&rlMtk WB3X1)$Wyszc0AP0DgN0N*z3ˤ BiR)x"~}۱ M6Ij4u@vP)iZbz&qR-5=jU-C=515?1<.0Ar" ,+ :-'ѓINi1a% J  Pnݗ)[ʨKzWHd,V| %r[K} jxUD s13G$z^2p]QC4ӭEmeL4 pmFB_wvL,vs:xQr\+|P0M}u9\ylaYqB)x"8xMa,Q{D&;=~sl>Ħd<<$|frk'ݳeXnG3V*J o;rp{89)ީ㛈~PjkLhсJQ ʌjU1b.ft.n)\5ZFފINpe+'XDmK`d3FՐЊ I|2N6Vi-6OWTΨ)cd)$/"0 0wyI!pVf8+nd #iVʯY,PWQsmy!ĆXd44[˛MQUO A kTDDHGNL@'  Ҿ22S z=(dP~yǚpZAbv%\Er^'#bQF [˓@eRaŴ-(/1 $kv?Ȧᖾ'C9IJfQ t dsG( /m<ɲ$v\u-i1:\L˂LI>;m۷̢A>UvKL\ XA YVD=O@ؓr3uKٖP}KT#8}ui(֧ /o$)[AȦGd096]*8358Qm.2^&d:s|KPJ1) A:vj]M3L(}Jde,glqd'A^ `J;9ti䜹;ް$Uc#~v2j#t3i̪4]?7~BȮrEH] pRy醍ŽgߜJRp!3t~-JI,Fw![l^ 5 #:}#%./!9T1& qː"L"EHD2h K^r,?,^)a0CM ] H3X}>zSTyEAP]%eǨf@zUlnϪi= ZFzN$~ nyEi+8^Nt6tqQl`JتD~?7L6w&wwǖ^0nX)h#_Xn8#\rwr4ļbM̶EHT4{ب]K\pF ]4@ ! ̾ 1))ਆƤuN 3ؤJ] moup wXD̬b[Id{%׷(sO;Pr-"QTԶmj{]]>NtmZj娸{yHdF$>cGީ%N{r<剹?M/!ꖆl/7nZi\.|z#5NjXvݏEWJ_-:{ y m)n Vԫ3`aK/F8TkKSGZ`vur| skZF1^f[ȍ34w&aOBh o3Jzu ,3ir6w)>i.ΔC2tw$M/9Z&d8pe-OPX pݚi8gtз[Gjf$wU?nZw# T-/ ۈ&*?De:N H{aEI/\c5lAwoHx[n41/\^fm+ɳ_.pRKGCnNDONseuPI3;;Bf>{OwQĈej6HfGb3RoM@dƾs=޲\uw }ث'}BA1YyċT6'Zs' U}0 j#x]Ȉ x['Jox@F#*$nCg@Rv?#S$D}FfV1m1Hll>OѰ6h3wUqߙ]U+!w:SD"6sD zf~Tj#$bO[·} qOR8 rw&ПH-D5siJde:XY$ix?3e<,RQ\7Gosq63KW*gǽ+k?N̈́-@rㆣG7!IZQhd/&T{'vsAsKDa$Ώm6,oćAWkL\vyZnP%ԤTڒ^DN-Qx' A&XztM_,Kd2B$a%%%dTREb @!nP!9ii?y 놡Zږ=4BLMGQX ݽɄXAQ?/Кo:u7!)*<)$uC_hp[b#70rF=n8V>pZHBWw ֆA"6B>Sd_J5T^NJ] s6)H lիr^yrMؤ|L>GDR2f€6|R:$7eA Ħ[Q84 ҾlIG<"vZ~A:{L&qa]%?w`9Zt,Þ&S(YJ-{ 7%@*uOv>BF!iWzuTY_o婈 1 Oɾ:}fN0 ;73 %`cO=b{ y$djr;zHr$AMHTwE*6OtE%9Yu{%S2zQ(,؆7b^Ww~_Qpuo_lP9U3ȭB ay㚏0,Ȇp!:г ryTٹ{E>U*.C\4\7҅nRmPE)Z4)S%/`aF4\=Û2u޸p)Ư:Y~Zq=eJ.κgCfyi^(+!p~eTL&Y?[X {p |@f#_rĈ H}TSiaZ҉lCF" Sv4K@B"ݍ&,t5!!9jp솇KFl:KH CkJNn&ctX{zP.($=;K$gAј19MME5mB`01<{t }&bn!rkfW'Wշa[ &8n*b1?|͡ y {lh3,nSu [#tMEHHT(^\@.b0qF,4R pS?B$d*N)UHD{%J$svcZj8>KNh0ht,1!^hm !ӡOvT8'kRI |_&F׫MHAH&"D٥}~*kA~Hm>f!i\jzuI#iѻPQotc$q^ FH3&:nfBi ,[e &T0G@ !iNWTQ\Pq\J{$!Qz #@Oupl8?зBk69G_h,˟InRmxs'L48!}a~ɘ]y1|L 5V@rѝ{(#`WIGiKerJEG4EgPi9?.%1PTa¿@j,c y%JBvnWf&fk35 yJUf'( $7KGqw+\$$F0Fd!= N,}$F$# <:a;]nP=Ø9X̌eR͚ヨ2:l(2 HQuF\hQ>,ܳ- ۟CȌ~I} M}D܉*"4]!/?y0Z) Q\qMb钆@d5#}j i{ T6 s|UfNJYsR~:@ZMe48,-'>^yNXdv%cxp%45( K ]dP|x'5;$*)>^=u|>[f6Bp:)`& #!jf\C/Mȵ3oX]C,T*]<oVH2F^\B%{a@䙥'/*KӽD=s3\"$Y)qm6[01M r.zH! bR))]cfs YDk {A 4!/Ŋ?$HKA?{MǔI}$h*_6a1:K r#߭Yϙ̜+ѺVShJ7 IdE_SҖNzP\Lғf-D4* 0ԌlqNuԬ8MKwJHWE"o vo s׼`O!1OntyD]$1kEW@&CSdVE^Y?' ";w <3tIq^aCط,]x_@MA7VpfBa< Ev3DX:' w2k!|?rfSomG^,(EȒZw&PuB =q|th?֢Ʌd'+\8nY 4Z* &ԐI:b33y,om[$? ѤV9e=k[0[6] &JuSw!mh] Z" W^@Xy-j+r2HԖ;U3lIv;7{Y]>Z6AlŽ]]rw@Zw|HXχEK\v!$ꮲ5_fq8x11tM',_1S6Ǻ/ꔮf ߾_ڬb뢹K1˅.9m1nJjC)p弋6͈Uw"w!oYQr* py !/1g}m9ш{tԵC.xӗ$M]o#kǯQ1F=jaz-7MȰ9d"I\kZ gJN3-0` ۭj6 jP) I@lLXǼE_:Q@;ad-9'~\`чDzvHÉ ݁J[BVI驀\Fq~@mtmh<ϾGoU|]Q@园.Dn5u tf.5rC/tK& fݜ[TG$*2B@F`? A&Gмi Q 6ju{dΜnӆkhz"3|j'4IB誂o!I"ߜ\q~ĦY9*:m%>+zgTG*Ijh[!<*n 6P@o}$osMѡ d3Gx_RҗZsq5 (&CkbV.ؿ4]g!0զѩ PM> NG6^7dNݔ*ťAC&XiUB$z`@&SHGze)'BL5TlKѻڵ$ʆf~rtz'@y/K鞛r*$ l@ku# /fmmAax ɝo)agpu| #>2H/EEY\S.AXAS{kUj0#I&붹En$/h #(5(Fa1+=QYo?m$YpHY2 =`U/X}Up l f"4/`68"!s{Ղo:!9-$g.ҊһSJK5s4ix8D6iSUK((FC[}CҠilsxބ8zH2Q뫘&Y^2KƳF)gem*C`%Vg{?"ɪ9@jGD إd~]]BP̌06&-\x[/E\?d oPC2郀SMۡk nu #T { ,߆>6(kʛE`t\pL]QH=sP!}xC.:oE[M[x@RTgF;t@^crxϔ*X!+BC-ǹ@XPba r_:DhB< L\~$A+ X$}|#gRwCPc7Ə^'\mVLFL߶KG0c*ܹ{ő O% 34pcBtnnY71Y 6Ȫ}>4S:IRyd`b׍fdEg{[GC ]B>les#a-I_Y.c#$ԪB 9}ƹ4po.CZlU7?O٭ CeO"ֆ9JH䴏(:%}*B9vw|gR~ZF8QoH/ʴ|>3efxyԃp0"gFd#3 '4۲Drl>Q at NU9kaxke@T Sjs43k]FČ9dp4m1Y a"dnx-Uær" _X}9x9$3ba<{-(o0PZhha? RuP.wJ'$=4JTby? [Et4@4.E~uԆ/RxِWFjP'b&]ʷ]۶%]95JׯSx䮀{_Y-f9r/7NT(z䭭\4sJk!Px:t7:tyش\{MU#F6Q DB龤ҖMX`9Uk-#RҾΣi{-C Le@|ǣl,15o@k_㚩=KyyBN$KUz{\=Q3.L@b,>"rm@d`*VW^; B<"*&^Q[Դy5ઞ~2׳ b{Ҩ^[Nu3}gN](JƏ~>nLV`%<X:l3`rݽ;^:K; wW _Boַc@L!Bȴ6wE<}[ *nU( 8ۉRqR+86:* J4jzh.xˏ$uzhk4A,Xkj.p` 4ЮH:jڰU5qwA0+: icP7|:<dM=KB:,%V$68U-̵in^$\mP\=7kRL >rYNւ m+(m3w9MBP)Dj !aXHMHa+pSA+%dȴp&\\E/ˠO!cXAzCEm I IFGw{y#S+ZѸjZѸLEݦ7OnNEZo^Rj-$+DA&I=|DߑqLXJ0ޔ"#M:E@6SJD)õE#JډEk.Y]?)ZL)ZӍ=Z0NٞK7w=qP΋NQt@NToW'0`AII_mTAXP$sKy BX#5 DkF}~#u2 LJkEiࡻ;Qsh3`~,q{^)g\_%ZpTЦV< ;k<!adH4k1"Bh8r t#7*RJ"9 -bY.X)dZ5onWE/f=n]x82eݘU_5,J1ĺg컲mm];ʡ-?Ĥe]AHO(.`FTm]|p+~N u#YcoU(ׄDC T1n鿯'ѡ puNU7S&QyFJZ7z.dkNlc9&vuwc߇:B}rafs*8|,&m_dL'1ύ"GDgCI1xW'ʅ ރ씶85#mOL#.7Xּfo3Eb5u"+359XB !fV(ӵajPQ2C&:@F{d2*@b4|3D*"gɎIH'o<=$eEA=­ N`0&u#qU_SQͻRHZpM@vQ8Goe`\gZTeL) Cjk12D ~!K<@ǣ$$bL&gXI3pQ`;V+NHA |LsM:+ Ttf  +$vV|I&x B`+6N9Dy#z"Hm"Jb+qב#y< :s:VB쫎5%ꝇ [=jddۍruJV:fޟ鉨.?Z-zsaRc?pi.}3wkuQxBrzZ[Iš ,@&kb*Rf~\GUċ|2"js}D'q!e7RyHacNp#Naqk:(UJ)PqwIY@/:zS%SF84p2Kwk5[=PDm8#P"| r 'uݸ$ȏ)f.%18HK"(h :!ә>F?̎ٚbx9ᅕDN}{7ܑRHa9t0Aš!s(oW#]ghnͬQTEh2R P;Q9L<"De{dݤHA@w a*z>=j5"{ !!] ,ƪDcyPGz@>us#q)"G7Ya!w-[Э<=fAKavS0UWKf"$tJh1җ1J>ֈw0/b5VK{7T|EX2qU6kyНoV({m~EǪWAwȌnxj~{Lʁ䴤8 w9d6t"$1y# K|/huBDgU 9F1OާauM $:h8ywhZw8zBҺ@5&@U"W2wa:Z1r %Н5gN4b}%[R*"WcIj¢{JBbC&ay"%׺{&I7 d`b?UAMWǾ{~!}6Ydk!3&]T_F#* "Q&zEb8A íAԂ`ؾdC0<}fz% .B6Wt) tE hPs"0Mri3WhEq\eЭaО^p"KM ܶW@4ظ3v/(=sҼ 7wGn4!ٞsnrۛrÙ"/37XE=]X]muJJV;QrlQnvvN`I$ 3ݳb"f֧-7%}$R-g.ПqQѥ u[tHVC}*&E 5E70 K@?_e #d}|A; Oݓ * [| )}P lӐu^CIn+U3+3{dzH9F2 QXwWNψ^:?pή(h&f b|Tm<4u,EnATpձ=HщWA;pBgyeTܮuD@mi! Cm>Ip_j;ϑ" L,D{(+(MUyu]Js6V-{|!Pb/p d~}q$xZI3Iuײ@t"6`=z['= i!Boyu!be4Iw{s)#fyT>ch#; )mHj Zv A4k=ݦ 2|Josj*ˇb&]v"őG0/Y\dY$͟cد!%}ܰfQ=ٞ[s/=sdYQ=.k_ƪnBvti\U=#C5=RfG'[Hg\=7 CN|He`K$йDV𛕨*i bAgԷ}!*L:$PٌP&5) &mК]t2hbd%g]kx"C 1!j#TwB_|v<{w "Zc >5Lg%ٷoOQk1bepSH"?Xs s&l97Sy W`AhOX߃G9m,c*?!/n.@%5et!cV?r*qVØ:T#ky qGW?_;p8]{TvLT ^@wR3^ѱGbt3E.;!z14/&B-q`G4"b I&I.[S^uDk|qNE_:BGpw=x45 >OJㅂa\$%]\(Q").ѿ (RµRis[ cUoNgvF$$n-3tpW4Ah8EabFO!{9Y `3_ŹTxuy)ؚ"+!s]ipܑa \L¼WwW]kn_  $q/0z|I'jn Bw:`i˅^Fp o:0]3P<eڬFF4nE(HeL[:#_#I7^g̦t#Jtg$+Z1ؑ[,#Cё^0:mb"/9bohnm dx)=1`rr-ý"'8Nn؎p\uz~l2WeYtn|u ^Dv4{kp{;yOgzdx-ecܙ|s @ހDnwXgM7g 6_]tёb|ۋ1\g9o^ݩy]8,ȓgmx~A]~}W?mӎIP9pirWpns f?d|+O+-zgJe½qMW @$[4{kb(1Qe0N֔6@'gLl{<pE҈P3Cè@2gLc7j hxT(^k!TU8`֯Eja+s6pZ7Tdf4pVtdS5z1ۆa*A0v*;P@>CF!yEa7$1,dq*d{7I '2y˩=#gl`3`gu8?BbE;@w+n#;fmju^yԽs\ a6b|?)Bkr4cן%A\gv.BJ pNŽx(;zƊ_902WD͞r6eU{ЕQ0*FPF~G0 )Z!i0feN ݰN3!Β|؅~P Np/"W9^£xl-n7ov?ɣnXHĂۚF)Pxo f^S℺ a) vi ,fYR KQYhj_)5x}g}&uTh 6 ? Z5-6"jC$zP4=bpjc'Ce?SIKpU~wtK&7N.5ְcI]Hbn ,Eh!0Nc1wGLUI=(L,GyX B(Azd-p w+ -PGi,1#( /֨|t&n08ǭ ѸXE(=1vH3 lsF tgÑ$"84wbZcN_= ιаs@=$p,.&ԲG('YyEx=;`+se1AI:v=\( [Q7<&~e6SqN_&@9; !-/9Vx+zy"&NG9BsY̻<5SE2X;kנ+fBUoKx1?lƺ'>ố176r1}03èOQBM wz3(7/˹Duie/˹aC{Ƕ8<(T*sxYT'+gFoF ƉʯAX[rrCt$jmS? %`|R58F {ѺGkܤy4i럇5)@ԝ%tPحil;U_4 gx(?f+.PV^m9#{EH{lFQ8Z=>nlv]Q!:W2Y򣵽#!%pw6Ϲ\={Kd ʝŇhfO9lBRbsIR:0iR$]͖pc.&$W) чML<zI/vJz]z L ~;j U<uB{Xʩ^딻u)8zFa!3鐧xeGfef%Et%涟ۘWu$]o_cMהwYC2=c(rn-Z19'.劺9h*1nRM(9rt: +Nz ꒧}. :m:Q #"z+Aۍ\ݮ |Qpl >`t)L̐ j'Q%/-$)eVM5RrX ^"sE$Il$JB< ڝ\dI  <G9p hYćQ I췐._ M܏K QX9ġlS Phc6`|cz)7åe7jI$qwꐂrtS!!T'3Q7'~8Xl]e$o*T8뮬knJ̻(oj,!fCca Sm26)䖪d6;dblwp'kӉvj~؅ϞL;e\@{/?`[kδ;CAcdA!} M76$}k~ DoiN Pҍ>rhmI;*Bw4 0qA\FD8TsSÌRfjp"濙Tu@1;$c96Ef,g3Qfau/}M)L[weM u'۬5]<VWp>UBĖLਾqB`UQ2'3Kiz n@S<ʁVlH(f.P$'ة,PL|ǩ@`ơ @Y@Ji {"Bmj,`DtN,/q_W#% ;`:`uWh LMx+rKJӄ@N٩Lofc'7 &Say`2cbh%QyAxwR| 4Ӱ>' 3ZY H|}8JM:ۄ_@߷\$7EXHĜ]"]=Add+^z2QE:cjM[)8"Zn&i],@uxAX|Zz~ޑ^ptgPtHQ+^D_$7є/uNq MOYZQdqM$ 2 t|:ƞXXgBaR*]=&M=_ghBv!O3N [lܽMNY~MqGpHE„5~r(-:S]ؔMⒶ:9%IXFzFh%,3#䙛ub52M3,eQgl{U #QԲ6VGXQ wT= o[{f27xAz}ÁH>zfZ@7c!B4]vH«EO2sFRO̜ľ"b7K}؝%2YP`Cq d@rB:IcOĞLݝU>6 ȥZ֗ !+dfKoS8]nyG5 ߁WwB"zF9/[_BRxn J>Xgm p |QDpwkU:p?Y?l[.&2M罺T2|L넗N'E@՜yщ/PJ+"/s0bS{cF&"#)4FAIr_3LbL6O/c!ŵöغڭuxitd?+'6{'F_ CYk5 j%)bɸIQo }Uw|y #u'MƧ'!ʔ._ɒI1ɩ[M6<V O'alG[/~Ea#-qnQ=Y^GY|Ԝ1F?xdlb8<j^W%nÂKշ_r橀{/ NjGeMY1IY\#\1 S W,\1{hX$,'Js.PoPe `UtACZ]pv}BJ*4u~G0M:k4'VaތrYԧWN X]'b6*f^ / gc|j%$5K>( Bw"½fu/̓WFzkM("y%`jwFej*䉥%'}s%צzXgyzGT۾>,0-a_ufyؘ 4EG_gD됏XCW3Ihƺ%kq. >|v<9S(E䔳QBI1Zl Ty՜>jtiulU(DH|=CTh`_%xobឩs!L+:)d,D oY?esɔ$Ӎ$!ݬp:6H*$ c4r.<$jb'N#sAxM)|i7jHEolqd̬:fL}&fe-Ҿ]qw$߆J\oZ"ɸ7ƌRlZh!9c)ydPc8[@ΠCⳟo!X@Ʀ7jw5;e6@}Q!  <5Imdw\_YJrC"YDk=;ATI;Ah@sc:Y_ڇkm޹p0G^淶@Gzlu#A0+iO`c3.UEHҕ8Y  RVcRVY O{䘭 w;ojN?(;#Ωg8nNi8i7aJ.UDz!xpn[j= BG"g 8P6H+Ț>pp>Wܯ( 6%/k'~ `]c3z1iyayieỶXЙڄXOUKFrt ݥ1cki^98D&[ծbMc4I1ޯ=95'|=? A, w"Na *b˥R}'2@Fa@5t Fsf3K5SdP~, =3ycFuEFƖ.؜5PyJfp+d0H`&:J-^B,Gv蘭/ ,pʘn-BE@>f&!(M>f~8]3ha,KkdG{no+=rAphj) k5#J[xVղZ*6) '8-RkWk [;Kɹw-+|~5=`w.p2lGOy;b_F β0Co:?zCChY'!-ڋ c%%t8M+h߯IxAM(u}hVw5gsч.!o6Nv2,꿝'b^/NЉҋC CN[Nu:ى)EycJ抨VtR~ȇ s LVက.jYroNx'΅̂3p~lݡ|k&Ө+:]C~T/<5 GwBE[אr̆w!ccxTaYUaL@qHn7X#G:LŮ#' 7t]E0Yd8?Bz,#Y A-}rn 5|#3PFnG/c׈y /m"Ό7.걪8wRAV]H\mM";n|hd8.L 겲8wA6ڗ[LBzߴ is[@p$3N-3=}FA,sc{ xTf#J` MZy1@cC]QI#!$40ΧN&AM*-\BQ͑& Zg,Ծr'q.r6LDd|hWM !\p;Ӛ^,g?S(%'KT>|=IA#:oHgӊpӇS%|a0w86xI:|6 rCayC9 2rn~0LK"&Xa.U1 h+H9g#_EoA؋wỴ;4yԻIkCpq~_-w9Lj(lV$bTMWM6WG!6 [(ZKMD>J\Hp _S$دc e񤕝 :F@Q 2 :=Z>nHtM @]JZr Y_:_*9۾No ˸Nw w2_0w ~KM 9rza폍C:y_@:+h7'ӕ4}1"8jBDɨq|}$IY}@[{C覚S|XB`@$CLӒ[a-i_kA kuJBwmf檓Stk +rtf^5%`@x}V]6Q{`XdwE.a Ah##j Hd}/ӌR'Mfa Z3}֬uy!_[@mecݟxozGYGNmlAU5@RFt$4R`;VsvWڞ\P*UM'WLGFAʟ&Fs0\pFZU*7?Hz]疃4=-z9uР2BמPEuFEǦ5.L ws y$e4RN ޑ'!KI,@@ly"Z~6klH8.+R6Wnchΐ&• Fڦ[bbD`"(Am_vћV"_@$ZYeiІ'r-T2:I R+RP-*'GuaQv~l/O/`=˱VlٞI3=*Y-qp}sΨR!^Mh%30E0B}L(̙ۅ/~9`o:[WGZ@pML`VI-@ 66'Sa*udh.aV5D/&g$r"ΐE(ݩEfMxʾR@Z~,*2} i UrPZGYmPLΐUlnt酴!zQZ uGcnE(+ݠߙwq|#p8r+(N.)r0 !Lmjb!4T].b@]:cȪ۟< %N'bW @,Wy,AK48{WQC{ʢ8:}EE3& !)^"R0?ltH)@0Fw51gB 9 XN Un` n%{%"1-I||:eg@~ E2ɬ pk1ޤSC~!:hU4NkCWi ^HebBz0Gk K ~cmbw!x@P$kQN {vVޘ3|.p'8B.`|B{ z\ߟ[W4~`rD5ڷL>_\疐 ě0t6'z(@HA'-i,ќ!uI#}>)p/n̄Upޙ Yɡ|{9N+ĐM qnZ;T7:EBTyw\q^*8,JM}JǗMP\DV;>S؈Ir)m|=w X,D0ulX52 uWB@޵OPΡlt!%`HHN[Kԣ}Etv|2C"6 uDQ (l"S}gæ(PP\!@yR,5dhQ} cR7 t[t%XCHwHVi3'%z)7ti)Kg֜Нt!EC dT,߇vRtZVᆱo JYs7u 78)W'`N < %*wp71p` 9O<<>M"`9GC%47AhY2꾃*ݰT pmjQ'qc@A_f?Z,1 Qm~63};ݙ UaCZLQ7c(/2һ2k]KT~Ч9Z[h ;Ld +Z-#y;Qyaέ@ 5E+8ԾQjG,b.QI9W1WwOmT$ٲ/;@ԽM̍Jg FjJKH7k_5GS/L3Q F,dJEbzZH5,_lB)} +b"Gmn 5>@0 )[8>b6I/cqi6S͊=a(:w(S+%x#& NmȊ/iKSiv[G=E{ ohzUp։ֲU\ͥF5'@Y7K mvX=X;-زvQ)A/rΨ4v|ʑ2j'l4 v]1f"ipkc5JJA@uYBZ뮐#7Uꊗnk!Um{ >0h?l̛:ԅ&Z)hԢof^ nwJP,?s)kPZ$*ۀ 2T{]  Ȑ_`-Xe|E&ҙ-?^MY1\j87ǜJEU3RDaVmY..@VlU*t7|{Z{{3^ivGk*SȖ;hDG1MJUP4noҮpm9iphM%{%Q@1t7MqpT,D2”ظ;!H"Y] @ |K%t4. wnDoicvݘ$67KR4`z΁6&D^1!ҡ`N_n0nc XR j;tS1sR=DHJ@t9EBqF?nߟH)"1{O3qf#{As}3'PQt4@^8h Q9L74]nhv_ ->jyCx8Mټ ZlYµ.$ۇxk oV5ҽbKlqF fŸW3t-XBvs%T`muٺy,%PE<i"qU>ݣ++dV|ZI oiqKaA}$}]$&0.2:sՔu8sѬPrdi:|trd5/tF/s;(޶ >*NN:BJ}٫8:pQض: u?|0UZq"D 0k$3'ܫd^*ʩ|_aoPX=|JGL8[ Ւ`1M SE!5oQ9L*݆A?Y -$SIi[).ü+-"$x(0GCWEثUJ4k:L}'Հؼlkyb[JG8nb䤱 QU&]@ {XE=ºŦXߟ57I|ߝkm=-`LLg%Y g}ÃLyB14#Ӏ% pqG\S6܃Foc{tv"h WxPyjRP{IQwlHH^?wZ[Bcu"9eB{ZVEkW1סM:ePLV[wq&|/sP kd8%`F>-Y,x* C?\Gl(t (} ^v=ޥ,;Ҕ)*6CQ@>.޺dkt7,SQ2=S@\ y>+hDW[SD񹜷K ];أF6w )Eӏp1I-*OnnE ;gI{=A݂e r*7B1,vd˜ki#`{6y_!#@ĒV:T#mv% Z-xˬHbOhunmfFk{oשBl}z\xZN* vWӺ A}}jITY1>zd@/A 9v0w}#* oG(|V]uY&~0ky)-<[|Ӱ`g(g戵?aJWR$sB;;R%")e"5m-IIsW0O׶ƠpS^]WWLRti95d5(_;Ph*Ϸw91U _(ɼ48 9_>yGE} wh%N k޻)xz]qXmHslCsh{Af|c tk'6/$QzW^Eb@7ݒ,k9ImctvBWuE{ڟOf4L#BHTVȀis4GL sxD~(P/0,ٓiY}_s"llnLտf;Dr l-AظkWzki(#L,4 㵠ץIgc qnG+wb6 PU@4^)_|$5Ĩ~ xg a`UlOYk'ox@yb+X0pvqƬN /ӱ&Yǻ\+/Z0IxL j8͘5ӫri\SrĆ--)\ֿWJ YyEٹx=#e' xKk]SA_u =d3P ~uv㵡Nm@a)1xHGĚ9]]ѮW4Gi}9- !]udЁΨ ^V vr~IʍsKq]̎NnDϗ(Qk$бSe]ٴ#Z[H='%Jzy\8pE4ʟ|͒5,%~pg /dMb!H >wp4)HbŴQ[ MGqx>:<.í'%j뽙̽ĦV3O[F?gODDC*Tyt]{(GiGyUeoe;j?$" C*(C)jI9m%Z%YU_8Ryv|C!!4tU}bZl(D>tH / k `K]Fa1-v`Pi`Qq-"E3ݢbOE=bpULF&*מzgWI^ ɵUl "ÜOCal,8dάGZ~0=5k=26y6K`":E}8*hLBuro/$B v.0zݼB9^4u~L .YHHAW?}JCG_21v[>ݐkP ԩ#6zWN Le.thXBB6 0+ND\JʺjxdB[F<*7pFMacYcא@-$=ae,d=<_Iґ `v:VJ7Y I_i7+kN*lz.ݖbtRSr愨\9yKhy P}4q[ifLzS2ʫiJt>/:홽 \lx,oZlk012)ŘmXMnv[6F@]l=^ DomWYj UQ5A/bCeӤ?`#}/U.O qs/Q ~1")U)D2xN|bi*;h{Y&bJOb &85:W<:#Hđ]_τlDU'1udNNÒ8¥׻zr֎%yr+>.=}G XBbaPyQݥLa5/Pa#A4s<` Ϋ;u>j{4t >JJ(} [~qZIN DhGҰI>UTT5H*){E*ki <@~GȖ8T+gךvƆhžKe*|rHql-yf@OQ̶>f*+WDYfԏ/}8sq4FJ>dXa-=0ce?+Rtv58-b?rrV㻎MR*"z&#g['9;Eqe&*h"uPBcF=dg$}1/zZзy'Kwp"h:*o:Z)^&*B\Bwŏ B{G\[ -}(xa|. X7lҜ#wV7ЉQ5 n zq^ -8h-w.JJhGmp^b3DQJ<)دzkRs0+&-^hP6,\1fcI(mP;Xo2FΒә̄w&5UIoL`*zB.3b@6gP;?f{%>I9rx:N&]J_ G'jy[eY+pOZ=HU@l/M$|(c==^@cx } e-Mj"Y=d蝜^Bu@4 e}Ppf)f,]Wfhx?rV4~mUhn_gNgAD`;'Y]u^%紧ƜGf.T2q\%pK2UZ5 Vo UU9tl,r w+&xR;~Oc:}HO!W/7>31J-uҟ4`U֎ v]>+X-"R>[s^?XSmFX;)>TxFSxEi+UgM>N]ݸ)4&:MLj|5qu\up6Cd *D&Z͆BtGt,L i WF2 3~U$zk}^&~ A[HGpGK!ͶoQ#/شKPJ3Mh  &WsX(8WVw(<$%$L` rֵ68+ 멦Ud`--Ӱ* {۰B9x$WiJT? &Gp2BaT)~6JNdtl/h`# pWPgҿ ݥ6W>δzv,T:LG-Ufjjc>|*Ts(6 kY6LP X0]%@JVBFu?lxP^>6 [y~˵FFidpTQ|^Iȹ=4!e|APT5E}11AJP1 x`B&s|u&,"u<D_ܭGb34/5쑅&8{EƥOW {`P[zeP'+h݇fi*wG\QX;yIKᶎE72;=.1xi%\IrNmZ3dW CL/:Kv_ҭ\wn]!)MN-UϊF$Ee¦И!Af_moEժdqpk5-a!͔H?N gкNXUQFX1#viԁ uu9rb`:`D n ! ㄀O3ŠoRޘӻY ~ PC܈R!oX֨PtBAADq+zKa=6kL7[3@g GTLi%D8w5D\/@Vbv&k= :YщoJ72P98=æ)I]1fjF~F厀\sO}/l8<0ܳzgFOHJMfmyA@8c񴝺d Q+:^z@c-LrNʚaG@(ޭо>W#!D*(w[!$BX5D˲S_%ڐ4SQz2sz<0A*|:UN;V+P?٘HJ{I33)c^@VskYM{0BFklrdɋܔ!5)B ؒٵ^ۡSa6'aPʋѺM"fHVq#UejL6^hRgGj|<^%)Ѻd@c9 *s*CzClϴjD98E>[Ɍz}}Fypf{ƢfVJ=1r {Bp! ;>Zsz/*eK)Pxo3Z4~;`H k6JAd $0#^&_@鷥3`^_=BPN`V l=(K%jrB*H^x:utjyQkD@ڑJ؂Rd߻!=^UAۯ #GOil5`޵ -I͟]@-DՋX|@e~[6SI)RQ̴Q s3'trۗ@nci| ~B_xp\HDSYL.{bG-=n XhOw g\ɪ,um o|)E iO# #5PXhg˴L5]: lJ>|`Y@kU08K ݐaaczvѧ8%yB I;/UKum:78{kt5&BTW~-H$ qbGoH%<ӈH!%'u vm>j~%oDy`-^{\ jU<[JJ P-S?w/l>]oZ_c r:ʯAn-enluML24M⼹XziJmXHI0fI6l31Xˊ>sݫ 66 @"e#a3֝+l[1+E][& IJϝ(fE $ PelV*VzUIXsCg_Ôygzԓ/(:b-}'aa5i!(xa>Z lw{RJ ^aRY9dpi<˝D(Zn@꤮@sNˋ@QH4yOnۥ03 ꍒ =Zy?XJ4@z1$dLpB@犹@.z9fOds戴Xxm<=}|*G # QA8TpHi<|\yL@XH(VctX=UcUձ]h4 f7 3jOWsq=n*Ix܃r{*BUV:1QNYgwݷ 8/7$%xFZ|`?h[x"TP D2``& f42 {_? f\)(.9ulо[zg>=?aHR\+Ʀs{Mݫ) @] A@, 64XTL2Vǭ.>!M Hz<DF_Sk5t5X4QFIZFęEwC.1o_P'tXZe2 ZG>doGO~.QnUNߗM8@O[ֽWCT`9k! J/ .ZV. `q(bD|?UDx>IȐ_cpBbFQp2WDI*ʼnC#քu c+T WEV nOTl%>'"py#x"<"uÅG+̳X.*k1̚5oYD]D>1vPi2yZrїXOb} 6S(q%eN/lcbg)N?1M쟣B%Qt9zs6#!*as8J OѣМȪ0>PfJ7wgD0!汓wQǶ~bVR֊B6_ "m 9Zt|\ 3½"$Uz%}H1Ar7Wj2Rȑo!]҅wΙBuE:PKΘIo!WGoX2m/_%S˄ )!=Vf Ȧ?aS&ixL@!l@|Foty-5>(%2˺w2Q&gv oX[{̟q!ĊPUp`p(nc?c^v͈:L @Kԣӡ$MN'$Vʸ[.Kgg.nYoMλ@O}EIrgP݀~GGW/}J`bYg/OFƩ"˅$F& y BSZRKE>`Ħ窔;y3Aͷ'h@8tsLTt @-ר dhfM+!W۝H."Ɵ)|`L|/bs;"Kf4-7c#V7;fD8!25cj1Ѩ3}{ΰpcEv'?G֒qU%98jrƪ_וLn+X!0(>³FZor\afؕ' =W9}~lX]JkaX]qzV셺} {D@~CB-&JoFNէw] pXE5;;=.P+ɢ$aN.Q-84f2^6n06u;cn"E?t nܱM[<.yz[U* nA3/lBl6ۆ Uigv}u0u_7V 4!B܂*b(u:<Dgݳ (+68.Q/OX zљinA"8 &A9CN`\BQPU0('sEcStuU3YRjT&a]~kSMt֠D[H(]@&n褢>Lg~El:jeZIZ77XLaIQt6j4PExSͲ D9.d(kJlk3>z +ؔ#c0m~RrфsP \<zF`qOӶRbYˢp`s JJ6ʝB@}6uN$] TȻP +z T9W5yTr5x!ݎvۍs^qE0h7NgF~6ndhFv 2:^{m}Jkë?lY" jt6|w[@0*U%Vv #ED,`232b}P1@_WJ]y`53D]`e[wG~* :YDCAa0U4p4C% *$dC*鬗L/\DMp(,8Oq<Y6.y 5J"w׀uǪif_# h' }}k÷6.=z;ќ\Oo;R+d>Z@Tos7"ȵ_K1|;:timdh6!b֏+R)g_费2a!丌(6?1zDqsxfR k; nhmD}:K&ů[!L#w4 $䦷Uwސ +MGDM ㅼ0Frq{%)γ gP|deLq3Hk$dX4mmReFku9g5ƾUj> mxg: h1VVPߚAkز2K`4_S}y~ uDoT ֗bܙ0Ez\ͦ/1z&sEgV 2D{& "D[3lE5!o?qt/eJf{ՃkD`d/UdI<E$wo >d 6Oұ*!-pDB, 6hũlքQ`dKmnIA'n5E<~ YCIRCЫ!SS td"V+(.GI-\Ci x~~w _%7VLƉtqtspx(r{"P?kXTsoԡyO[1LtlhS2}d=+5h\=#w<``O|*F]0qv9%xzǽe#70P0zm ck_"J;w.4;=x,C )ܞ??PՎ4٣~~ҵV 9Qlk!\0Źbb*hQmgy4LjwtRy!0$X L8ϱ5~-\F 4lwp.^Zܛ]yL f[ 兑WKwo]~3:؍v&: <6|v])Ĺ2ꨔd4`ęHH-]GG|jr`ZN,QvъI4HkOugt\O'EUC-5}JL& B'׾b%oߣN4ɝy'0`lDp(䔧'Vm9)p05ɉt%pYP0N;HYjh钬~:ueI<4`#X{^PIvoYSt +/.ۉz(/&VތVp,.8}ҏ0]lV*zH6QC ]dtt Lp"Qm07}x lTk:%K*W~23^ kיPrqA9L\Wz`4^`6|ەPY$klXQ=#7aO֍,V&XqT"_3CJ׬de!i H) @1f6f7h"I-~]k}sTS4Q:%MxqϷLꈀ;ktUwnQDmGD]τ0*a;godt~_e\Q]r3[\E,;~5cJmBwzvQYqgQ3h"oDcg8 41YCUk> >s>مf^<~m)m42$PԲ]ꉅoHU?$79: QovF)mŊ潹Ieũi^GŪl%v K 41J(5>a/IH}j^v$:nʂN[5#M/1+鼁8!ZH9{˵H9⿵ 1D`#Pâ@ DQY224'+Rt> ľT(6 QK{K(8RR.b:IWƘlXD-4TtV5UNĕ`7(JCǰG _ +AKjSIfg"2 2m TWSxǗ2I?5Tw3h\7a+/d^h]FA^~=h ~G94~_Ʀړ {l;ec ETIO-2MͦU8=h;/Xľ Wݝ,+- =ie|;: l`OEh{XqC,I[걈(1GWEvV;dn$yFgK ّTD>]ZvoX1*3+* fH_1wbHgz 쁆d-KH##lzljt}-vzp5U*>iƺn'zQCE\5"[EշmK\75O1 afI(+TQ|Jz'amnk)7N1ښNaE#`ːIރl=LE1*.U!-ͤׯ:N.zIڲ㥺Bd՗F\T#਽P.f5Ukg>WJDyj  ܜkS0Jv,iXQs첵> 8ߔѭ#X:st[ۑ$S>#FrR#'Dp8}=6'账uQ ф=56 lI %|k!ۃ+ ]$)W ɧZ͞15.-5YA{M!T`Eb%3?]-昰k7j& 7e픟%ѵӍ&ީ/f8&m/&gQhn#8qV*@eˈ^zLŞaQ HtLY;)E+eywMD|>uWJ+[ MF='\9imv x\@ELp|e58?q>I&&a :IvEwYZ:3>D办<6c/M %\N" }p&u`2 KA$!×MTPtb{h8]V,S‹`o[PP|3QB`0KؔMYeFr1*.A''IK,ټ4C-XjW|j>w)L4 ?4SP*C|4WM$%qAy˱O=.jT~O#`uN' s B kWGӽR ܷ`87rSK S(&q.(vI a| #BΑQnTYJ Uzs>oץ}%!$)Hi|/Q[jt#xFbPDC_6U?hаNø#s$s$F))T+]?ҡS#PM90jO(d9ߊ'å(K9?VL>I-DkoP奮:K:f5ZQ:ͦeMӀq~h $]ы1iު7͡YݗȻyx23 8/ٺbI }ჺ+5 ɨ10lsDhƚ'7/1'nJ{*ʃ6`:?& p#t$M$QcO0ʒp|~mNH/t]&ݘ^ʾJRR 1"hw$Ɠ%3jZ=uaI$49 E6SsP==ܔ%|ȀQ'GꏌpR'n!dnΕS-glXD96|Qm`<3{P}Wv pf]? :A'pUқdmX1LgtKf2Z" (T0Yw~(Ef~=ZE˺ @(Ab dyq}zZ@l3o AD 0jWE6dF^ y*TMBM*܋6@8h$פk^0cHNNaM&70;/银C+𱼀Eˎf;)}4W9!]becFQDQ@/1kY&v(jr͕ W@J`?]"O-uEWL_ wc7Pq `;;Δ[v kIvViG`\ ~2Q}c8\/,q%߄GU|T.@EVCZxl.3o?I_{pf+|EO]0=;cw4-0[T]#n먅LaطO@}=(Z7-y> CƎœ@f pK XB5=7l4ؚ b9gQ3;b/Mk0vEpD/$ȁ8ĞFW,Z8L#FP^+wOGJP TD~:ظ8 -!GҖ L8%صُ `ٴwro8tyQ:4}D$2d 7R*:8 NxĊiAI؇~]'IY@L5ANa4', "%De`nwC\yvt:[‘8+y+Y@Xwv,Ya-^FoGy ` ('ݣ2p]xåi_ 4 MRؖ/di^ڊBh/Ka~ڕISULDCKX[q)Sۋֆ$Y#v- O{U[p4{}zC>]>qn)xlo8@-cװdK}0G|~6t,vt9%hNv~yrc " WQ0& ,y*8KЅXv_ 'MAƔaI6nz¢jk =&)D:3+ SWQl}Sc("/IϞMXxO !n"c//*×CD@}(7@,D]N!*zޒuo( Η z|@v.<{lxz.nB4jl~4UЈ@y {ugf[#fk&I({^@ 7C`ɉBM0Ϗ" xn1FT{ WH_v48HkR T /j~]5U|UD<ь UE@6n83#ĺ~x~OQp=`Hs'[a.Vy]aeovR\QFÔ)3 XoYq-cWl+Q'z[pο=^FtξROsĩ&[Ns5K2I)Q`o?ey-tPH`e/JNx =3Z:Q[vZ:QZǞm]&5mțN%SJ_g(=>mV L#@3e}Cu_Gԯ~>}d-A0ƈ@Jg*_zEF2\=l-Ky__%K@WI,Fl]BM ,/m3wt9A ~f p ͪdЉ8y7~ ¶OXK=mГ:FV*2E4\1j y; z!EZ0ݛ>xĘDbٸϵQ nDd]~?lET6V`wW;N%9sGs5b^ril(kW̴eUwVC{ңfD0ZԅխwJ@jQ0'D}Fw-w# rp=+eKfXi,@K[$ S%?oag.}}qm )VY5NŻ̩ΐI6܆BqNV@ԙy% m f_޷wN~tIk5K@0/jadVܲl~!N&IS{ zx"^)LR1E~E3Q(ܞ>\FpcA37RDԉ֟t07Y֗0\sKLSɶ p ܀6ܕhS , }nU8_ЃzP%Enfc%{uꨮKxdf^u=7FI׷Ճaq.0U n~W\LB#fp1 I|Ik\ ڷB߁oYI&fcRgyPtvܧ7+xƔ=Y[s!!yh8;hty(£Ea"F^; nL]} y:G뙮FV8],|#ڸlR[{Ehd^5eDJ,rg/]eE'H(?ϛ 8%2/Af/p|>Pr+\L`k=|G3V+XYU ۤnrNXhTjڏܳJ*XLM<|dA3N=O8i75:LIi2~ӫUP Í,ڬڽr߻DmJllM͍by$+n4CA$ktw_Z¼<ݣ ?Dd!n0Lu8ݳmܯ  Bٜ陮Rc)*a;`-XcGf8Ą:[٥}2zT&jǺƻezXh0P,RG1WtгC-.S J|p?,ٕdψHоSm? kg HeC&g>@d֗:ۮFW=oN +xF(o`pOX[1>KCu=ZȺv/FVNF߰4R֬ԄNb $!7-] N ݺ`|<3/=C ` 3 2EP~87Z&h⊢vZuʗL .V*TvI{8][p"1^zΖ Px>dmKx fZ+hw'8ݥQ`zHShu ]4!Lߣqeρ2bt{ Zhͅq0_F˘ruƜ/ceȫ׫Z>Do@6+}Rq嚻2Vy.E =U8}EDnN\)CϽe)bF=.w뼄տV P Q~D݅mA/qβ K\eȿݗDy)pD[Wjz-J "!][@UVE,ُ8Y~_j9RQ08R3jG`]q%>X,?U}fbPt:Ȇ c7Qݺ!6fëbfR&;s`Mܙ8Kyf-^֨[vBk.𺀰[DqboY)hmg,:!}A*8tn *z 5t~x_Kq'\QK\K_ 8xY|7ar- |N5"*\wCЍž"F!5 3N-qbW?޷ v O?1NER[DIjH?n[CNsAe)DEd%R^_ĠHvz 6'uKD(*fihǃWb<$4?IQpZRv3 owuYlss1訑5,v5 3J3hmRrKe@dV!(^z։!)nSf.F(ID2f/)*"ݕF9sq-E:^O^l[\+,NAx]LVQT0u%;Pu@Jq6Ɯ悁3#G{9m ,!}~nzW2<#^) ._0#N[n g3)ݓ>~^"DlPW}|Ube1"E[ ?ڤ!gȶ>GZ`.6BExI6E(:GTA%Cglmthu}Q99 VC}KiU `zQ{,fd͇jRp(У~7g;h/jBVgP/*9b 6.P.`kԱ 0dl)~D mi{NBt5OJ@FpAJ:fZro<; ƻ9O΄"-Щt2V@PY螰<6 gy(sڀ3N@p5ÞyƿdoLq͑X8rSgfwˮS,y9k[@AYZvtACrgdie C'ip8Nfm~Be{x *6}_oayCTBAfa&t™ȑ@.w(;H_Hd1~G!WmcguExq%a19t)`2.Q Ep]Ћf!Fxn&LQ*钰H^ ::mi$Le.Y``*W Gu %diryCX?W4" ԙs_;85HsO~(941Y,B١^[VQvq65 C#K;hK 3=zR$=pw$$V7Y'QبyWv Tn7@ULxSp[kb:ItJ["Es[WQv[8UJ; 9 afn\Qa%a!އel͈Gm3;p.Ĉw#tnl̀xtF /i5n4-CdS7\kcyHGu>0W/̚=:BZLo/ Fi(f7[WN@c|AdeN{Pog8)Y5ćs4B2;]9`| .zM]v#Iw$%rW:Kx6@]AZen)L˪P6y+qws%_F3dתּџcBnN2fZ[YIo  egVaiJ֪o ?y"r*~wgiuCo mЬ!N, Ȏ˒6OAe\ϟQX>u$ra[CDZ`yִ+/D6䥫~\TbL@o@>p8ҭ0}86u-@c淖srҌb=c/٧(3静nC]tHXalHe'Gw2)( n*v0]^P@`OEw߸y ٯ%.FL=&\9] ~#xH.5ֹF wt'&S%N<892;%֓G餔W>oDf3je-X LnudS!')$iy~;w精x R qdKPl`pޘg([vlayd1~rֻB*E-~o q4R-7>2#I6B5.#_׵[u(EE;@IJk#MJ;{h_U48 Ӌ3F~0lkL4Pi_隲?PNv*~`$$2lD$VܴgN+ȼv#,? r|kFΚWP2ň+>jz-%q> nl-^ ψŃ4/,qh8m._{yP :I) ߨE) YicsN1!Z=ex#~] .#& 4}[tG }?@B]`nٛV=RiO}uݨo7 T] @^"A16۠t1A F liq%6ԺCc{o# #06=)'9lkQп >gF{Ů%PqA:WmV݇u<̰1Ù*٣5_?%\KW$fAOfit"V^`Vp $Qe|80_t9Ro^-`GT67kko, 7fv X4ywlŬ6Hmo+~UUrB|f ?khٝ\,p|kַ "<Ğ܆i"LT WD5X-YC{E_#ы=9-GJo>$>Hh.of+ u jf2OrSXЕߑޟgʡ7]rMPb ?lPiN M7.flw3̟^i[~xnUwG}ŵ2I;x7QF1Z+ g##`I摀VRg 1MQ2Wa)|ƹW 1`*^PWF4xj6g -ŨpL*(eS?. 磃1Т5WZ1_D}ō?$$uR0ތيt5p2u[r&+P,iAle$Ө7E"`mdzx[-jRcAQ~Wux'ZʩB7%ƝpUC:fQ~l ecP/*gslNχv4oαowVdzEtGRfMFNeк.wFfSF|'NЎeC}kۗuR cb{9MY!4ֳU iq~ /䠬y Lk Ư"ћ6XIdYW!*UϘW{i>LlMM$JhFqvk ( U_A%tǃʼs]񦩍`r˶~.{MbȀ~DM"}c$>'WYZ Dׇի h⪝cXplo(&3?_*Z\j$HLAHلdoDbN[87|YcHl:FX}6EjCvSZBipkUl/ }S z9k g~ؙ2LZŅ # #3wSړ>`nLW*zl_abɲI_әbddC:r`uLӃ53&AZ Q2 olf%]D±5¦Q7n?jLeB]th܋h_f"R,E>85[hÛ,Zqmvk;"OVo+j { f}O:]Ѓy2D4AEGc0 Q, $Z`brk GhLd႗)S ,|螜ob; ,JUJnָ Qp2%{+ yW@=HS N<⨹SH-4%{­VP1 yڏDuWl;\<\@lU); ]=¹pW6xO4ȌQ{, '&"ptZMy1;ATb,o썁f3*&uj<>XtN \Y#/ݳ\n]H0Z|w3HuM%<{6u9NQQtMp/'`RyVWTW*-رT: {E Κp29Os}TͼåLi*3bIA)5'+b8c&+wIF58@klFBGt_v{vNLRѬW8ujx8)M~*p& Ґp, P+_T]_'0+2Hj1-.)6xXߥ3ᬾ`LS7*~p/¾"V>λ,Eld/fbn?+{h| YZ2fãF  =6 3k  zzf+5.>-j7ʛG6%vL X%_ ^2dޚ1EJBdHk78MuOI,lqЗgT>Jܐ׵ XŞ> )ոSv N_c7:A8o+tWMSdtZ΅UALzS?~~SlW 0*DeKth {'B&-%m6&l_~}\EjJh@]>S=fˍ0r6x%wS%./ XP((RuE&bm̢.uϊM ,mHO߬ SuOh3NxCX2I] %wao&c*G?1$7RNHSCdl.d9h(,cP\*xdH%UsV0M&K;@wP̦KQ;H3I"t?LYk/,ٰ1sU_gh+"LsA5m \g* ݎky|@pS3ۜ~Fz{RІցcTVvib 0rѓVv3J% vFl,t'nZLY;8NLV˾N֙(U/^/7 81:%J䨘=/ Nkc/ ,&)Dy Tlg\sgG:҆v[{"-^ 9#PQbYe(4ͭ3՞e8:͊zL3,qtXnt.OK /OW0l)V=vgV]`0=2*~܏,y.o xϸ{@>$͜471:gJqɛ^bZh JڋermMgO6V)n I`B0nXhYgI)%+HȚP;WFGа3Lq!̞֥P"I+D0$' A D/}[lݔO= #&|0eKnRga%^jе#jfla5J5DVL+h|V[ͥ`X!rU_Ig + J5;qjg~O/QM([ۍ=1P45>qp^0yPA@G0Vo(НX w+"Ne(.T{k' ɚ<.(9X|aA-)u!z]2蓙(D1D&lx+h?srqB ]d,œk͒}Yp)Jݶ5[hܔ=WV!=OJTcyҳ.%Hx 1r KeG[["ՁnL2TL Y&j'zqyNu놮n@\u^6ev拏|7$3L)db܊O~Ǵ -ݳ3R[C0sάK8 "b) B-7g+jI$'r ڐsD1Bd;&}+,L(}ZS&=1Ρj^#4x8oW!C0qM>l}+WTABJ*bځ@jZKzAr߶M‚eĬnRxu\p2Iyw>KTگ jŭQ6|,JNk3g5/ ׆bջwDee˦=j+rEMtŵi9{,7-xQ3@jOݫM5N&ʾz1L0avFtb)ۢ R轗hEQ5ܔKi1ֻ/F.9yP#BF/nIIh3p}R`r[吋eIݴ%9NdѾ ^c6m %=Պ<ۭ"fK.d&ɋ٩erSH&[mzI-r;>ʂ<%>:+crGf<}3 A LK8ؖ@u{h+w~:nχ>_%{e(C8(&fSꟾJп3>CfGa-x"qFccORݮAb׹NEݵ|DsV߿ yh!н7pZ^POnY2 @qaP9IzCm;q_6"{qfj)\S:6 HA&Z}K[Hz VJeM;G:L;>3&3WUhyWk9oB' vfͻzxOV/ˑ8"}WD$(D^g[*D(_Ǎkz-crfw^Ӧ4p27KLzBJ;7awURD: yϼp~o|^GS:r&cN}@ &^[Vvw7%Y<[Ĝ6n.nDax!|5!a *9f"kG)Bv\e=+Ϊ !hI/GOߺZlQlPr $+4'fЁMe-n2X磜 aWrgPm23l+3sau^ڧI:5}W&qA,~(vq{ɷU#E#) +S ɗUz8LrFC+A?vQۇ6l Diݤ+`4i /gfYf5kN34,EtlUTsoăHjm^u3!9Ƿ 0HcM L35De@.X!xh ణ5|Y+ T1pzDŽӇ]03ϣ2 g} %~ (_#f"ݫg,rqy@r(/3hM[:)=lEYIB}RVw;J9n-KܭJx~+1*+?w>4<猁 @4(V"Y‹0bAZֽ$U׵uvOPwR.E0FMPzH+h'Zݔ6x /)caT96 ֧5i4jg0>uaq\+]EzL#$ d0ڍ o[uPE1;GmyhP'LbU\w[iw!_;Kn@&8"+tԺ^-O[bX"V}[_oz& W/]s;s6n\3I)X (I/hpQM5<Ȉ+U-zB\ ),05N J,=й:548rfeyk WL j#}%qGgҋz4YF/(&@3Le6\'~ ~i\r,2ihJl@Tqӻ.ʑ1G<+N0]_].+2dm}JI"zo=B{\ tLg>7Uv[AuZ_m MREp/1 Cf t.KEs%Qʏ[6sؔǽ4S5=JC5Wt 7>_vyXU?\GzKӱ^p[N*m󭏗M %#9ʁ15 aRx>q݃Ef`J@&)hIIm7)"KA`djx;>w~N.CE՛˗0]4q=0tP>nP#2ߋBvpaYGGpNbܐkR@;;ޥ4eT9(fi#Ԭ77YXxss]#[ Z=G̻6<'{n J W]ݼw " d1j\%>|K#951ݑgPuk{' N1lu}y SY =TLB v |ީXDQ!c_I =o+*'j"MV&j"9Ů@%ٞj(W <Uy-d*E9gc# Xl&dءd>qg =:tb;HL2ؙlΩkPI{_[Nnu~)BE+a;pƾEF{o (v),{ @=CԹس!nac|=,`/̜8},]Kˮ\`I*XAL(PK2{LDC޺$qo-]|6: di*N?.#⃻ ]oUZJvmr@eQtuPbiRFL`|B!9`2`:DWG_&z@Ey欆<>N j/MdXZ\s &%{j~6khL6 *ϔ QZeU-[+:ǕЯGypWu`j]Qk>QϬ"X7:xZpB_(3@R"mFzC>b!}>IRh!Zx>\zH!cth2sA ȨݧP:H9,kOp@dzҰ[)hW)qء,޹Zrjߴ=lXN '6<*ǽz21e.;Raa"wҥЇ{@lWXL%HV}W5L%쇅WԖ|϶_}m FFaNPM՛/ {` "S 5Tu2_X#َ׉M>SABp*M^ AHvm>X=dQMt\X jyd[v ~'yyPE)+j?|Y'`+N)pfϘDcU2 46%e@Ll!e8+,154fcJCr k)^NBIgfu̶!!ȡ>u}yeӊ7PO ODWRڞfKjpj{nDMhN7 ̆'gXAܺdnvzwg<%6Yς4I IaW%nȚ*cI_gbփYx5k7` cj<猊F:yM<# 4:Ok4 TH@ts;x B]g%r9I=Z*U5kL9+J$VUhkV&So ! eX9(.;u.(3m`$pMG8 p'bRR_uݷ8!ę!)U 0,Jf/Eq|yKl;r]#7@9 ?;\(Z*R81gKJ59fu 2gj 9;qᨑXoAn= qm?jz؇U48 [S1kc9áNt& kGBFkFgc>NVwW Y(˦{"mPܱ+OlgW A+*wpuJ̰BŹ)U. C]3 rb;kqEi7qA);꿣.0cz|E DwExd8^fdlFd 6 "%Fܬ2h1W'uA$,ע!UK6,]W Cg+b]3:?x n;1L\Vx8$fvE\q8S(:VH5&hwZV欰FA_Pdҝ_F:7_eHz5=Ytxܒ1.WV^TBQ ZU _NFj1wLx>s۾ֳ6]Tj]]w{&>rY r 5 FWzs{bp:+gͮ~kDRN"\@̊0_$s,~&!|ބ9O`|}ʹf^'YژH mA ѸH#!qTAgYhՎ`_$xLJ S`ܫ:96p%ܺm.Qxb9>[WOl: g.Q/6B)h}KXNeJ߅j_NӾ}L89x|n7 ;AF8~AX ¼-,3;uq.Nv:(&)' Ib.TL ;({5w3pI@ϟh3Sފg& EsE0" ˘)@O#4>䛮dWn\@[zy>rgIt,"|(Xu`OoBD m _\>u!X_7pǘtBC7%b2k˪8 H>5c>qYB2]ڻ ?2~&sgq.BanXBm3p$]}O@aeFPpǨle+`gpta&ħ2煉被g[ih<4>%TnāՓ^^B.Fq ^qtF.pT;Hl)4VES0ͷpZ%.`6 I&l%Mb(mKRq|7|$Vo)k:B>El~ZB2p%lu%Naxpfan_N(4k$W|\U$5LAϔ@ꛒsGa Z,j]yA[6`VqU{_-D.as%+1nn0C*0BO.g0q!Tbu&ڹ.eA9Gs&b)%b;Z1 @OHQ5z0Dn~Z\3VNN꿃3VY@нDK;P̉.rⁱ )t9'p^9vFC6d˶‚l,f߻dc ]@{٦ٗ$  #l+p˦3NvbđMBl@'w:9(3l[6p9j&SOPLE?GCsBZMi"%!,46o#[:rd9HJ)4CŴ!-h b8gd 6? s@:Ľ<]'o`])،#ij?%DG~M VH-NLm 1\^p$oucҮ+eaWxRHc%)Jƛ^ίjտb[ 0wMfeq6FzN5 x<ۆCl:j.,. <#).ALB+$)!g{['Dnֈ6I>؎iG$sV!"\6W4ɚDodUp5ALvpf( ÷t``E&µ=U &ީmMu:Nے{h<Pؼ.8##̈zWA?t*{RJi7|q*Tm/*a-yR 33p!̍ \ 4ř1Lf1g ץ^$!n hMWDz/CKk r j@8'*-A3kG9w$YmMBZ(8Ƽ @kY?Kل$m1icq2Enæ9R5KQ\(KDuTeC%չb&TF8@Z`HNA ҟ>zĭ6{C݋"Ck&:ڜny$H5f({ A򭖳?}"j 4%ř׈g2"i9ӷXˣu"K ̮\uA\j8f8)1jiǡ].Ѫev_A>\fhX hBc۬+VJ~N7DsQ \~/ѡgQa1'pyQoQ(87+fabJ0B藵h0tkۭoiJ ]'7M RoM*ḾV)z9*jjYJPʴ"~\&jCٴ ڴWĢvS_)_m 9NYbDڀ[S2:z*LJhlv}Jg"yM8dQlجW%$X.:ַ%}0zݡP[m1GJ.~e6'n\ wx&s̏c%Wo;Ē51@W t~4`Kmp_n"G6>EK$NDvF"PՀY^[Æl-?)X'ſк+nz8) Qa0 :QФ#j)y*j^,.Ě԰K`4J y $r:UPsjKi4:eu>p vMUSs1̣ LݾSz! J=^5E}1_ Rsn̠KQȋk 9 {oXSj:Gi5²h)a~#뚌!H}w%#ij:>up:liy|w:i @||Q9lw&~YqGLUύL?~(c]qڙʴB|-!X1"C M8hC\^Nʞ3a.k1*DMbiy\{%qY" wz7d0t[  #'-=@ٗhSHnn ^ySǫv퀨牛e BNh&3vS֫}K]Md-pk7iN4;ٳ; ` \+Ir]3u82J*w&> IF\s|.3Ë~ou6r=`b} & 87l%A#K6=˥S7Mc` GY%yن"E䛒8aHȞb :7"ELqc6& LcIФbQs<͊䯽Δ6V5LvyxMsX~ࡶrH5}$:!1[;ESJ.v;VqX UzBp?Qu#[Tg0%8dSE#!ϭ=6>'!!#ŧ+CkI M4-xpaCAVՎߊ:q'rJ&--`_΋*mPK6X3nx8'-2 gCwO;ޤm0[σngg=zEC1|a&ZӋ"&*'К," uN-уv"[Pf@1ib o)~?{P `_h3AE?ZL' 8,l0Zy1vC&\}dbDXBADOǰ+WQZs2yd.)sruo i.Qm9Ũ +{sz(w'd,QM\^(>0U&w+fsy)C|*\yw2e5ީB [-ԣ:uNuH7]ޤ%XBh cӖ2*\Ij 0I^TLJ|-yYbv%hjVL} 9~_KƳ-bee_׉BlC!H"Mp ¦Tf0^;WJ*5=ͼY(j AM4@0ev Z@kg {瞨GGwk)mSAD`;a] ˠr>Rg f"ܿ" :{~8p:6t4gUj&fZĆ ]UvUKw,L(y$`+,z/tEuĄ+|%ڵPP^DS˞xJ>J6ŅPΛ49}h.3l!5hFcI<0q: ny^.Evo>{<޻k+yA#s!Ŝ wex2:7'x_v.l솽<gͬݦCzdHkצ a'$D)p-[l@G*i]1NA1rKCf=B# 32~I_5,(otukSͭ3*-&SFE0iQ{׾sg`hQ,0gǼu4IhI0Tޡ5 9 ޙ~ ƎW9o6Ӄp:ehzv]ͣͩ\C80DW٢e2?7G?T``j˧Be'ud>A,&o %c5a|5Iz^7/9Q `-Q*a(^Lkc0$h1#*? [AT yeKφk@XlmtjFP6[ح:F!x LxA deNΉ֝}ߦ ΧlEz=qPT\t0DT 8rZUmy0UTpXart[Cֈ=,>AqYH@*xA'h!GTA_/@;.- Qfo=^ jJj0RP*kn{Ԟ Q# | ?Y H:X!:1ԪSjao+rKyt0h@7 e٦y`g뿜d$jZ7CE ~cLjC7}Lw_[%F胭}43{ FϸFߣ/Տa I'Bk-Z2N#[uHķ–gY$oE1 3Ώ;WM"{)n6QDA:2 Jua=Q,?C(p[PFUP0ղDeMo&x.d˰d9R=jç(iE47& DWJA \S¶Tw $pY ar&hayѵd]9Ё3nBQEZ0qZ IP<2lm޽_D&)4 3B2tN QdDr :f= a_^1Qܳh0Fpx@b=mڽǰ^et y}ߊ)_gT9WW OeoZZ2]E|JF SSC˂EFqBO/Ppb`HǾtp {n8Ͽ6ɓIn:m{tRb|x#ʳu)]Nse B4Cu z9aX]9QӍː:ϪAS/_ :fl$k} ;&Yt%"xnZt !u5U-~WddƖ%]$nO @)9M]P >Z4t u&Xbbpgg8a05W SwK _.rWc`I #A =G=2Wpv_qPCHh 10??Uٌ!D$f-|뼾iڰ ]3CU5qF#<-wKǒi˹0P"0SIGsKӣ0LB#5RwWDBi_ト=|WC^r(K.lq}JlKP:EME$ؒ5\A/aA)hmr6}&Â3|!1&ޑP&Β hM^THۮ[q8PY@ơEsgavƌ ʧ_6 OFޞ:O $|(ۿI)دi&`!a>tnZ$NHE*^h2P]Z[tȑcC[KVoYBaW!H}+ F엦m^+Ul{= 'Oߵ]=7|V6`-%Gv,$)Z=+A鹰_uն1@?a&PXiRbFD{o G=O8Cu7ɐRzTyQԛGlte+*8FeZ;e5ղ{r TVOg:8|!N 'kH8b>&b J"% L7|ϲӋVb`f}偶5o0%(hv5vd 697ViǀI9AJd]Q$ nROۮ<. e gAmTԩ8HkZf&a0o6:@ũq|o>ւm+B0շ4M7-9Ma ?w2^왂OTH;.SgG鳾@w-@oaT[Ǧ+ "Xx[\q|>T_}9~@ Wjm.3_Ҡx pl:En;r!]yŤ)A>kYD߮- 9hB:NpIH pPA!h ˡqEW UCnkY2ɳѬ\bEQaixI6D %d{\kɂ vhmq%: o:S:OLS%lme#F[·_0̞Ј"Ihl+?tQ(po5\y A.ľRjˮ9vwVr(e`Tދp Oއx{fsYADn7xkWy'8m"aC\vB!.ڄYywe^,9Ӎ 4Fd{)QB\:l I2SǍOFo֋zU~hOX{ר5P7&r=.5d]]>XkFQWƣBNNw(-#**=zjy^M'[L:scHyt_)FV5bd;$ͮ AŒ`3qMf̕- !p b߲; +?nճ("܆UDLBu52S#N]2͝4ϧaWswi> ӗj61k2V2DOu>dzj^m IfU$Wz fH8&~ZvWEfD(װUql@Ncq׽u=2ҁ4 o1k9E噝2zhsݳ _ Y1LN 1kYu՞ Y7Oar 2dr6^LLM%ۨz#"V ?s <##$aCs |[^pNfbiWwv`ZjRmvM8Ծ#(#F#F?zkmQ0iN. MWF񼞞f aΚm\8`tർɫzsk&݀»ꄠ~.n 䆺Na7mF3~cvsBtT$ M sx_C9\Dh`+vn<_9? TfFtI&n[ū6,YKNxQyuMFYM+b'kZrMn䗰"0BɒO,{!c-D-rLί/=P%{G 3|uL~0" K <rnH 9o!Ɉ"PN0Z(E;$fg*!Zj6nԕ_D.w ~@"HJ135ǰrㄝ}ۣ ?MK@|h91V AX1?'kJa&"b:}`(O?~@]t2wgK$ A`B20BhE6B>cPPtB։LNE3? mi906& } ykqXۭ-!v }0l L-uH*x}4\ ͅKHn "/ŵRR@G%NX&(Ʉ~ar4#7@/-0ËnTA6R^r&z&p 6*# ♿zᡇ_FÅ&\ iY4QB(i1V.ن\k$ٽ~XFe-5 }J՗Dw[. a9RG$,; S,(dgE QsP!:}"TMKw|" D37yLEd7H)t5T2{,%H!JȻzf!w!)9ޡɗ7ufrX.|fƁ$ͦV{8 Gi'ثѪy8/pDy:5 `ڃʁ<$-hǰx f>LsKm8',ǐFmt|'4M Ďҽ҇[cM;=|PQ\nm9-7)p =1IXd~0 6 $<[la >_"l-Q}9h@;HS¦k'牡e+t5~H]䑹"0՘iCls5u|W!g2p*|cW~[$+A >.LydUO>}%$^GZ;m'vvyp ]e D/'m憤m ^~?G";@|Yl@N-w/4PR9;0h[NQrI] eV χ>ͻ2!q7ܻSM6(Nĺ':>N{"+ڹ;5L}juyȴ4NHf3O ‹e!en!CKvkg7 x89`(Nk 9X :;,Z/u"v0ʧF7L'ePn3[b|a$8Lsu"xe(z Mn\2C-OT0OxSrh&2SɵǪ#s7]ҲŰKUptu2gsxٕtZ ƛ%Z DOf%):JM08k-p,0D yI09[*JD*)q2aI ;K5jƢ3z?Ur|-pJβ]):['q\RkS.H}XDQõ}S%f/o _& ֠N4TGR]0﮿ Q!8ai%LM aU{m TuɃgI@ ";V:y9?tO Ci !rM02HV'0{C+qo:^V Vn` av-CvDM1b,64 KO-SP6wsuj{N R}Az *nCy b1eȱKϏW,%V+̱j=j.h" jbNѨ=0^*x'LDߜ8Cžw#̫OiIRׁY-XW,$a{!#L@[o=} 5 “{0^a} ~Î,ʲ@5Yi؁VYmyVuc:Ԯq];Ӭfw'k R=@ÓU!TO7Bh @r;*X5tF[X A= Ml"êUf{rwq.աzK]qhAt4&Cnt A/?V-)M[ewHCyr-kRݷ[8\ Y夜o;=v!f؀:59 97> *bb/{Ae.*qN7$ⴈ24dri]44A)i3ΐYT_ ,8 ;{T)hWkQGS(8Q7+af3O/tb^'ŔZ#@:0Y[mdA.U& auehcYꊷ5D/\R=;DWwV MqϤv?65H<€p* S1~WB3LG7Ƞz_1W2 |=`E}<ڮρ'gjDO6b._'NVW K{bB;ut֔+Vm' W D<$)xol[F[yA^ l R&K -%#$p${-x5T=!v.u5,VWzƑ#TЧ1GDרS1`2'&JSgΐZ_"Ӵ)1,B/1w>"quViL*7|+I}ͱmlDd% ڽW$ ޳C^‡mp:0 hC3, (ǰpMbWu(I(3U:LE lSPCQrX~-s .' Jo>>&%HF;&P黖Cv;PiM&s l`0 69nۉb !~J]q+֛=ߡ h^:],8EjuIݤrPS l!yX*JpˀH,;{6Q*wz ;ea 'BdvRL0ѺL>Q߄& l0kor"Z0q϶~&a^-3?P^1al[z#m=[7380z3OdE?D:Jlz$s5[%q Ll7PIȑM>[ ‹?)'U C\OIw9d$z'_q{(f`(R'xI * d`4k4 N- la(LH}t& ׮ ){)+=JUqd)a]c.ˌ_\5q9{M+RvZ:: maOQD{肺0@/ tc@å+bx>XnQz. ޷Yq~lg SBBPͱ:~d7;wVij{p\F̋up2Ūe8K|fF2+tEp%u$ɷJy=q:HF!Km&Dxl7/ONdc4p~=SBu~TB>J Gx`Qx 6loT|}fmFe1Y d94 j |8&;F惢\ƅd("<!ȫqd"s}+#Q7Wd^뭹z #)='׆r0m=Y%Pl"tv>ɶDU̦"7HXžcA|i HQZ{Q8@:W \QO- _ ?o@Lc CP_&f~}͆)A ĐOd9 Ni1}L! '"û~;p*3(oA!١@' 6 D%;†(پ`Tk0 )fm&ye2lf/>:nd.mct.;zK\!1`"l 7(锭&^.;PnsEQ+Φ0a_}]H{Af4aVըH dz:'`8nQpePWN? ܫm˄a; PZ"#QfZjIv%-6ph-G+

'2;΀}ZP7) 1AF 6xu[=!DrͱmV;G[ſ  TQ0MvrwfVgt+y0#lCF$e]Gl2 }1fjyR^~}DkH ( sA0u"ARo"z0 X#IW\f;">!xA1µ oS'j>9;QH d]Kr{{lݒj٢w*|EHr R6&5|mEJb!h!~N"@W7k8MLt{q"cHv::Q@iݸ{!:U$i_AH'Sfbuh—Z1ioZNm;'0ۯxK N` RKbci0 o_[Wq, q[>kўL([3-XְX%YQ߲rNs VóhFdMێ)eG 0̣y ":P%^0Ql C6es Ԟ=&>{{vw8nE}Y[@7c3jSWpERU̠qzf80/ hoiihq L܃I+S 1/#rFYزD VCln;He<$@ zy'kd;dVMsx?u{ʏ^bKS@:d9+Lf-EI%6| f%Ⓞ/#N(̢`/Xap:SYP6ϳ {(YT}GS0203 m[CsU]8P 7s@QzϪ 3gCnZ߰覍t(~q7QllI/6vcb sq@Ñ$@gaʧ 91ۆϸ taʺ_2&0>+3q$S1Fq fW<7QNg50;U}\GR~!Ƚ_飂~؋b B^Nٍ~~Kq'WF2k n9/t"h-Ao,pqxհ퐲pF-9d5Awl¥Bw?=Q I gm *s|3k?^k̳QC|(ۀ}@ܡW/2v{9&x a~k0 1Pg9= 'ӆ&_RǽMDd3+u{"bϣQLS4N4Z͡Ne8A'*Rxigy+pzQ6Q,Q?e!R; ՍoxLK\Ljg 9PH `>3N77'.:P~Dsކe8-6چs ӈSds04; {tm)[&HjVy>ғA~}AoF!p0!z5͝ ;}}E@![ uv%3sv ?$ض!ލR/U/E$-B!arsǣтnu(傠GPjYx 0`D';DOWJU4];f.p{Az.EVMtX_"`z`(\曫A% XpdI7Q/s7G0 ~\"e}D#7I XeȑîVJ4,ߨe9*Gj{|Q B },3ɯ L2Z S(n'XF|M5"ʩW9N]U1al'00t]K9 d#za3g,Cx{XL[*-#CЊd N=m2jj$vޯY30 hRuڦ/F`m' #Dx6X"„d}^DЉ7ݵ!UߪCFfbRVYdF/{h_ nnUzA:}VeA(ZT;JX*KwAϦSCY:>f&O@ pSwn8-7RBhs:=ш礧z=60bL֝[CĒg; jPЀ&?2|h\O\΄1cld>B2"VW9n8 f0;:똻-l.V)6vB2r4,Te߷(e\6+/PnRj߲8O3h,+;PLk_BsH6\6B_.ц&ȍ5z OmЩJf46a!6,Y?p!0`9n_PiekqƷWSr]+ 'n3-kuφK dDy}vb$`dMa D2E7 :v@9Y5OOS zfy {2X ZKu>l9m31̣K-x72Z%#dqt§ϾOn Ng0::#aٞO8S-W |DOS>Ir )^1וz",[YR#`= h0MD1.vٻI^xPgw>#|}p!769Wp*dRi8uDDrfa/vnU)SZ:^f"h"mxrQ?h84k, U5'@ެrE9GGzhJw I9RB)-5zse ٸho-9v [cw_]d%9]C +b Ɋi{|Ϲ1s21q°,C%bXHc:g+d!h^b X&K|0/9%n\1njNeӻ2i1;5'2Lgγ)8*2{]wII h|Y2mYQ"n+M4^:tK0B[!Nbm-Hݳ%""zW+Q>޺'[^c fa#<ىv`CCx~*ڵ닙fg _zs 7MSAD3&&ӏIڥ%Z.ko =t$ iEC+[  4G o˪.|szHuNRHFB$u7z1}=IRPcb2)u P܌{)u>&r7\NH[p<=߾$CY C_BJI24|8}̩°{j/U-}TV䤯koy62NF KbuJ1ކ#1 ?]RK4 *cV9M9ӭU7@&IէoO xgϰ>3urj_yE*тa]$l YS,* 4+PY*f>lx%5!)kjQF,b9AAAq2g'0UR&j/ 3|Fm7VE#GMsnCGHG -$4v~8@h7GHk0~[a@*i`xXa Eޑw"NMӦFxۡ^Vp$HyҰtr[ԛR cE] {AH!ˀ [,SuWJ:r+3{wj\|-o# 's2ЦEkAyx& 7/I*![nbAˬP@m q1ГA`MN&E\* ՟mt+ daퟙzu0igq$$"2 JX;)^X%Xɳ!CTe aP־sfd RP4+ .ʪ~̰4UƤra5pnFe6~Ƽ\ʶj{NP"T{V=0:A).iaHHh&FL`ba,<`ۀެl&p=س/찣g[T =L- /qz-}\0|i;i si[8ؚGGH>>t g~ 2:Ht<w(\}"n)`ڃ!do^_-Ԟ&5xS>.d FsuС'0eoƩ} vgNV;e}wQs[XFn~φ`T[qD-[|$u xzv8k(v 0NnaXJ40[ ja #b98m4Mdg%C^ 6,g#cEM5pT0a*x7!"jGgpA]1E鳃6;/q CKc2=9>'w7?h}$1$>Kf|%ba`c6D+׫.Ibp k {L&q w]1>|{fPn'- hq5;ڥ4n]Bw`nh8zt>;Xn裢CyiC嫣Kޑ=)xͮz0R B'[,wYIacfWjܬwRI"J`{*=`.^" gO=IgaLdR/DgMY~vrnS!9VAcyR6\uAFhPӢ> ۝l~j~`(r\tq%29}l<];rc*huP_mU5P/4l'^RK!osנ9"&_)mx4е%WJc4NxcxWvoIZ}52\_b7}/(Vr^30[ejԣPr(] ԦUa'*/3X\ u!d7NT'ֳ4X-HQTs؛~vt&|rB~l$@GmĖGՒ2чshJT)vv_zD`XQJ m}I|7g!7;lF#p-Kz; H$]P+id3^'U?p{4Hv:"U]=@a- 18ioBL) v' O==#:z!J3{H 5R2tFEn~p[!>;ZJ?H4"- j5:t:,75}L9N@dy# v;GXƐ@*:4wS]O6/$}}8YװM͉m.SYm,jŁa63{脟|HhL}KK e~D@氇l0wƿ@dGҍhO^w/9.0h:Nѓm|P/<5R/d"b &F=),"W,. B7lJ,hE\_{O!n;e?]6urlGˣ631 dwa}^U*%Sc}^d0kzۋ,2;?36F٣x :ƚ\r/S=˙G/YSlq0,{?xPO-B[f\H1 L ~HbB̐y'|+ rv6#pbzz6I;Q$ÙZ~i$ckЖ%l@ah8V9`L18 ǰ";uEU[_~xɖƶ:=NZeeւ~1YM=#P#gx,g в.氪a\MEԣaD-5yk֯0tRT5'#0Бbq t*uÕX#q* VvOc#;t`Cf9ne%  e\K>T9b3T~F9*~~)ڄ+@atp)1s,&h}*C/*3ny-E) 2+`YЈW8#IIp޼4ȧLᒜ,8H5TXH$ ȱol:'3&nAɚLvb-ߝr{.TDfEH*_zït2W\1tz;o Fugob>ct0x)ˤ˿ۿ_o?_??E!go-toml-2.1.1/benchmark/testdata/citm_catalog.toml.gz000066400000000000000000000431251453566013500226130ustar00rootroot00000000000000uE`citm_catalog.toml͒Fe-qGLd. )R#!Q#*n%v ZA x^9/;^ nYU@⯘Yȯs~y|\MW?/Wlɏi2}7]l8OD QI,$14j5r]lҟe m=MV_n_?H67/&bBZOMBFq('?eRjs8hg NI߅&Z}=_|OURyp_>>>h6郺hia+hb;:^@WHL vo&Ymv:dȻ|g~([r"tQ泫p"UF n {?9_iZY,D`#YEGACCCC( NxoOv/,F>]&oO}y%g/_xQ&, mAMB#U#+J,ta-B3N8N(NK+Z-/?~^`Av{V;,YȘl"RcV*HƀT@*  HvT@* RHe Rq50jL4"#T]`=@ ʽO7H$ Ij8ʀv$n8)$H5qңG/'o捜$;.I*I&KN*I#W3vv{$}SE+N+/N?Nj $j5u\T2 UP Z{/TL @*ɋojl&On.* I{pF.8R*.ɻM~>weW7L? ( nIKЂm'wy^v{SѰ4oM^ʊ7L@^wM WuWzaB^UO *Y$N4Pq%ko)8 4N'y<$p8Ɍ$I%DN?qI}ًcFː] 7kרa55*o5j/רvv{$}SE+N(/rP),'2nYX7zer`9X,9dN!J%kܖQK7b4D:I_$ (kQ\Yp1 hŀ,G d6BQ9뜦lgn,-̍.v;s՚YUs]Ů|>qE(8Z ;og1aǬa?1nS٢֗AqHXUEi(^]7qh^Ij ࢆY;ՆWlh+m+-^ٙ^9Jt+}h+8A5  ZP|4HC1N'y<$p8ɌDNInZs-9[{??b9sn{ |.c4k!Ӎݜn}tS`4 0)0 ,Fh`40Hwc5 EUMݘsDwR1$M[Ƽaϭ)n\^?8, ]0̢o/ лwd]P y";sV5%-KJ"D,NV́֬XM*zxIl!" 2YlX{(Hb>XU70HaΈZMlAlC'A !="I$4,Bz| IMHO" 1ak}' BI"8 N2$i'g}̆}m]J"fcxbBoċ 7=>EF1oJl.f1s͍h`40ϲ Fca4C%ay맖?G*ti_H\l;;DFa#<=&Nq y`] d i.J}lJ5гjݸjκ\_6 vlު' ؄?iN'Ep8 dI9II9yaIIAM(фR[磮 GP;ԣ$R4,B}$M(uđMhi8%8 4N'y<$p8ɌBjeO?8ܻmrnc$ܻpNܻEQ'Qŏq N' CI"8 N2$JDaLC&N?X$exGnW{JnK=^<촾ԭ 8Wj&Hh7rfF6 _X^eE [I'a"DnB+?^ ]kԍ{n~,RXkwp :vgړ/ 8zqas,1(s%P mI?d xHT椼a➓*4֜7LIw1N򚓪N가Pը|K! y)G5c\g=&Rb}7*Z0CʯH[E@ l*[V`dP 1ER=۳IjYG^hw7ZWNWw5٧_@F~{.{`2'In+q{rTKTKTKTtߒ pչ .pΥ*F0V\_Vp+8 4(Iz'I$ 'E[cAEBq}g&n@s' 4EN Ug]8y@\\`" ku81?i)mh>Gt5*DtHBt0:rbA8ȂV6Qlx=հC!/|KTKTT-T,hPA=p8Y'I$b<T<n4~`H D VR.<"52J T?UuȕfXc[n!-"[fa5ۣ]j; RVSͥ,w8v $jEBг0"fa5|IGpKY '90 ''-IO?$p8 (`6k:_ؓ@F;4knh$G>Tō Sm49@a'c^D d-U@j y}ٓ_"Q}mmnE*^z k"-/=5ދZ4,Q2q۫zpv{$};(yHwZyqYl7e(O荨iǾe93*ak|g|v6?D>Pg;ٱnC^JqTkjI=)$ 6GGv$# N>U8'x(VU 6E#S`#5)¨w^&nq{=^MuzKҷ3nMgtnE$ljDJ5r]#a6?UD@" 0((ª}jê% 8II?q/ {}'!da'ij6錩~YI{$pdzN' 9IƝF>d 24qRC|eHhka5ڇ,oX)!vP45axbˇ*2\/I߈WCʋ5{ fǡɦtlфbp"B1T&h`40gj kq1tlLalHԄ55ڤ-G[Tش7H!l5?FNNf(yc~41nSי*IY7cԍ:rTir8AT7R9@T DQnQ#Q@T *YQ@TG2 *Xkzw@T * Dt@T * 2B;!jB$ d"05exG(nƫ=%w;^%+xk_ @sM17Κc̍yѡMt}rAJQ G ;4DJ3@2!r#:/֏{SRGGF7i5770s :s 58\3YĹkpqs-לS@AxǫU.c>cw[,c|Y$};s"Y  yP"dAƢ[s#!"c1/昖pL1 Ǵai8pL1 4ӎuLq@ϖ5n͈ #}:I@ް>eE*bt0_]۟YTwz9 !/I'*_7&Z](/I:1/uQ`o9= a^4 \~>㺲=")QY-TRqV,xyQ* )+pjZ>7UeQp]{XE@Ik;yϫEIJޫ n&e WkZ|/I-Oxyxuy2Ov 1 "߮1%۝%vxm&!~6ːw5bᘆci}fpL>Jᘆci8&UP|OdFgC UF^TyD@pU|EN'BS6BF[وxP!n"/n& R"𙪖Q71T5`N\4ހY4Հ9R.L HW@ ]U-S"TdC󃍐8HY7W=V+O'M M*E7xI&E&AB{ w܉nhwZ- h;K:%dQ=eVSxsr0Qzu7]3au9veux+׻kq ! !/! kNv39W˘7 %pR}2MT)Ӹ=omED3"=0D4  R;JCD3ͽ؋ڈ +V" SfhhvQY6U+_̝4b1Dr7am4gd!)yc# gKZDd=N2PJ LqZd!>'bwp'bwZ{"<x[avC Ȉq׷x7xm}l7X[oⵣR3K^vRoï aL SarN(/`e̛Ra)(X,7h Zˁ29`9\XV~sL);2 Jυ2 -Is L*uɤBjŕL*|7ZϺAk74j[^Zҫ$#8j{/7}T |Y+VS.'wNP){ >J^]ZnHH*IwF;Xa59nՄenr\ Ynwbwhx3Nir7g}٧_@F^w_'`J۸5_;}`c`T$srxI$&VwT&}z N5|mc=N585:Nς!Z۞ BWcaWNF~HRTyZeO5P *}jDnS[*O>͑$=rϪ7%(5ԶtSQ~O7 TU$N<$VܩxV9G^*x9b_.p!*\FpMکn6B!>}pE{*u~Aj;I:'t+guEH0ZUc-*a-h`40ϲ kq1tlLFJ"/a8r*^ ؇˹+i@-AJtfͼy'pVz7nOzG4уn[!ze)ǁ-sAg~6' ,A5BfYQtKp#C>=p8Y'IfxhIe!&ue^4nn8I戓4p} (mT4pEPɗrqNfIkVIA=p8Y'If>FpRS&Nbj;bjS?b̉15'^)oyP"mt7"6nD [3IHHDrƻ8ͤ,-LwOT? k7VAHfs*^b: b: b:Ҕ vb:Ҕ vb:"H1}g{eyaB7xUq[j/ *XCT8YFIJM%] y^ȋu0?.ʨSAz䍁@^ /,mX@ "o̬FJ⑨5Hb&TP&H'w5tL]Gi;DꎊC #RE!d2acu˼ f>?l,s B5:9oXceVCc5Pm˜7,2VU"2\/I|2ҝV^Xb8 =o?}JQi@UQ) DQǢꀨ@T QIZ]+ٯ .mu!8:ɑjJ+ &+ S5Gh}6BM@*aENL_@HDyDy1-WHUDH+l$HW֍CZS_pNC\p*KP1`#i3 M4;m1WQ=V9@T DQ5m)5gD>ica5z]#FyTG>کnD>o!M9 7Z0քYh3TF%QoۯTLJnh4(l(Q m4)gA'z:c1gt :"QAǃ::#;%ד- zIê3=K*ZiToW ,v; m8źU%amp6f 'éZP[uuWŔ <5GSNS[aebۛ'~d$CERe:iXu? H#,~\/ ޯϭ,"-VX~z,`g\{ŊQ\E'|U8*P Od>a ;e>Sn%ݭ)e* ܝ Qx`!N٨|K8ECaSW(eġQ 81QTuhkrZuaӼ洪DӪjf`W&{5OqSj8AKƹӉMǝnn*wdp <bʱ`Wȁ8z2$jJhzEêuHBް&K% 38U)_.no! @PQn F! 䚆inעVb@QhM @cEׇłg/fD c9^@ d_@1Ru(B&B%2w@ڻjc ݔE dO#b GS N!=Tb H w?!~"%/s8<74qxګW7*BN#BHmSqNp'9Yw;N7)JθlvEv: TvFv,8΢aѱc~Iu3rvtXQSU6N65lL`cq4l<|6bcd lr*8?8BXay6iT oW ,vT;@r8źU% #٩3>ጣfp稈jsr#OaՑgNC078 4s@Un0 a6B;#BSCs;Np'sw;3d܂;f:O=w;Np';elŝJ%VfMnsM%75ifQnsds$a"`\CnXYC qyC|}fs3rf2MC kؘ +&d|dwzM&W&M(#J[p idP w;CEp' t4[ʠTju Á&:Fw]N5$IUs5?ES  wM5Xë:4 AȩtW"$iGQZ,s,(dh/ $d#IIHdws=s%)$ 9hؽsPѰ{砢a8e(x3*x<찀lT%qxn4fpF7N/Cgx<ݤ$67\^nnnnĎn*92i1E*==t1j)}T~Șl"; A[gTy?.{q1wš7*5 km# iY<7ܹ/xx b5E}]jR lU }]s w=?BM{S.no!s @\њ @7ǁ覡tXS٢ɏ@vֳƽ`cl 6 4ꯠd3X_Վ.-Q#!W}7\Qa̅MhX. M& Q?(Ti VW~!Xgz'(4#h`?J>4 Ը[\ 'p?*+>N+~\%%܍ JQzsER-\4,U>xJC*.PRnS$g &#Oi "29xv/FHSb4 ]o {?~c\te̛~m#sճ1ƽ`cl 6àlTac^ 6`c1l 6e8&ҦPz6&,K}>\ɽbnZ'oL&oWbGf#h)#yȿdS~2 RBI!K2K%7%?]&oO}y%g/_x]?]gtҟ]r6wJT:uY ɄR]ip=VYE}}6l֯7/.^Lz1,t:6uB[aSLEr>[NT\#81?J>{1y9[z"]_]]ֈ4n">tNjN_"(Or\]F"A$\_lɋe<ߦ?܉JYg@]}f  ??$Lɳd;_o apK1)#64GӄR# ~u۫ 2Hveӟ^8"&",٬fgdl'ti?]nMmSiHǃ 6/kviIq2IPPxr Dr=LWt44R;[;3N>7F$g٫zrܮ&Wod8ԈD=־X#BVQEF6ΧO?1!Θg\}l׷o7rD z>.kRd"^oyrt(ٻ^RTyl47H]ckk=7]皻jrp`&WWzz9|Ev3 Jc&FjdH.gz=OqflEl/hCc&yxP{z&ߦajrA5FjdߙtW"&'띐hÕxk,,4 M=zz/12#3䥘GgF|R9HO#5Ufd]] {56UfdS]Yr>|?Y.̇JуlL-7˷-d^akaVwݱScxqU^O,y7u9,=7fVb]짹.B`J'rXn?yܮ՛oyQE*zkbΓrܙF>˒&mʼnQA zZA.Dt?<^Z'?ޤ!i\; I_yg;s'5{Wsy̦,h{?w!b};J 룁lYY܄p=[]Β'a@_>zdBđf/G&{SͲe}FQz3e`ûYoYŜR>&xZ;_&,#W d```.8Zf2}s`gYfӞe=/,°OtڟLsY2Y余ϖ7W3VWϪϲγ9~9M>FfM5vq>|fQ]d%\$=&RK(f)p /?^^rZ %ڕARףM&RDXD5Y)ԡxsAUߜ[z檸 a~hFD3K/W)Ml7ݍݤmqXS䓤]"5dX`hP'KjdT,fYN֥&疌{t^fj'JMy_{e\l2U INR'iui7Y `O%KM`TlWt\zEf9LftHM@TZed1av9]]&}֔SR?"9Er[^_yuKgFWMzvl-'lh/GӔ2?9SH=~lCCmM|W|G&T~=N&׿_&϶gg(D#כ|W枼G7+T8/7IYMO@^%!G*=#DD?b="I@a37)gYmjv/4/]w*J?M/LG%Ar?zy.\TiCy^E"Hv;;9Gwo }ϞTWOԤo3xq2Mvݮ sMOAO;DOX(]kw$y|y?VfVӳLc¤Ƈ-w_S=g_}IO (IOO B}]ONfb1NID lZCa,jXS V_tpYUtC5lxF5CM@qj5M{5LC=̄z@4u4PLŽ-N (7}}w 0 y1h3$ IC:wDCl 4ѯwC\@PB XHp|:/>Lx_|}W cC=T|VO&jNHmU]E6F5Vۨͷ1ͷUlk*>6nm?AY?MEQFl泚ٓdsGlwkg;g "{ jx@fh= -/IÖ%)k{IqKm/dqD[JDmЖ^֋d2ly-%6lټQв`eDeDeDeDd7n~5.Q$IO}7ndy7[n4DV;4!J+$_ƕ__/iKX= )iaTpE!)NO@OO@/EV< [!Z!!wHCRg͇%J PxddEP$Wϳ??y70}ʕʿ>gI*/~+}'c*]ofo>|X~vr\޳է/HQr9?<8?VI͙1ˢ@.(-xY~,Erx3]6]$u~㹗}j9GXnA}z)ӟ~|n._;vwݗ|j7WY׻|(|ٍruY$w7xYs-~?U^n>RX4ZA^GƅzZcsvz3?,=svԃgo-toml-2.1.1/benchmark/testdata/code.toml.gz000066400000000000000000003725351453566013500211110ustar00rootroot00000000000000V`code.tomlKs8+&^\=lag.;9LL8h9D-%UoRH& P$ڪVu0*ωH$ǬܧO_/_~*_VۇYy>!_v鏇eO_9;^_~aK>#Gn t`U"lQQN3V|#eiƍWS&T3Q3Ujp&-N%^6ExP\<Q(fY3ُC }s CdyOTi&~׿#_җ)fGzNlu'lnWm]k&;5);voYyhl{ x#|*Pz쌭 7u~"&oU]$)Bhx"OCCΪ|=xAoE))1$7ɤ\KjF%m=e5ۓy ܴ2Q_~x9?jtBXS?Gm6 )ս RŘV=ZNkKb " $@$A, 9[an?y-*tuւȱԜrp%B/k_v8)N[0V[ = ]&0xZjPFw,czyNP8,pD Ѕn.lJB.&\f<2Xi@j(+43GMvh^dv$l&I<'l)FJgp>O? `iǽ:-҂bV}Y {|Й&2=0`0Mr%{y+LZEtgUZպ#8JG99,x݃UګW),]@s50fRNKE*b:-Zx(UvjYճ(- C\-65FPJ4fy:GWـNej>NXE.Vf\?b^C$g<2|J[vޱ,]GuBmZ6/.P8{vL.{鶿NN[#)El&,p]z."Qx+&!Ef4VA)9 7ו5=%7B5O95dyO3?Ɍ}3u1gVQo)&<\!m\{g=BI]~Ǹذ`c:Hu`* Q< Up(zVS!RUOOE!&~k/4l˄JR$T2NN73v;qF* [ Y?b(W"01cLj;VhtCVas>фXι{$.P)]NU:d .S&\C@TM/OuST*iU+=^œ&`S1%s&9UG2Ռ q@}9Ti#5|}FW1&2>}z:hgB58c[rsNb׍m48ga)/ga#R75l㩅@32 [ƌX)'Z<=1Fa,<`fQ̿Uy̚|3TT}6 .|PvVx\0i[>{8ośxN '݀=v٩Wvɸlŏ-/26 b5@X>n~#xo$X!m~40cݼ {{v-7\fXn+q[WW6NM9C'>fexT9sP"I9N)b-{ŲiDxW߷ΉBkmi2w*v2;{}Be?D+I[r)M,V-:GfKy L}ΐ=H]QƳqԨѾ׃8X+Zk1Mb1F嬵m@ڼf6Wq^e!]b_%V>9׳<9VxE]baO5 n:e.UJڶRqsxȈvBJ𛐑  jB-{%K7Y Tr ,l}Zu , ]="P1E37l4U"jyéT]Յ]ar!4*oSR]S U*8%%HQH/ϫXCDn2jqŷ=qZQ洂r(4.aZjv W )6J!Jy[|Y5`9xk#I'431A1UKNȍVP/gICtP@#/ʸ`sʢz:B-{7q\_V 5mCڟwSiw&ģVk2͸?l_a{ '{H#7犍xټҴw*}bnXD1™@,\qe7q[)AubFF+sl,J"%JS8 5 QctU?F갃ْ=yK*ZϱU C o.ɢ=3sّ|Ш(sƩh ' |{Y إLua͢;XVYb✱ `aU~qΟkfU_j7^2lqΟkz=-gT6ߞR),L - <'?@Mt3J*KװZWA<;'>0cWnbT;QUGEFh-y d^o:ᆁVkt+-ռuO{&>U3jfEvG$0UD7мt3 5ۥ,̏D \=q|#{{z{W4l>n-옱\:KKGZpDn-c썓t4k޸!ۭ"wiM5̻sJRU=iIuz{Ҕ^5h5]Q 8eyOTj8'HD–mD<,NX຀ZDW)fGf-\6- V-~{ ոDZr%$t@+A4g [е)Xdn#v|]'iRq) Y,i`Rl 3X{=|})A1M\hZ.,[Ǔ\02 hnʖfE2/ fN[WXN\Oj7}/ #[WRќV/Cwa(d/3ZWW/['ߍ,T#VI2i1!ݭC}$4tS f&OVi[8Vl6rK.ulv!{'F<=-F]|ToY D4Jr&k,PC;'w6QL>Uto1=LR冼?=.N]dH<>ޮ 8'T)EvLI@ڎ 4;-R Wت57;|F M9m(3 3&ߨriWoO(:6iWa?N69 6X^2XaU&'Y-JR[AdyOTϛ|>깈V"^bF94p┵C'ih(X27$ mҕ2on >_@K-T?gcڪ޺<;j8TbX/<7b.syVi^e~HqU9I IMe?q7yR|W/mA[*)[1;bzv1/M=3g0FZT( ;KfLg{@UgUЁr biq4'jU(T_-qPa|LrW"޸rh 7'"`W*@veyOT[WO(Zѹ8sF'u^Q|W qCyEՂ߽kVFeyOt6\49.XE9KE̅ KiK!\8س6?}12RR# 7/XZD;IcoOe(ZkOfQG؎缕qOO{8Fus'3}Ͻ3;c]qa4eᾐ) #8&'92;μ"}$M |Qz)-_ Ѓ|b n HgX|uܣ. LVm2&2J 8BV;Ń =hc)x`u+L`pHCÎ"Ø{eu1yQܕdChԻWI GA)0j5<>|ڡqn?Xmq&Usuo@)#xs*y,Ԗ:sszSF%Zde\GVSy^j=]&LakA{U深H;B\CZeֲ2d?w!ޝ`}qt7tu>=V\x.e8D{"ĠMiFCꍅu|7u7Y^mQz~?*B:,AU6-HaL"'ؓU]Pٟw37S߱<g[h(cb3„&Ljs\$ P"^o v=QQ喟ϬøJE_<-Rnخתw9o@9 [3Y ]gL_+>n:1Fv5> lZfKnV+F}U" .wE~C7ӹgDWY5JOl5@t\yu aasAӒGtmh[>ѻj~*5qZGx$\?14j )'qQspJ'g0S}H[λ,N )ǥ rZ}^#i9=ʤ^FŎ׊ \vH+e;VlKjֱQwmZojPB'R fusˏ1@)fF~"-{2\P0o׫b*mrxgʬL{.1#Pm MOkp_S\}o/|=jS5pu v)?*;/A7]ko-ݼReNss|Å_, .L9B>6}0Tx-ۤ;gZS7/'Mj:H 1"8GFICQazu)>Ӈ7O>?NSHiu')4്)$uV78J;7Қ&?ޫU߿QB`͗Yl;&rsc6dMg醐656Urb/awV p8.dDߢ;'7tڲ|Dp =ř#Ҋ;Qh X~8/)jҥH5 UFcZŀ5X4>ypFY%S[e[[Myp0d;>yJ/QWȢ"}t?撐I^ٷI^}d1ɷMF'M:J114oOoh66hmSeЏ>O6/׌H /x\Y,S>C{ nyktB|LM4Oϓ8*pfu!^ܛ麴 *(k)UD~$_9rZ0S"}J){%}z,`ђugjD֬_[?U~™4G_iٽ7eqbpb<,WG*YL*&_b9Ve47BC] lo2@X585rXuٻ! Lކ~]6q<]?%ϓ,^/⬜ 鿗W{v#BNcGPQ)uQTP!DN^$zߩyfˈed-֓^iݾMg8Kpm0×@V]p ۮwX}7ޣpߢ1{~B0H;8qנD*yݟ;|eX1|o:u#Z"=ϖ`]MM'xi8i](6NT']_l®hq5{=nyث`2uߠ^N^>9;p u8[ qnD<^pu,\Iߟ˜6NB'GW=A>Uߺr=7˂3r?`{ZR#gY֟lo6:݊ɮMdE/ceF-^}I^w/4RG)|(O/"RmG)^kK-oQ 7X ocћ~o?ty@9(%'Ӓw Ex^ͦCR,]qnQR.JKBKޕI NpّvS"i.0"I'tvqd}b7v2ލ)-c|8+4LS*;EzW@,7FSUxiI1LY/{{O}"=КvvE䲯4/*{{Y$W5sB9Д@ЌpƬwu> +4T2CY4za2~:^]D{of6˓dof$gj5쁇W"|=%ߞHdmTq:x~gn~i#0ʕ=.#rWHiSf&i=Wa&Z!MYSB[xY:\R%(-QpYsmMW9"2ňuG&SHϏņ) '\3IKqC]lGJ*CR&FZqJLy#7!* q[l;2.>2%y{ 1Pq2A -)V<*B=4[?m"}bV)eFǪKr &?uGkS==n%ƹ@@PR hN0{' "6S4nz߀w@l{/rL ~bSMF(^L"V7\) ]x1|;. 瓋7a /D`$hmo]on ཱུE8Qٷ@<'6yY7?n(y;r/ I}VKf/QwOrKw?8}hv,QUPaqtHZ@9FyѪӿ\oWY֜. '+Ŕ(?2N-J1W3D萄7P˒s<[ÊR(aQ`(COAxʖ(}{]ӴupZc[v+X}-}[[SN4JTK')COfRBjkHOnM"ҭo )%5!K1Pkh~~%)U-/ZE;xRLI gY}ǧW1jۭpe7v{mk%yطKf9=n:9Ú݆kmޕJ/>jR'Vr3n'[Y+4L@(k⎖H84a8ВB[:_U|{{zLeu%%?Wo C0$ͱF>ВҾ8}6 Kx* ,D) +UuG"=&Loh A7ђU% K6;H.%O dGk%k5?**^-zjS$=hVwIxmr^L7^" Šu6(ԊϤU `BqLa^}t%5aj> ؍2BfJBOyQ6"&74ca٤75Qv)ȳj)gC]Up/Bwz6ywRS]@ hDl( 5JU[F¿\pC'pt@\5t|?<}}Mu9tY7{xN"JΟb=Lsy*PXwP LPc M&:LT[ŌX$Q ఱ |4˼hg8q8)J 09@PpФ`c1Q$#}&jVcr j_SnXVd:w,wt,/"mN9¢s#^+Uܝ٥UAC௺KVF>ZL%Vnvw+ kQB8)6N2"9Jbg8+d~BJhg1S.g{T(FCU s‚%g LʐNxH<:7s΁J(C9sWHX. T\yi1G׸x1>z٫tp&B @ph>)Yyn%5:eq0EFlS-e.AGZR#@h_7^`KѩK(cf Ԍg$ԀKYfURe; nָY NTc/%ʊ>Gp_V Bw y^gȔi$3RFcë};Bf3n bvRh9$NCL&3Tf`?lp%y&nc3F d/TĶd?`^4^ouMß%aFZ- JĹ̗-B%S 6k>mg/MCBBkVaB Vca4~vyӒ鐘Oi=!]XB(VXU9JQ$5Sl[$*j8CaR $nI>º})џ/#3 LV@>a!jcw&YC,,7T^'@B b{F;8۔1тah_/kͲ8߮.{m2kr ;L35+l8g{̈́xdx*Cnpc됁Mu_ GC0R(5XlFjNK1/Y<_p{mbheexOKj+ϊbygövkBeIP%= zp!*,\3;jC `I]Pj*M Ap%^ U 9:e2gI{ Jp>L(M4*RA$ = ӀZk) FB%- dDGy`JMZ &.ҏMZF&(HP>Zx'؃s|S O iIW,[:F!h.*)>8_J2UTq%ye>}l TBWV>TfGC)wmP%Ђ#<n\]Yt(rMbLQâ6\I<<}%5 ]k6e'/poȭD΁H-C&poPU˺8(wN܀:(ZFQ۩-' 0{`T,KSP}]u= uS]Je CBl4bcwiIth}Wot1#\*9ᔎvN\t-gAE܀bD[>1)4+(x)?,C /M)?hI`qڠZhI,k=]v{/WFY g8Jwk Xe.rDʮwwl.X2GZR#!f |ηisI1]wcFqZӡuyDrx6lbBsuR֖ѝ?f)wL{M\,^'zxp3 VAm@}+@3 UڹsҜ" 9cڭ~{G]s[|4OV{=36B1_gφ1؜AFyU ɼun iZY#yLpY9ucx~_C+ה뤓%# <4: {ldg{T*h]`Vzg.6E8a=V+({SlL7giYi0,xy஑< fUoXC:_̗K\b r*[1-{Gҗ$~[Ȝp+YqHm5F1ĵWcF2\ Tpް4Hk1Pz MzJt FgW5Y?Ps&P+[v/9oAX܈[I/_ ՝B$a4xb D81I$hq/o<:,nNLw}-Qf[)F):&{4ӭ-|ouF +ʙ-W20Ʌ+z;kc-unoҴڦzEܥ QF2Aqu]p?B^omE'tH iT1w)\&y 9s]m)NfHv,gb zu`56 ;qbJeݫ[6 0Cۙkbss #wAsY,I->׹ϰ4NˣB1M+3G{&`;HZFR26{eaf :pNG6a?F[lilu0i_l;LxgbWnb . ŕ 'Uȅe04RzB)8y 3ߟ'ymap&NǬ-^ZVk Wz>J/վ4MXlXWyAް;mW :!w񼠳l=4r4K|co<54Ƴ<&aBL;*0M?0rb@ķgE2f}~Ά{(cUȻokyR#r@ۃ1'!Jkl_<0m͞ga0o6pawè~`4{ao&pw^ȟxeq}$Z++n̮V|bM& @~U=2ue!t/+FSb궥JW#[k ю.r#?ЅaZaW8xLhjW&tT^8ދ^W֊kۥ쵮a :nǤj=>4jn cY:Zf׻SjNeK'}PIlhKE`daE/ knA*',/m*_R%\Epmrʭl*t:%_Y(m]^^-Zyݸmb77p!QWFO 悶cQ,b_*u9l|H!bUp3'ΎKpñ%ɔMDGP K:TjYx#lLZPX_bںapױ=7uĮ%YbU&Z %ĩOh3O& _uL\a5kIʼn*/i-4Z o)pc`5rţ6 P/$Al0%<7-<5Z1]Lr۶\:-ܐF\ǰ-@5S!ޢ0f'_OpL^z[hB'xpw6s؁I,,xkI\,z-XGƨGO vVӵܠ.Q5é7#/ `7-bhJhBNXFo_'_y%E}\㭥S<[q򳣃6:ؔE^ bsj}̍zy~ fcQWoCLQJIG>aQ>qY kTtPI:Rlb|: &i|{+`qW~o3ưA!뎫,\0jͭtU~_' &v罦՞iciuz7s.lbS5 P4^ M,Ϫ~۠zRхMb ZB> %jڃy;,/#|pME-@!5+=DR1"~iifA,]#o-U@_%Ia`URvBxk}7̇C*| ٖ`lko)5-*--O{W]SNe\6`[눚s- 8XG* LJaވeۭb Eӵ`~ͥg2n ~J#/H.'1GY@i !|-TwO~dkT|Nm47Lꖩ[^u>drKi"\㭥ԧߟ?Ql::LLZg024K10:.͖a&Cpߖ6n*rc12LP}hZQDdJK:clqMXBhK-o!:eb࢕k[V4gk8=F`3xCo*˃汮oH/ k8ɶt59֡Dv(Vց-`^Ui .6z-{2Ή-ji E=3wùuI-Ǧ<-5;![ئWzd߷Zy}O'ˍ;k$lł{K~`NoY ]3)PJ.2^Nly^ jXbJх8{̅h\ 3麍Um`z{(g6o-Ugok'zN'YXwWc7ZRXImy i{v;%=oFAa ǁN]dr8١te}`:4:zLu%#BF( Vm`gh> Z㭥ZfrD+EvfVDSE0K]qM etSf hRWkݚ >Z'&DYnZuEըDIFwLy XD=E\VYKe J  j[՞ rh0U4 Çba;G)햽7^Lbv镻$Zf+OyU4İmY:zٵͤHEFKHuJky·x{;yeCGrZmWhGRޕvXhd:`CiD! RPLP2yåaܱp?>.{\㭥3*ۿ L5<\vo#mn򍗛}w9vb͇K)&&:R͹Z%];ܔJ2"ԛQRu!+~/䬰ea4ۂuLl~,hߺcg5*yPeXzပx{a㝶ak8)lW뱄p݌bzx[tvSPMlXnvwӰaXmT4ܑktפ.*uP8 o|a_]XXk`>;&F.((b +64 l/=65d& .]~I+j}mDX|A;Vٯ{p\W8ch'b5 ",S2^B1}o_&v?au4)VHb WH\c MĹ!m :DX^5}eI- 8%Kwˈh Xz]F]]~ͣ&-[FlGMmGF0t4C71gY}C:(.σcŢ]zր_sch]>)b*@ P~Փ9x[\jNvRV@)_?`!ak Hy@1(%C 8 gsμG]aN4Uhֶ)>ؐVd&܌}y8\g|0]Kt`ALȂ ybwe'ߵlrt$XN7t]俜}_% $ F ͖mQ^&Ĭ>.h6f-I#25KGg`pG3VZz_L11m35bqt7:]Zvu+@Xek8%ykNpYqd:|mнMϻm8цǧtu(091KOtZn1qy3V]ov1w nli'C ߴ/S0{ٔ&UWݼWLvF-l9k5,V8\Z2˝[#giMAr;gG`1;56IRZ'b A-m¼V],b`l MS@ρi&kB Q>9TƉuq1; ekN8lj;Vp(}!7ud5m.&.673Df$0ʍ!!7{ɡ`($,xkDB?yk9z3 13ƛm*^vN=fCL zԙR~j?v0 Kxq<e-\;2Ž#9BXi H[wC>‰kʝ =nG%zOlN#0=}ʒghC u{lIGݣ+;/>6B/ ,l~v1Vc,:E?47PfGq b8f78IkS" 4%e4rY=Ɵc8V;cg4^Ol 3F%515ŹWEpGc#q:M}ZTf4AYy–ž>Qӎ>ynj6 {}%f ̶]TwEˑq]C =u3ؽQFVǃ#{l{I:Aיno ?]-$i6̣dux64~˘CZB lNͅaʟ9žꖐ3l8=f2Oj+/%}y尷Dslװ5>4Sy9 e0J | 3nXlY͢(ytB蛛.4L:Vҁ*!UY`=U՜XM;m0.YJCdbgfG=aT5ؘ8,T o-@.YDI~GkxC#W!Wh)+tYAUje0R'G\Қo0^E4F1uiَ[Fݤ׭~z=v]j7"y K;ecq &e[!!s#ommM0\rHk\:W h+6wvORTP(PiB3eW  lCo9F!mr`B.O{b◼ q`Ŧ*lsV^Eo*qdG<7|==̩^},+ZY /c漝2}$岡a(P;LdҼSMBeI`YH{[/N9/EHϸӍɯǾ<6NC' E=lmս$YLӶq8ԏzp Ҋ…t_I@ ^ʢ@ZtͲo~tNֽ-0Tr;V;ۂlh3{VkTwhqFe˒\v "\o->]x:Τ{3+*gkEɈ|̚, ~/LЙ+8`eKK&k pjPm>udH0,l56j ӌZwO4zq9Р:S#nTTy=> M&`77le[Fhɀo-U`$xqkn Yzps;2]|w u1%H1le•$r~HC%no,. _;`;qp5Z:2)}f Vy0O5\[~xkD) Mgh265SVXel?o-v*7;R:9-7<6^f̧Kȱ SIη~<вgwI}e 1\Yd \ٔA{7GZkol³Q^iww'R^6~Uj3ߗ_+`ұ }6:HQitxk:co6,A;tP_1XamP#qkYL#i)`z7WkTI0r8.V0F smS \ Y~҉f8W{WPHcS.m<x%}8CЎjNb֨q|YcV*aednkG o{z 8Wktqi^g,8~v '+`k1PӱWT|.=ϧ:TJ,k6w~쭖ƃo=9`b[֑&\ims>b?P%O '9,A߲&[=R vZ[Μ?ID^pt(g|%8[ό+wQʯOOfßfl2O ̧1*TN,QOgu4Wrhe{Z֝_86ta Y3Cu4mኗBtuˠՆ]4ǻWG:NY[KUJ^٩cYl[똦Ft`RtmPh40 K=^ W=]et]6SO!SRA@+~޶bLs/d кH2A3y#iVKzR'^~h4l Dc)n9֡䣶80티60xҸa%n0H6q]l\p%Q}8[k,ThЅKU,]\Gs ņPQ 4<ѺpǰLA9JvU,G*>0#OCPN#; mZr!=<].xXx[UwC+tŁQޅu;H *446Xq)F$utt.xᶀH\^آfPb9&.ڲbu^Alcs%j.G!iJsB> 3uC5N"=^].r$<5U+x&}|O=ˏWT[/ Y0Z^@sGh%/MİrCI<~Q(ǹl?mu޽*mv=?wu+9iwxrE^/ܭ2cv] I@Lckk)M]Gl"!;YLtۆ!7KzxI74сZj:x*-eWcϷ<+5ӎ qZiڍy;Ta}qUx'j^5i#?kVb=8/s O2= m&1l Rmi!ˮ%EQ)TRCfôoզ/jVm-_ucKǞutbj]\˲rz8|`m: uMW4pI#-lڤDvH 4i«I0]j;>$jxOM 67!21,ݲp~DɃ5?S ;wu @׳RE ϡp4@k\,ɛb=L$kXQ,O5ZjF[W )sQ8 .!ᅦɰuuH^g& 8Zp-̰O+PN^bo- ̳FWwE jBl1 ڡήTz,Jrڱ%y.N^ʲא=' &k;;BXl]jީNI5 9-rH@7mBo-5k2M.b JlV7NƯɗ1‹bІ>N6W|25Z:7%ip(eg7;Hvpàpu='y?e{X=߃u[aʯ Bݏ *:Ğ2JYܔ5=m\%:arj&feքz9P.up};Ot:y7bq:nq4A5GhbbZ-͢HaA !(ePĠ.dv#p.G{=zerQ9?,\z)dYEg&Jc3+j[as a Ă]5(w]pB@;$g60T]};ر0t]m׍)F]jTMq4K؃Ǧe෮)&z15ri@mC\:\A˚B`' ׾Bcp8\H;0 ]<)c1  ^F3fQn"y+6ܷ3up=k#o+uU]:-5 f&q,QooѲp|^1@`z qfY;I&ym:Z`ѰZ㭥3C} (52MZ|v`.,I0tNzۮn&Aj 'C: a!Y71TW@.[e# n .}ڥ0ʸxb|`n I GmW :OJ.#[;$l FPl-\$lk,c2lC.bF,4>PR_ʆ3\X1j׆1 wL|y_0^?8w5_[KUabI^mWZ fcSq!p}C6V@W]nr2$递P㾦+@vyi39ͱM*pZ>U{/v"Ka:_wu=o!YwuWYXS Z'@hd~ o{tW%g4;0\۴,p^j6XB?֘n,8C/?g1&vhMM}JKcDTvZ ϧHAb|D-/Y8W/$# }'[? {akhB\[K 6ꓢj_K͠ZV ) ACcfpf"{)ϋ0Cq8q#@Nν0v:d,`Qk8(?\-xy>K2sbel֘g你s:VG>IS1CNL(4j &{޷;o8R tÁͨQbj]Re) ]`;j96터 FEe[+l c֞Q";^WRI)P؅[7,35'sXc_Km yN)TY.0_jMu|r]2ɠ_Hc7 !0ncc/֮g79cXU`L;f^16r爷\l Mђ]FBwy@pm 0;W/Bm*Np4h3}btΝM=wLTF*5ZjfFG1A44[WR\CL[h6+Ojpcs:VG @#b@wEZ[K,3aCs26*zԈkk( JOk7!nQE]F o-E]rǫqY(%&@̿sx @;ЊecN0Ep^k#CH;T$0McNO<('G½p"EnfM"٧+/eY/$ڇSgB_A=-;o*;w:llX~g$; _ T>JOQHT͞DzȄxh ;:|ϓ%_x}O+jrmy6TRoLmPEzb3ו**NK"gSRwTSJbmZRDc8$4+fA)ʶ"ΰ_~FEQxOPpD_3SF%̏*26f bI%J& ֠&,MjiyO͞fK0S";T{L~T@#R`k'F1{JVȷӁq>}U5̟$W'TUP~Kx:g.uP=xՠyIg}؇dTEľ0/D`*Bӛ<|Sg%){W keOMhr0tsnP[#;gK?cf X]xO`A jC='B?y*PXe#EMdTexgOO=TSRAtUOYeBj&)4T"D2jXJR%4+I y4R/?=1Z0\S!OX)|'*E&̟0{u'hQhň"=DۯIQju0Tٟ*N,gO[%5XTף%o^2杺C"k>`v\xp{7'S/ ?{lDk%8S}4O̯D)(͖i_͘'Ndy7X3&`_|xTcjLUj͌i5 Vl5_Z~>tw31k?Θ /~Kz$iYI9{ C!:VaƔ`K~4qXKC=K~ͣox ٚ!?(1E}ڲ 13EC?<-*tLu8͞8JǼ2&JfOgKC>zr.ʒ(@61M,"I^3pTgLCqTȌI(|)>&_͘19{8ԱewlU sK5w:>c *cwɏbqP ] >|lSO|Q2\{:H-fu>cf~d~烜q z%+&f*(D,'E!jXF("Fx:`<,cpL=a_)o?,lFwG/"8O KW"磗M+AT5{w&r2SSSYΒ(X1JPO3Sd}*[u>yJPSϒ|.4|gC%JVD=m OOUG{T u(*nTɏbqP5M?eaGQ|{*A ΊGT}HG<̔ Tch+fZPG15y H!BuuTQQy_ ¶SDKRE\pGS 6JeWJ))CѦypv(F٦5F:*h^U@!.Ajk&JS%gi+25xۨ"J,%h™^^S%T&=S%ɹt[Ϟ%hLSA-EyǪFӖj|,Q=/TEu*|K'b:SDXIox08~gMF× s?0~lAD>S_ j!O4Dz(ɶᤞ}UrhDoUsDK,gOD-7AJ;1pIm_y%ȌC`5 " FCǀyxCR+\'F x ՠ&9R.zScfX$AP8G½_[4V7VwS V <A o^b6Z I%*qfQ(Eڎa<D^rb7lkE VUxb;m$^[^Fw)D_]WD5+xؕ?Jwy>]yMC^}Kҩtt١׌XL)$e|K>+ŗVL roueSؗ%|j{} ?YԓY\/*Mհ~yz_"<9-<}4{||2j0Ja|jzJ%Hɗ(DačGiY$! D &%I2ΝB>^P!_Y=`Y%~JDqqC\ -gjzgʣj& qzCM-V,PcŤf?V&ӫ"}ɯ'%[ p*-|f|+F`QTEi앳OT2**ԿUI_岆klS 5{<,"DX\f?Hz;*5W-&[Y@TRL(*BtռIjl@zPkKf $ޚG"(Q0|"ITzZeQfɜwJW+-K[(Ũҩ}Yc7̸Ɉy{}K(*PUd/+A˧B:|Q z`Y8j'&di0JP积6@MuVm Oϧ=KٛW%xT Ǖ*ӖjHj": z zT|} &=G5ߋ?uΡqAo _Pnm0Fjp0=O~[*Z>O< -~2Q)C󱷦 (s(eIViG|25tTjhv^3ӕ1U|r2+%RXZE L>GG ja%EzuDۧcWMeIT|HG\'ӡ:wHM/SS.b,S]*]O~T✔ 1VHJUdb$KT*[F O*6WxOjejUJwʟTPQ`'IڒPj=F52aqd˔8vы[":7+v5m&{̔iMӨV3!_MH̝N-ҔJHI^;[G">]D Q x]y^;Y힯MWewfrt+4LoĊ"IMO\_,>.BUHɶIZUEoLcgO{À Ayo@=Aa)[|N^!,m{bv,J+/QQT!R j~Yj)?UV_y>Ľ ڨAGw?C`vd}O3KXO9Az"^O );h/u"mfDATe%ͩDxj' C>} !^x:M"|g"7-sR\I5-rs ;Eǣޣ}T3 [.`WEN "-?7H]r-y"ls3/7k}ͷ(> JlE7˷SuQ~cp?R͵jߎ1C;Zߒ:ͽbkȼ΃>+)_%{yAlǕ {b, Ӓf^OW^Q%͞ a(oI3>e0gJsfON)cWyIl;%8/bٓTjx8C(WfOVq8K@UhUц|/毩B C'U̜@ĊԔaf<EA?=AaA[Μ(a͞MK=ĀD!7 fZAykeDתmi sid?E CoS2HCxiɝ쉫 ?:,u0P/\w~7w~ԅR2[y);5-5IGRkU˰اߣB y{ 'B4Q{-=EERc1dԟs]RMo5V| <ګJ<)<9{ڲ伯۩cA?umYr^ (礓 Ig8Ymu]lD]6bX,S;?&v(?Fetǽ:E-?ZUN)Uo8fv'i_ V,rpfP8+҉;w| pX|܈kIq}#%_[]u?7]b˥1>҃xnW>/cpL}O ȧ:QȂX?i3tr9]܃rER1}H]g#?H_ |}9Rqx]+ R#Rzb_s9;sG[&J }V.fS6f(2j{x<&-eirTO@ُ*x@#(O@)4Jo>ʾ=@PwP]- uP=(AU9t[1-su:BeP(85 { gPy-ղPU/߀P'mJ\w[(N2d/'Ȃef̕"n7ube9<@;NhܙlYLmѽI. nђƧtS4Tmt5 ENJ0xKn\rG^n]U1jzicˁS;nj;=dG"bRo@j"+%6cޮʅ%RJ`{dEw¢ރ0raOa* 2ܲ2׀ƛ8L<y*6s&zPuxX=AVQ_iժ $JmmV ф+ 2 qu84C끄Ŋ+"l8Bvcb@XQ4:ɛy[mS-جzoZ;*=Lx.蘨:S|鶠t=FԎ!H w32H,԰|/HMI G`d*Ox'㥍܉pqlC>wԣ'vfu_(S}MJ5&Fy8/ʷirtav鎷 k`Y'H5Tj"4b芥7} !;Lhvq7ZMo/ 6{tNUwQX9Tp]Nxͅ_ m`-{e|^*H8f%?~e1! Boŏf k4I=|IrCU/O zjW-HU.X`ejR*Y~1oQ\X"0Hޙf? )>ҽ EKSj|XUZMUŒ`&eܲw";q *n%xdMǖ fuTՁsBsB닠`sn|< j\~7,+hp޴-Pk c$$y4b$Ki[2fQfԷ=2C΀g\W*IYqbLczWi>ڇhZYzf=QSp8.5CM6_$S2u'etb)AnF]F,}M5Ia59U~bHaa *:\')Ss-nc|M7ZoNmo_I3~MDdKi(F.<"kVXD1ͺ-WOL}TXߘ8?>},Yy>mq!}SWLTGȉZv ˨r# .CCzΈ81c$m$kQ#ۙ҉폆Q-&:*'?n|/cmfF.C_#!I݌"X|U)ɕ1Nc'b?= Mα_5+u}!7 A1ߣ^q Ȧn/c(&zi\S3F1uv):%Q3A.U*8mrJ׍w83h| 92W61QowqơѳpVǷz#vŵ4c>EgE f} vb~`QiE~8gZdpJ:ƅuXy'hZMg-ۇ}eu CEHgo vޕwx!9(Ȃ}pvTx- ń׏&$~SL#|3S"UQD#BHG@B 5{eNT25 ;b('O@ntTaDH;f(GE4N&jK: Ôg횾?R"~mlAde-j|$|G ۀs~bz+4HX"h'q˥:^ pGίgA֦sCe30-"yZw}zpMLq"a-U$H6@G_Yz^\[ ÄъQ7@F{փN K6P ƔĻXXŪwhSUh&bXfHS~_?{ZݺƤ .+QsSkSh.h] v_ؒO74jar5I-H6_td;knc :JJbz zg} vq%ޑz+It|z$UaZ4PNV ܙ1ܙ۵/iQ^+ͧO:V5fyLX;VO/N"*!nvWb S tBl_$OғIuUN7F}u6~Kv}g^ʋ>>}Gv*YF) IN>;tw9ٌޣn>_}-^ jAtOƉެhm яNr ~9<Nt3en`Rf L&ii;l?8sh^p=@-50y\!BOR>wY%6OV@+%Ը$iYLOќtg;Q:蔷cR'xrܽKggx,,2q9צ".d<Jy*RfG/.{yՉcU3tZ烥9il?ς | NM+ I_jn}ó|O{s$e4wIʽ˙˼;ל}I_F-%eʱ..)t/! ^X1] BOm:.Ag=\I@% ~- 7")g/)1ၠj05bJ#8'&}J?Fo.;nyj@WNqDJggޤâxMie9pq=<2j<όAX/72'Ѐ6\W_Gpk ά`j*T-Ža|~ޟAtzY1(@7eޞi7X(}t5jy0w=_o fi4T V؂9X Ͳ4pE~Є6420R/[2'6ۃH#c0(!D(z8j ZvqgpD|T쓾YU}?칪%/L1r{ԧ|"cqڡ VNՄ!ҞÃ)I* }:0>!C!%! X/N*X+Teo,`mCdO#js`сSv_j@ݾ2X5EL`27ZOsTR_7yW)@텄G㺰\(&s |rn(sDŽY4l*΍oҼRXbK5Pm 5x@T?&6COc,]A"S=X5U3TUUAIЕZՙ9o׏w&tA}d =; ?/=vܔke=l!eS(&S+xu1uv>1&6oow?u_XM-Z"4qSg!mHᩤVo$}#:WձsnڠOX0^X;vcһxVRh4] )Ϡ[([cĩnk]ԫCeڜ!6 bT+j. EuBE۪ɰMXVSIx7hRF7&ɦ 1EgՈRg+!Ӎ]_nCOp;ك*ܬ^'ǽ0fٿ 1}'Ms/ U=ҟ G1K6M d&UQ7/WpGWdzes5m!)!_~y:ϭD9XP;I8(}7jPkn?*3ܥQL]BjM{? fS8N)pTvyՕM=ϸ&hz[$ WBv"]z;qn9y|+u ո4KHNJnR7YMWjG4!8N]#j|:8;ock^O*B啬0-L+eH#[XO u^|×オ*B0V ؎*AjdEO nu@銦*JmQ$VPse.R v]XrOj7)T?,0:}Ґ"ۮ+ǶO7GޣFIP>7FdZ8hsV2E|g+ %]G7˙_vm6eps`);a{yy{ffvc=m*_QdY"/q̹ ,acIroh$}=+++6Z=E$N'%0bQ* 1d)3. #H"viT87gXx'^QK>ZˈP@sK9,.pg 0_AG;P?\l$ٖ@?UxAߏf> Yl c\Z'}f.5p᳅v8tsda?}D XRRAap#SX2khaRĮߎv]WXT8T†Aw A*Xf%R_[] dwˤ= &UݦL3ݬTh@`1BdfBJ, qB w#wwDn-X]q!k;ڄ0I`W?J7,e]a5I,q-Z`,pSSmCN$/% ͳwB(@]%JW:*C/OTIccĐaϮ5 f!a2Q⪎AsV%GSm'+TKL[BaR끌 z)!)eb|L]IB е"iҗza U7/Zt fZ(FUDaYfl _-.o7q_wG傦"㠥6!;V7 FYR~DcsRƭcT kr݈YbX`'_QI +,0V.5t)^m4G[f$ &-~{Jlm!lÏ| eN'â^C&/4㵙_Mccx㼽{{g2`7YxY?(dǼ+&zP~+&oԯ`<g^쌳iM|O` k+@)W,fڜ3dDP R-İyQ _LqR6'}RhBUa-,]7hK;X a۶2ɭ`SH*4$L[^+NP|ȨxKmrszq&vDO}Hͤ;΋:yQ"ZQۻ{{ nS'5s9֮cij8Zb7V<g{)1Ô7ê{_>Y25C$04!-W~l( K:[WrH0 N5RdPk(Ҫ9:YL`S'b6ļk^MNKpPp!E5JknmͱnOa^Rԁyy˧ f1-P6!AƘjPf>s\ZXYr +_ko%r8guT<== a?w19nqãljnˍa굷5_w,;{9suwzH7/`}LkeWSh;{EA}mVEiA}M KC_/r^S f#,)&T2s#tg?^Zf5s"LQp> nY>At(-\Zb1QbLg\B,4O MO:Ua q-0̂$ |\ͼ"3\ FoЈF`>uo0*9>g . - * ,p+ ~/6ؘz" m#wq'pASW:4jTÎc4ȪRsUk0D KTbe(x,ofFH]썇X b هDIpKu`C!~H>]s\kX/\*d wk.W lI1H6_v38a^"f ,pFD*{g)R**H&-{ QҌ",- ^CW /7rĈjj!%~cӢ$.<;ogY6N}DZ+*Z$AaʔW K"> +U,o! lBTxY3ȁ d̀*SM_~k| 3̚5.Un\Ɓe zC4wL QSs }@f=8zA,m7f-)ЊʊwW&xx{?#U܁{flW{{ۼ{UKC'=s6Ӷw?ܚ]|fW/eλjiI.16Ok8+-f0TJ&*B KQ'* qjȇ]jU樖ّb|k(!xFKV2KT^\o -ھV!P>xܠ[wF.X-:U1RT|Tgb^FNT.u8xcd[fGk M6}}L/:|=O^9jc?%5mwxsjP]4Gg'!ZXYo;pYǍ=nnyr ^EJbۊ!ZpI4ژi >elG#;AkX:V][7!Ӡ Rfrmh5҅ƌc"kҞZ07`̘8-1jpeq"޹O fLF!L͜b6XrFMx&Ȃ索ll(5GlH+G] MMaj:QU2(^ɳ'o$bdfmH(6eMME!fRyjɨU ҂+ S'XG֒7k_:c,ư~>J ` YV:*$IoѷC8Gb!ESM#Y3M"Tn唎`b$>6~p5]~18 KgwN}1Έ 󳸭.`ofzqw=Ǽ- wun&Gd<23"jx3T?scܥ2|g69u-pn=ӝq,D0Z^b:wyŨ' ~N?m+2%5^hL> %ܟ?z2 N_r]59G!b1:{J26MRc8hwSd^A@r,b)=Dtϴ q5(f͓Q3S4t5u^jf^I1Q襚!`/œ+qݜK+hAhKoM?}OL=/ٟ~Pɩa,h^9Y-&Sw'WG7;򴻅Ʃn3u. $%?mܪ0!ψ}S4y^ma5'edtJXr\Aޖ1dZE湋z^sWk,ml!ClzpiӦxxr gj3ʃbL<|ӔE_cte5wp٠xKLTcR2Gy2Pj,h,,B`6PS|wtk ј\+l8QG$S'>u45y!SӶfY+=S+pv-w5Cƺ;RkfBٔς6SWK7)o}S/>u7jNs29]`, KP ֐(37 YOoF"K1=? SRpՃL=G>u';nt!d-NOJS:?z19"Zmc&G5>cɑC DzWYPvLSB1j5~ Z9[?ش.hXvнYX? ;RtJltG3j47P$֥u1xs8Clu}=y5 4\Ajzx8S* iZB3FEq)qa~C;䍋ZXY"g#OϿ&0ijNDRҙ( FɓBe_A!!,r00[Da ^gੁ.SvBz_~[֌ϱFBHx)B $Ĝ+6w -x~'&e0MXq.)`K0Gݰ'է;8vB// /a,hkD&kS9Z@G)eڬ (E2Yg1j.)f /=Z@7 "![Knju/E[)6V"}$ n\Br2pFWjh$R5N U &Q|]9{#u `&IM좎Rj$ԯP:$Q<~1. ZĐ[1FpV_+'nTE5m)RJJy !Ez1,>?8LM/\{oǖV_GJSMI M<`f88VPc!ɞTh3`'ihNյ}afP!ث;f9!{fn1MF;?ܧ0H ,( O|>Eedb3t6<<#sW{ @!n\5%\YXBdj'GF[6h_ <˻W"mij7iް##ӌKQkAF p鈛}X%/Mg`H̬y>u SC| -&o[&CK]tWaず1 -azTvc]@y~Dꈦto4VycK5 k5솓f\ w5Zv5 ~EQY†[%QYA0nieHeb&! ޞguzm٥QӨ:uSB{4YurZ +K7w;/ A( n'LЗL<"I0+,xqr3 @&Nr'β=P )ݻ )( Q}`m}B+i;uw(7]ecPl>LC\p̗N-HQy0%; ?O⥤6tWe{ީN-f',+tkAjr">0}K":aL!yǣhZ'iO0</CU1Öea]oBJ1J@K4%Dzjtqi 1՗vyD,JfޏFvރ%}N;#PH|,KcIK@Ї8WaL4:uœmⲇP aQ%$a C?א(ɗ}牝$Iew]:*AyE)o|<\,gᣫ*y|SC+g~4qjW ^ې 6<͔F5[Os'\'VI=/jm[fHd;uHReq|kD|&1 tGt_NgH>DS`)DFt ˷E!#%~,`( C(\SI}mzW4s|, 8#6ƣѿYAO^F;UGKE]NQ8,ᒒ!c4HH#X͏'x_]nyRB?$ˢ7"8D٭{|YQdx/vlVy;e$h2b|g5 pF꫿Y 5|7ZgW!Ԍ ' 23$r"!8I7O}T ^A{MD񮛀|_@OKe5 3u Y)  [ި9C+lRyk^9Z]cY?~P~eY׭qZqVꄥn7]!Cul<8ݸ̟pѭh oІpT#}Ta lbI}SFťֵ0i҇İXuP`5pOfd3c9'&TDŎEv:w ꑪeCleV"81JUzAZ[n87Z1,q, R sӬM4S%14blZɴЁ̶<. "Z n\P{uS7[+@9!k%"[DͰ0)Lat籭Ag&tp;9 mӾ!:Hcyp}El|bof)ׇ͇Dkq2h kdMUv;I:(ҜFh9M+hAT R_w1#T g!7]?Da8Eic?ޮ %њ VHRutò k(,gͪg[[,իz,Ux]Dٹk]ZoR8 kQX+"66 &N[7Y-I7ð V` Zrbھ`~/ rh;N,H4N;j@K`ZiGF:[RR})gC-OKk+s]3+ˉDbTnZw |EY{=y5FjudΉYN&H~:7H"I3u.wo7}`8*]Uf;ʊNl39&X mA۱{Ӹ *UaMr}T/RV8zPZE^߅qx+O 5[-Ewݮu6"]1WGmm t(2ϸkPt`ZS.  tu9"؝,V?TÇ ^fnz$1 QE<Ռ{lOޑ_C]K{8L aSaA00LoLN/zw8JdgᣫDdKvi ĊzM'#+M~( %8g|1~VZ+z5y0/M yUWV?C'[A+'0L%=? i=0P8 $A **L#)fHSlޥa>GJATw cdk:nc P+iJ8rh1iH3}b !~}Uv{0 8 b=kTpJ6rٖK4;U9nCHSSdyZI}$ehP9/4;($(%=֥**8Ap*qe 9dZ'Z޿?ʘ,%A$̦ ﰲL65)K5ҘԬ||~i1oe4eFIɞ² K2gpu^8Hru#oj^kQLڙXz?HC3jl$(Ӊ4زV5D#G*i%_{~ii, yքfi=d;?L ]?лa%uÓx(É==28?tfفKoZEoz FxG %йTy @Ls*I=?o8k~@KݗJL=%ͳG^SP\*c3V[\*,pK/Q3s΁p5: 4u6 /L|U-#O"P]j8*Ο%0;lN-H[VtImhwƸ ےЦ!i~y,-Ԣ^4γ(璝s)gUAq:jsus|˚%y;`Iz- /eNr8(M`#_WZYX Ĝ1eU8Zhv-0-&*z'q6Ox"'E~r`ԫ"xYf91!|虡ȁM؜TU-IZbiX_G'ٙ`%Kx}f8,\A^/っ.pt2ejdW oIa8s"pf R)5 nݕm'(vYX%L_govE=0M2p}DxqwXc ]Rhot@LgȺeC L j +k}QЅA~/y]FXq|KSw :&]SaV:ԷN~6fKpJ*_dx>+{f ۂ%= ipCٙa\ EYj DNAiM r;UXr+hv%zfϵhf N>a4(WRv20몢e5Bm >Ἵ! T^͋1Wмii>z7/qW;ZzG-2_oh.©X$J<ZV6>u"b<G}X;r]L~ U917\Xs]{1P8Ʋy5twx RZ+ P3쳱zs & 2R܄cu'j8Z9WO,AP1R=O65p ²}ctoF]޹+k;C>rH,64/W9 !<7 %5Aﮃ PYKe/|'I(qei,A(m[Gqz KkP Ï4g1`> g^Tؐ$i J/g砅u9m^>4pPiY呪^uz N`JKKD6VJҴlJiQ q0(8؂(ID59rVN8^|D:FMց>ބG'J XN E70m!\#* rh6|}oTe0,#*8Pϖa_ݕ sZaNLg9H X-=A!9Ҁ%0 %A@~yk=Z[2暀.ZםErZnu]6yR i*<ޕ[l0Zǭ! 6Y ÆVJn5#eF5ռ@)nMBl88| _He4ab U&qX%|7KR_ GWk( ݵ4XPdrH+W l.#8akd 8,o5?j9|,Ñ}w?˽װKqPvq[+s\`qPиp <hY9s˭9o֖a-@ `8qotn)p;cYEeH"H0 0"[zQjO E/VkWB+F5Si[qN 8Dᅃ`]ktj6UЮ;@zqDp-09B8(Apgor8KU̢fAm)?+RO$4ޡsCe Zf$~nŏ\X띭l`N?TlTKCt8AV'CYi)J'K喛˚/^Nup-9(.$IhJU!ܶ75s`ď7VT@1/ >qJP鿜̮ڇChnlY ª i+Ya n|pE+ʉhcUA C3FJhqg[Ѭ!J(QZd.A+j(rfy ) DCجbޓhWKuFx#+ՐiszROױ i(5fG=OW8%_Tzv/"jYAdjf~>i8=;JJVmhΏ 麪n-Ѹ166YlqL1}qr4kb5R~AT!XɞT[i:QuFbYCo`Ԫ>p`d lzbk?Xouk V'^g2B$-Ma%O$WRacX78 muu2fa8fMb2sepulO与b:io T\yo='q;a`s]IQTdzűjʂ !qwX}TE>&<*kJb{ /LhA< nJ_tls^+%:!VX]S~ӓaX8p96BxWӏ?b킋'i'8b¨,B ȿ.7DFl>*/pwIiz @k 51L NfO H\A}^X!M2wsom-0$,{ ?%Xq "ha^?c=lɿ%JƪQGo ve-hreKA)Fփp5B6q툪!ܗi, C}Js'蛀ȋS>+MJ~pZqhA&$R!(G$㫿Y%p{u*&4xy1@Cb |2MhFi3 ZWcTzߵSЅĊ8 Fn$-9t".؄(/_n\rNB<u[2zkbgo(2y s=[Bg`?2a*0VWfWItڥwZJȧf~6Lk:HC[cWi~u1NtRB\o$K 4܇2?Vy]e{KI8+bx-+|Kʣ:4,21J֬P-gQD!7Kp5Q<Yꮓ8-`_N O<-$Rqi.":y49h녅uT,L40$QMsRrj}ooY 0s -\9hoގvMT|$qfE1u+|V+.#x]5 񠄮>L 9 ht?;0=Vֺ hkK*4H %XCv`'Ϊ/#16ZϐJ?v. Q$_-wP>(+΋v7` }sS>I(7 sJZKXx<#sso @ xBj-=jX` ]qyUv )Y BF>a3vzÐC{{6 aѪbyh)ͳ3o>Эzk:nF[Y8*;  n(nh-8+rmM=}CADtF> F&eM:i&*g])XS˨튶*l & Krnhޡ8]vMe{&Bx?M'^tnb &WF/sX&>w'pU3GhS{6-?@(K aotr^W5R;T8AH8J ,(x`St.YSj‚,@Sɯ8`q$qE~$!NVE 4q 3` M-X&:붰`8؀#$X&zC 8[4[|X&:Lp,Str]cK(ǟ;Լ|cMIj}paCce q@= GBLv 8zWyyrhTm}d(=,򪣛C\Ϭ*Kk3WKsЖ*4A;Z1wHUȘK l=pmZ:iY=`,6v)5pm`M)_]sT'ch$Eϭϰb{) C”ZV+JZB KzgŸ8VPd{>8s9hfʼ3ysH>.#S/AՐbF:l#|oҍ8, Lj'Z/qĮ$P#+l/:@׬fp*WB+h1UrՓW'/_kD*l{L8@Go.̂M)RTF K/a_+tgHI'Nг6ժAvݮݐd-Te~beiyb\CYY8^-J վ*k)+1lMƟNQAn#}? UVrWK*z{-{FQKqBix~=QӐvt3kaDGM藵u-0Ō֯^A)3G0YidYxD&^/@y]i\$[ENRs#^3 .@X~/B?Փހ(2nH]+"K]kzhO/CxC}v$ƿB# AÄ2k>y:BI,Qj>"5ꨠl]=髙Tl` )T=#8#– ݆ڈ1 Yaw*, C2qb#ȇ(hP.= Qe1ea`\{ $1I5LXb$ .F0]*,po`"xq,eqrǕbr^u/jk򧡁 8a:,搟.`uzp\7uCpfK}@`d!J3!p`ಀ`K?uk$Ì}p5Ü pI3V`Ɓ̞ZEu Iyqsb(`>l4xp1: a0KHƇ9F!ύ4P`[D~QeN]$ܜsRhYT(t*'jOZ'qKAf_ v0 +Z@a(ܢ{ԚCe(T*UE>ʢ-sfg8ie~2M#/lJ\Q#=B~eMCzMo.!Pnڢ@pVMT+nZFpЭ>k6Y0gAho>̩iY7,:k|zaR[K֍ZDocUoJuSh|bzmCr*OR(ݯ#L6Rz*uHTurv8 s^]69Q@H,͏ >@_;ƿx6ߗl6 hHvEYdEqt`DLˍ1/,ַA jkˠ !5K)`Cp7QTFx @޶_7y%iW=PiiW^.?\nޙ [5@%BQX`(ԝ@J\RLC?1.啟XY4%!(=7OmuYsȈ `]/-ɡz]A#J6rS?!ܔp= ,8EcGY/x́ 8dN,m8F N;gW^:?R t 5 OԫcOXC@){+#Ǒ ۡ^IBk4(uR+U*L F<ޕT/ćKB?Iq$0BHdM3ȡ{\xLPNL9w'Y 6AB iF$aA1Sȫ:do9RUMJJVx7 "\ BYٹ3#>NW _M )D ~4༵sW f!ڦ{5+UV#l8lyxjn̹@؜Afړ'Rڔ$$D\l$[Ԋ^Sk gp\F>Slj+n*` Pq&%[|Z1R{3!9/ߓ5_]0O٨2:žv}e8Kr9lX0KGq3Ѥ3+騜&M3D]0zauW ܯ(Hyj5?CmHб8DDRxU],G*֋5kOyPېƣ(_̲0|[+M+f۞@T б"=.3ڎ-RYȕ @/O\%^l\2Lf΁)HI֫;p}qW* "CCS>VŜ(1Z(_\.(ArVt .-#40i>M0e=ؗVpC׬J-_.R߉L"Zpb> oJ*T_PAp0Ӟ/U:墒T J*Aϭ >s{'hedΨ)*QlJ@9fk:xT摣=~Z7'iHU*B`oںNJQN/L~9*~e-;RuOL {棴bT{PM6V^GfDx㔹AfVA q kO `Dgrn ;@Ou"pQQ S̟6(( (6,p|o$A6C]]п $j-M:N8m[ ceԓUox?k١T@0]/J 'Z)w:4Z0@+0SD*]]Nu9^|Twl+Wumb2*iNʼ+ǡ<(\wD~Dz&F}:_WK|oyX@ׯZᩀU]vV%4ςIW 5#7{uwЪO+ֿJI=R0uvA5?H~€ȴ\wC[]kfs >IZS1@Ie>Q!8 q;BH[3s$KfZq{@l=-}=h=?X!\ҪzGNz%=ax%ڜ4i' `+Oan_}%74\S&8> "H;WM8Ob6vko`ܹ$鯛Z5*ݮk+л͘39_1s#cpRt kkW(]ɽ 8-syB\f5t v/l`!TMH)o/VQ_:En\=z~Rc T p^mx纐 nubeٻm,X @nvRIUfjj[}JQ"l!HIY-")tUIG~s!PJhIIRISC]r7LBpH MfJ^i !E&rb7Ylᅥp`^l`T u v>G#]ŦOr Beon~FOs{ECꕾք T_.Me #@W-v$BsPԑNŬD1qz,CiVsNLjy+6;q%;4EOv^}~"؂M\%8sBע,3#x4;tZߙ8o-w!`VȝqÚRzv }Bh?Zgda~ub BQ]y\Are]ғ4 E 9XlAd*̼buegZ-yn2s#uK]v醅T⌘KnCc`$.FyBSHyd߶0;X-MW2XškDs56O~|pDt+1.[KM b^\ r*/%y 8ԛR=*ѯw%@`ߡ'`b 0,+`0qmZ1'|q,/]Fwj]n#8#ZUj_G]!f:#C% JGވqD2 ?X+@2*áP9UǑ}0`w,TRÄ+p!{wށkK"v_I LT̤so8}{+)$b3IUQZOO7/uWϋQIngt ˜͕ODxnx2,#$J{q܎y̧;z:x{+ã[_|F,W,~=i"&B.(QQtgFr&JחKVSѝW3棫kt}v=ZhC4'2opb3K/wOr^ި9u~MZTEfmr  ZA8/ofY$Cfxȹ9+Bp y Xʪ L9]y07A:-A0LI6+BgyruW)Y s?SpWuԇ^,9ӊzh'm軪@Šn)0l!5C= F'jYᴷlKv^[^^!|,JNW[ 󯏾͆BqsL% zsL u[rBZ |E/QrUr7 n|MVRA U-x(x"b2}B6" 1cVDtO%fcNyXp;N$j>dD:ELtrX.E|z3p7b㚧Af`W뻵IPN+6wYPqigc7*al@M*̔]IsSTۇ{n>VkER0YTaOMFmכwEWȶz6h!=G歄}'W s5 TuqL,5E͟v>Cw7ku{jbnj=;QR0PK!y6wǑ@+Mh"uѴYk׷(?66ữu@Uޚ@jC]pEl !嘲*qM$ :a9p*;j:˔*g_*U}ulNŐ>XS#HKԆ> }eB*u9j@!d8@D! .R Dh#q!#D_s*#Aq<Ȕۗ6s};^-l}Hj%dNlϼไ}v1؟Zj^MugVQ؁L"-- ,L!V {<;x)Ew8}5!듯:lMe 7.g\ )x&l" s`J]G6V{'Rls3x"@ k~C՘ΕX?TU&@eaRG8|N P q y+fD9X#AԈ7Js[ u9D9]O$Id"] 4#U:ry ~lfW.+Q"Tۆ! S;p*K/o3ӦͶsRt`7Jqzyq09{HNcee#a =%ĖV5z[уZn-.ѹo㷁F%͕Hl<7cC}A0ܕ肍mk9^;bN "t4xsV?,! *\PUV~ l!NHw~qéZohvY="j%~vǑbP֍/z{CBN\u~3e^=n~4cG.:in[8gKpkQ% k  YAG׋!ķ{[Nq.`i=zlZqlScs19rŷ:Eœ%&NfW xe 3eal-a*cFq|@˲$%ݲ<= ;UX_9Sr]WĢ{4vP$Bn=at8y9nϰBi BuGc@'n,:ԖͩٶCV[w(,uAN9E9Nj9_uV6wUw}>ԳlƄ,m/O!*K^\HnqkaTBeY弲<:jlP#@ԇhO6`q #xRq\ H"dTNr%C"/6za@: D&8q+$Clf78wkך?±tEVMu_=کqiȘ@KĹD'`aZp+4Du*?cR''"j@ J* \5H@.*G]>\vp&1&8V-V!+\PboYɋ'QhNza*g4yB%ɼ[Lpnhvn lh0hSh1))Y(KC#@5֑cе1*_z]ד-P7rK&qhYp 3Q6&K$UZa i{t=4]qmp;3p@4:mC9?~@j_Ag ն#m70 *2~@4w5*̛\-"g#NkC^A@22rc4.&fGp tm|MVs^gOυvaOgګQ=gc \_bwW=?!sSiMa_8+{Kjwä,vc5W/ɒBb;PZ/Gbt4H2c_Ih:d,Y_v6ϟM;z6>͒6X ΄[l_:N}j'ǑqDb=8g۬|w]AYv4IJcu}\ž0i8&2츊)rcO0_!e^q̛.M[W6F,!u[;q>mk f\/]WAc^/RUSΦ- 94.V6tr3epVS|kl8֏KـQ0DX^{+KT,`ҏ6s=sJiMj}g2;0gc[5ۺ^_FJ<" 7ĘdVj+ S>O;]w̼%麂C`ʢrv0@|Vރ.s]>7㌱$W7iui1V```B蹶ZV3xR"b`ż%D*=m^Bg/5SotcZ[Iq.`̮d]ڎxLEy WORI3.şD$e2Jb\@zL<+RX^gA[:7mX]B*qK8aU:uLyj.&ߚ]dSlبKbsR-Y/^GVњ;K3Bz=i,HrA.^#W]ס LYAۢjt籚q m/c#YpАSp_W40nK2Gh.RsR^LݴAz#Ại5? LY=dzEcihÌ )N P2]XW/4]>EXQ ![|5. N\$DWr^1'' _bA46ȼz8Ccl&nur}?lC<ؙ6h :kȩ+ r :B()Ms(}>=t_#jr 68Y.! PF4CWQ\7{dRG Gxg.+v(Ge;FMoRF U nS^ڡʦǢ_Ud )E)SQK>>GczE40P F):_ dk*s L16=XKK1ր0s~_~YB_m_Ҥ\.t~.;D6V6rqX-8ZVH/O*7_[eesL~ C8ҳGx'$> a~1uO貒Efu9B$e轲.1004 ~7='hN䞎!.-DzkT\UYwnrz8."} bpz ] ( g4bӄGSעڵ 6%҈8aQLNhO)Csش"ru؇!".52ق2Gd%m?r=eUpY_U濛[|Tr=ԄhOꎮ+>L<6B()t͎V*2-<-Fߚ|qgZi}nUVޮ6y~5+a޴4InF 7SI!\X,bT&$hd6{8JڋM;s3&7YhO*lL]v[^㿶2SsbшD}Љ}?F S 0h/(?&mYwrkzF}eDz;*K7S#<^&m7o)';Lgg$"?OB-~t z,>dQ&hH8!,NRN%7u{*cڧFNj_|ݹ45}m5_?&{oڶI}sNE{6FjLgNdLWo;NFpOw܅{EI'Qd{"ղ1.$juUK˼<yd-BߕAJjiyH{vM|,C'd},XyZU2RBs?}+72FkkQ4ey`s6[.Kw z3R b킎1^zmwpuF a՞Ev* -yeȎ$wؼ77.YEQ!Tu{ȪsAXE>5ݢlz\g)hs]MG^u voA/:߿Ie\p) ﭠ=:-I| 'n靃˃&m˪Mٶ\ۀy"Zs5չC}R}E !abڗo(z ZuZ֫@HtN"!Dcwv;ҳ0v+\C)"B X!1 ¿w] '(lo:/Ұ4RxsCqfA.5xQUf]U-L֦,xf `_g x -b quxA#!` f*a6҈~8lsć:PRh:Z6\ f3fhbLDI(xXrJ"ħ>gCQ/ aFGp6ώMN5?6J2o~t`2+MRc]PHB]p}])Ԧ(y`Hh75ZHDG,H|ʢIs_qY~1 /͕>Af|3t~.e% /-nto1sG۵15:#kZUĽk 9eȥm߬ /Zi9RS{ ?F=YԅZksNluM]~}LvUuo IֱG{U+.`XexFii)ZT@->eĵ_9'קv{"oe]t; ZcvK n ၇*]N\X Be1U@|w .˲'XtaV\ǙF ΍ȼW}\4@dYu&`˛Nm 7b`''fQ8w<>&Ig{71Vlc(e<1Z<$W{AA4mE8qVT^*eל-pݣzO( t=|*~zAsE>(XU 2=KX hPO~sQdM*OwcIttN 06Quxl3AIDI#l Q 7 3YS-8\{h=>H\(gJlhXUqWt_WxPα纞C<`*z2HF竲iuҴ¨0]T١ `+ZX f ݠ & Y.PBbSjrrJ⩗: H˭٭exl˱ ndYufݵpFb앩1x,%l85K,>aYar@ ėuSsT. 2,hve`MqM ت{#R2JsцSN` h<@*9ā9 >UB0tN{@DڹȮ͎Tpo@ش~\[yV d sfxs0u31A?wW/-ȴ3A4v`y^,u];#3 T`T4]ɳBHg%MYVkClێBgU]ץuVtw.z`ˁ-TpNG4hA3޻NwwfV([E@΍39K] '_2bх#Mny&f]ҳAwf(Rk Bv./u-]Sʜ%ώ 8a\n}|mkV1t &JwQU-sѪg᫵侎k:$2^z5ס&=peR]AS۪[/q->O݌GDjn$y)xbDG,q?UZlԏ18UտYK[*~n I6HOMsJOG'q O-|+Tn~2w{,A͘wmB?FL[x7eݫv[@s($4aarQ>[w\tK;GVk6_*Ed9Z +^m,$!\- !?vd4fvFp~!)fj7)w#)$5< o-,˯SEd}B.e?z4:߁5!v^>2rM`Āػ3 D:d 4 @phyzH9+ \\%lH*25w}`]e8q H>ȼzۺV}Ckk½m /&Z5uFa'+YF$;(ؗ"$eE j5M%} uCJ.$u/[DAlYȈtֺlYt$;x` k;Ջ^:-l=W zh?J}+Atqg j$[hէ/g]G՟02S(IבڊG N!m{z8P/eZ9@x*?{/Akw:v5_=B AH0ŅEރpw:nYkJ@.bXk|w-?{?퓉;uFgO f AŞv:#[8g ˜ϘŬLln9WBNI@I?/m DD  ܆p" {}F(prw}caqVvcũkҺ̗|´*z̽ILG ]9DҘ* |me]DUsVRV~khD{}rŗ4/E卆jZ暭ȼV4?!L|mTzEU'1Lڴ"dY3%,U̔mfZ1!a\SW? |J:NUbiDi\۝ӲhZmybӔ +Ӏ3w?\8+igI8r&&#qq({}$hytڛt?&;%#^& VS# ؏% ϒAooMLkx ͭ~lEsʪÛ!GX{߼J3^J Nvfȧ0e"Ȋ#o*EЦ.9l7Oٓm=L~r0RIu/-U p>UGppRz:wBXŲ:>GEL1(wRcwϺG5ai(9udxbidESGĈ8dQ(bѝ 3*L#rBb@'Źr0Gt^|C)IbO 1`̍"Z,үjHG bí7]ǠFf5Lv7oF"K'Ƴ!V\tHoE=(W"_9Y}+ Tg1Q7'vmTphprHfN 0 x<g"jb<4y7M.NlPl6 R%Qr70#[դ8eMjY"^ZM[ ,-RM48eǑٮVţ7]wu/J!y6FA3pXGx0¯X\4~2q7`zR}jbhNjù&kdzQŃee)mPd @FH(@7HhQ',4aOxEFhtQv;LT}ʼnQA$ oK8n9R[(LƑJE̅"]m=ꪱoZ I$ 6eJO D[)w|w@ܗvt]H8אS ~w3qQrLlJeT$[jUtH?__up@~?U/vz$M$aJ"n9ӂKJ>`e):o)cJb;W̠u0itP&t7i9H>,E<9RZzžgoDD<]D7|+OHPCqu>5P >'Dłqa~UpER+jFC2XD& I \ӰntḠqw15/7%e(a;N/Vh3"c$l|DZQGtH%WѢ2T GT5M.e4"ziU## F gx;Y2ZAa݉j%wBbq3DщGa 0Њj"AQAT 4&{0Pk :gpŐ+?NfzhneC(bd3T+IɁBH|"c*L;$hU.^F/ 86aUtTs(r&_[5C_8QXw3UX"*@4X`LwrЮrQ,R01`J ʌv;JjcŹM,!U`Q" n3 2GLT3Zm@|V?N}HZ}P~{)Q.UQP9q r(ɪI#iV:%kO0p !nMO1Tl՜ܤWYZG:#P*g+LJdjX5㼠f͆F$ZmoAӷɉCT֦f'k$g!75*Û_BE%D{BE:5ł>*4%3HxC-(VpC!JъQrAՍO@qFNv< [+Ö yA@ub/\ƃZd$%O&Hc?Rt͆ Z˛qch!`>JD;@P!a+DQ-ی‡LkP>leEH(Ps8 \]Q- WGUbZ"  ;ÂFCz hB$h-7[1 h@@X#EY+AGO*!a+'[.0 ΀.2"]BW B :Zzr| 6l5B5` k/AFkU+#hUTv] 2 FN+PPa ţћwc'bO 8(5SbV-9 D: d#e̩Bג8Qճ5чث<ߙoҦZW4{U͊twˏ\R9"!XtTxe6Xl$uZsTiwlxZ\29(X`B3,, 46jFG-RdEH}3Xp6\uMViPQbӑ=Gx{ dL"\'QP-*}*a"bK#a*G;JQ`qXȱ`nG{Pj`%X`QHpи!fw G50"!Q)bPEP4JI0XW:Tp' \JEli"jKԴ7ti6cH3j"N@AT?.* jWacT2bvך5S)U@3eH+u7fQYJMjɎvXk\uEJ*B}"ԉqPlQBZcBk5pc @k" 1|[&$\aW"B}(xEV [筷4iVCj!j IQP)*{PKPH1bY3EL`qf*}cTjȭ(3|*q{Q;oa5$5,8jbTxBeE1awA+G5nj{j"\W$UAOA^ 1瑢XmR, 4j@ĈStdn#L@d@DqrtjU-Bt9P;\XjzfFFln&Pp)Pt h$)9r9@`875M*MPk;Py ;isՁZ+' 6: Aع7a`9:`PuVP"Z4C5#3$V!(NlP,V+1jUJVMyF7U0G L؀qT%iD 0Ā%]Bt zSB W1C-QM5Dlp4 ]߈15&A&eD9JG4>H'+2¹-("5 0z2%WĈc?$ )i ^LRRgF%O5oI+*$켖K6fd'&v`SyA^P q-^Q;bBqkf4q(tD rs(JU:"PAH$ZD U\j+lؾ. (,UP4޲G(PF5+rFu$bS(QD`n2,)##. i5mZP]av! =LB,ucQ%CJ% U]/BiG!c%l+b1bj{S%͸ GL~Z_ .u\.ڷ}%\ Tۗ0ZPpq"irG%sa¡2怸ڣP!K_f\)@H5NjKQ`ErvqVMn^GL!. Pn D+:PDcTq@F`I#F0:T\Āj%JԌI@QÞM1s"VP`Dfǜ2bb>Oj; aֺ][G)dw{>d=-($1CK%#X{J$A\mU@%Hiĝ\PtP,p*n va`z=J0#D6ra} 6#n׫BEiLOs==8bu갨 V+)Qj=>,"B(n،㮸vv`x(@DZ}]aAJf~V+k'0Zj~X1ƪ,CF2=qr8cDuE *V!+՞J\gjET{wa-xsjJ T3v TX%#(_1XT(^vX) ,w@JR[uec3_948u'CA Xw Q'͉qGP2Rk@ݵsbq[ QdAѬ> ,xq`ǩ;+x`yWEJ#S8xsJ!vzP]E70z =QjD!RVa!চV"DwE/ Z5Q`uCÉB39%NPp^T Є1o@%b v̘KnQF!a,ZXÂmPlqD.(hT]TCa?@0oL EQŌb1A,FW~^h{]b }E$G1*452D#OK;0R)DsbHX al}@I$F X $֪Q@aJQ1E(eT1xtv@X}]+{ }5Je"mÊT=Bci >z2lڧ  5"(fcbu)pMcc~9Tef)z$Ï{ОY N3{ԁrsXl-c;E3sIsT؞iBH.da˩ CYqVr`jQjα?;peOM'1 s\M$H>r%.󃈣u>5cE#b*;FQ}jj5qD4GqԣZu6+` F奃F Υ 74 352 ^KPxP$;k@2BcD$ful7YjȮF In qldwZMDRvGF6XT g&#[E2F":0`dEOeQpuc`2(99H2Y(P=PZCnj"ƍց, -"RlU惐N"3>&J XE|Ւk&$ Q0oV!y-mx(b^n 6("$e+| F/i1ߦE %[,T]T0Å0:YW@0U t~xu\Aa? xYZ,$V"Ƽ$Tlg5uG5fW}_3iGqpMp(hz5--C_NM7q9d!@0i-c1T6~Ha#>fƧ%n< B=!P} ql8$uHXǽʱP))8\ qm9 ՠ!ި~ qPvX4R{;JG&1B6*7|w1s#V Z[ ƍWvh\ؙhAbqI,-ŏl;È2G EE{LFϴHߟun;]^S#7JYtFrh$#g2l߿^d,TdQyY7w9 n.*xb!<8;FgYk{8v|@gʍAu_fO alq6>hEF9 )˓H:ۢm0*8P"D\O#A]zm 7[}6,?h2/ Nm8h4MX'ÃYwTˑrTX_,^u6jY @wRn iү9PT ܠ&Q+Z\:C.pz s {i*VIc8+LM,-Vl6$H{8?]wjmh i_k1pkxr jx G*AldSN>|<#nѴ:ab &-C|V5YqN9[E*NEʯP8A| UO>KPcyT%uP}q8>UdGj|J4U]Cmz3QbO  |Q—lDݾp?일~ -~-~6\~-Lnn n_ ?\1"vk#5چ,AGI-jK" &}84t}:}EY՞ *t_dѷ5`t+&ccJ[O69FA@wYq@6+u~#TكsF1@z,\NmhoUC,cK7I~ixs *N.$J!/`_a媆@?ro*Ѻ$}: fMVZs^OkW-Vi,Iѹэ6ϰADn- N8 i,_Sr>%kHw9Ńg0`e{k\C;qɖUW8I흅`T"}Aв+=jo݃\mсڟM<7p.Kgco YiqzcM8*'2Јo7)$^rp+:[`-/*nk nE?5U}) J_t34YA*cA^[%< Cȡmx6ej7F6R #l= wni'N[9 Wm=ЀC>ڃ|Y~"a*{UR'q6ק^vlS4Xǀ;0n?$j:Wi䠵V`Tp#DCb lW8!a8 $N+܎sr@JtQ3-Awk 8;{ QLYxNB;+q [ P`SI\ళcVJ 8_r|AWNGr3XҞE8N=T˧^WQm)scN1DxSxœpD f h1K88<'^BbЎE {}](3 ٖh06aF*dp-)tXT cűW9ݛ a8AMp!jǁy,N!g>ơ.Y#lOc@fP}δ[p U = /; ~8Ը{3 Ѿ6()Dn:Oǿ !246-kk>EQ!=@i3ߞy@XF/(rl0SlX6ܸ; ,PCqisI쐮@>  Sk[q(ܡ n+{^?!%0* Li N8+Shd2+ćvp# %{,PѭYq(V}of pͿHN ASG+>[0\ *s ͢a7Ĩ_e # aAl&!C5^h۞֯ gut6uZƤj{ 9f4x֥lɭC]{4ls2_koxnwa&2h g/)8dlko>)>bcp47}De;c CͶ{'f$c%'m#Ef^o92 ;N]a:}SPtpB^_|[! ߭i7ZG\Q9W/92&n*>!9bЦsZe@lJCg $5 jsKf8KCs"0tGJ5;"ÁqsoyIښmj<~j"^n?/^ī Obh "[>f8f@~6͡~ U>.lB-EU-m878MOa 'hW+X*N-wG)NWu##Eo9"$@}qNz~8r3;q i9ˮP(@hPTl^>L"ÏPE: AƯ'AF\?88v*PٴƃN))#" Iy(R9axfdo k'"Ed530O!=>sB%]^cM8hc 'q<`m8Z>ͣeDY#IViW4B/i| QLńcAm7P܌(I'CZF)*#n\"NE"5uA"TatIK`pw性(%ʕɅ/|_q[tĸ$tP-F OV΁gs& `T1 +]8ٶ>ā3x<;zd0'$JGJ3  YXLKm0 _k_ݧEi.ٮҨՋ(kRKvt3 *KgkGsk"<dӓVlI&Bt4Fpn{ a"I@z^0@ d āTzX+FB9Sصk^9"Q͠Ġ1i#E?J$nu8j8| :o B| `_$G⁕KO[=~(I?anOZ3/p˩bM=1HSN rD!hu_ `Y :gy"<ƜCJfhZF:%|I=yVOyLsXb oJZ7Ea@i:!T :2>+ʊJ| .k`4>hl#`jaCZލ3ūy%͊Y4gAG;&sboKP(E0|9ӷr"v'=ACT{/&l\w'6q$n8ś$R>啇-񎋀B\h!-W՜¾p y쬃pN q:4P^C}*_>cB8 ~{hRv=p0T|Ug=WCOG%TZ^DD9&ayٛͶxܖe/_Pq i`Ysah>1ޑ2hl5\֛`yn_g[NDWPQs&_Êvlf82<[!ї&!HqX 8h a-MŎd֎Fh@RxJ(/60P'Ag Ef 5vYq{UW}s߰.7ѽbRハdiRx PA|= !e]@d9eG.ݶy%[F .[>dYf?+D}@4:C@Akm ]_NF(HkI "H " w~yb%&Rj?p9poD4xxdT!}|}vn Jki3kHg{^]^,4?Y~^O lƖJ G4c@(|p H"!!&'*͈&cMnDSjĄ~1<(B4K P 3'oX0g5w'`&RlӶ bXRTB3)TD G*$r3:(r3 :J~.\Sg,H(]A`Cq=&:C0{kƄ,Xt #XTҘ:P>x3D.R"Fcabӱ$#pa aD,Z ѵKE:jcѠ*}I"N3B4 QPq LUi9j}J4@3AqcK@a# hdW#@a:XX5C!TBs $T*p7V [&JEHpH$C5jNzRH0*5S=e>lDfFб`)ג9]Uas$BS0hۄ(`:?Pp)" %q$%WjEccg$"#AҭN(_,'݃Eb Y3F,(hD1`ґD6Hb@o#:ڍ=xP9з&SxZv JĨmWj%0J{jPqйbݪ"mD0@9bYĕYŌn dB ({PHVI*ZT(l}2u7 f`&K ߶E`xZ ,&(t?GC ajFWP39EA AL$r3x (:N!PTSY7UkV+Q %b(4h(kN(T,A.υKks)L^XTXJ4aQmG(%\$ ᨈGZ鿌uu>9MeQ_BG8GM$MMD ȅ G4S|~13YՇ{lB@~lG7 9m2IڍGvMNtŚiJ)܎=m?sgT1Nbi0> 8O;-q'ho%d["܈AGk$ e~2l\#-L3L_)6ӫ)-lv_'l98@G{ߖNkl!mW 8S^L2kxT! ݩf/2`p&yN CW4o<*(^84 Pc d.vE B uYf8 l%6:ٴ;zԠ̨q:í+#RgˆٶgMY8ʙ:Q5 y\)(VMuT#S:,LSŌ<:_zNI唆=;SV ݚk@, *D9*+@tWnmCR7FĞjRg=-# | I|2)E| rJG6b PT 2:3XNOd| ȫ? «4.켇I uy1sy VWO$he|-t! 5?!vy\B@gT|!鹬:<#PCXBwj+ Q|}L8vmBKZh.]*($4U,^ OTBOIabgq{CFQТF. Rv Phh>e6xN!{,Z?9f՝P6k@<:キ6C՘ e:yJuQ=W?='35Z۵_ׄ*`@Rcw!*2Ku= 9|}϶kk>ee^'sv KR82ךб(yQm9¶8@ ސq@ 9HګVAc$ϹFn/A !Sq3"JTnnSgGSrͳin!fC ^4Cz%AcDBuM2)4lm 2\rΥRRA Ƞ>NF+G+&0B/f`F ` HF~m'8\OuKYcxNT#ܠ̈́pb1Y~.l/&XkXVڌy&0U1ʶ8" oYS a*IBS=]g߹yN|ǁg{D%v :[~Ulv9VL=H !8yh].c/FYX`d<{3&@}B;VGmO'uCELHk$vc4 L;ƮD_qa=s+MX6[ZԺ3[ h  !rK\\A0Y6qgv!+cW۲9$XQi  ږVpQmY\k-E;uIKWp`҂S7p^}b1]&򎖯ds!\p^y+@n `rV5sz@q`N'P'X2nBនI7kNk6֍Qe ҟ𔏑fdg6 hBN6bk70ʨ ADOӨcz2o\XɬjdbRuU2jCJpn,);u~zؽTV! ,US^x\р&é{->M1$6_*[0W+#g$U}>' h3*28`]t2C 䆊c;hl2A?5kQX^Ӛ3#:l"62~Q#6Wr&iZFw'_\ǂ(]cibW̻ h^UЅ&8. 5pwywi覭:W[1AX2›ǫBXqMB3rUW.g㝜{(gXAmm†n#3g ҬLuOb&OF_f;vkZcF,?z@]peT;9$h`61T/JxA^51Oyao9dTdf#t[F z;<4 V$Co'PSgye̥=yl {ԄnvAXs7'sw#Ll)a柎crf!%&c^&_qR&X!/\ds9uJꞋd6+g͗@4DńC,lГ]dgCbC^|tPdnrY6ЁAgWm2SH*y%rQqm>a' 0Ͽ{#-YjkdUYBQɞ  WR{OkbYO&arx;mAձBQq yaw~@[ !gtf>fl0mSeCW>*m;"x/ .ĕB{gf)> ?RL8Nk32;9aM4t8RoN qj % wω*ÀH=tő֚h3qS}b%Z[b`hVhOxWwE<W C$0Z:I"A@ά,#9:#@@Ğd]ܒHu?3ճO B;DPu^r&Ecf[^B/Re%k+sJc޿?8:,'8hthHA#?V@tkf2e7ށq8(ѤhxZGmrNp}[3")fDT2tiljRA.ds!kΤ^qZnL S))!2B+/8gF濝zd3[ʣ "7fØɋ#XL!OEzʞt0&}vN k6yyAmǶ ڥdaŔ0a~88OƖB?r?sW:2VZ`F+&>8*TH+3g>-}=E2e9JIՓ B+2HbXD?(|b484&6]SБɇ-bM"Ɣ [ &č56D(1aO nZGt~ 44 h5͂5McldbO ˫QcrciatarMw/q{w +i:2)d!j&`5 7w4Lpp=uP8Vp Ts/RvфG*-hwS-nbdЄ+I D"-_{/QH\?Rf U'1;U,Hl4*6'f0s.)"ٽ,,ɓ]2l]/@y&>̋{rFI}gIc>)/66MJDHn3(6g8X[:9GsI-e ]5ҟ6B74-T;EQѴI_ \3.ͮ|_%fΈλhո ZxsT&. *Tjp:TG)W 9E:)"d&~R1Қc1w Njv=; UurP^^Xm6EAbMoG*RF :8IWrª٭KAkMU9mX϶tnG'k&g( }63d͟1mon&_.j&Q2O,\__*Gzې[ ZyvV4kl&t9D@$>GϲB[@FS,öW=E@,qڤtdee '{THN9ugλ{ B7G7?kΘB[6O$t6X0ƍ0'Ji4m@9貄ng_mINx3`ֆlӟyjdd)ThC  " t֝6 ]<孩eGNשܬ/k{N\2 !<4obx$?_A*6nЕg_dC'. 0n U7܄n|Fx(ӫgO,4} $ErL߂?qJP?P<62ۭE]rPͷ@\Y« D`&`p /C]pZ"A m"LG1tpbpIRht SЬ38o\Ky.f\ Ik&_.݀/UyNE# c;Kw/Zڽ!t@ Cb݅3q0;m7!̳&tH=me_Ąi@5dedk<-;`zBע!o|=k^ٖ*헗` [C'ig;t@޶Ay]@`=fLN&t@=+;B5![VdZ*bQ ƈ ~1Ÿ4dT;qb%ݮ"vNLO>/ۣ8)11: 21\l5L2EOb*mK= :R(FYc2:]'G`Dr?d>Equ>3J_f "O=R&tLАu(U!9P*}+{q!3Ai a LX#9@=!&Ă'(T?2ǀ~͂3[mHl7 69y*b&SadDUUͿvn6]}R#ɞJo$b{1h"|KBSWvtqU8:?ۖy$ vn5bZe6U$XtG4MRB@o}F9x5^Ej{ :q)d"^ #o1ɒĉQJ |EKUzZf p%L!MkJDCZ^׬_lUjw[]Bŕ$:͌H<8uyѫdq)cuHw:#Zp1n-VZ6]Ǝn@ :T,b '3k:qU1h:|ٗqe"<7Y5v4!{5rɈ|~$*aF!#::N:ߦ, L]hZ+!G=CԿBs{wlHL*u5H3o@ B׃K_ao\):T!ڈ("F# 6~@cɦ=$\S1SwH^1ahI͔|;0f=1#7{\yq۠FA ob4yhɬ %@d7Y=zuI(NC1^vFYvҍ1Ud+{VK}% @j'㱘uc~uBf5wS'C nRh pׅMyAyЕc9ΐ}VK!5+6tyA@r:oStZE!(AT@ N>IcЖU]!LwF_[Н7i7>BN?s,M#p`K̹nh]" A᭯쵩==[ig|}@ap:}h0Y/^:40yc>t.,ͅ,čK̑-A3o3Hj.Ý. =␬_J5ڤݹwkt ڙ~N0 J CkK Xc{ EXSsы0$ /.yb%Q,•n~,"Y|_ϟ 74Z6kHl[l$gx#eHjƹ EI)Ix Ɯ z{{[W˖wDwcv+nx22q((5K7qiB# !q_]oYS-O&[׭]00x>CXÛNzxo?HIH ?š*u{R"MH8s]"{WT.kGt^Unn.sv=nH[kB=UR.qb?,PQTcRrsZk*Mܝl9^ʡ1Y7o 6xIܻ!B8Z-O?mFM8^y?r>8)(mG28J|v'z>". X5Si4bMkoá{fRW'f=+!8ѓ o urQ.t@s=KHv/] AG0μ$0>VfR!hU1h^1+Ut ]$JM g EĥVŒr+>aԤz 40N9 ]=U Ys\2t QkUϞ|Va0b5 @i5.p'! Hݸ vemަKTBgc D$2hCp!Yխ.ёv 6tэ$Eߧyi؛@OhPy!81D7O+O8a)6~=vwBL;T䛃|Iyg/",n[\_2 i|akػ5t`11Cֻ?], "w1f!|G_ bp4!C ~ z⒡]άd&@gjx3piФg'ە;lL9c]T4653<&&gRt@I \QPƛ-ͣ1S'f*z`]rMT0"REG *:}:.S-",%aq˝X"1NJWv M.P,Z4Wn|آ'X +2&H2bMS*AHdeZ5"l_By/|^![e:y5C;Ʃ&yWFg`݅T33lAMo&Иb'IU}Ԕkrw*qLdV^YYt|X AyOSeXf`l끋(pݍIFyINb)"yUt|[.Ӑ53)luKzAI[hs.: ő<<Ό[~dLVߤ j4K*vlˋr^av$5$Dl_D ܧs;ʹ1?zd;x#.en77zp012uL4܂4ovs X;ےu~߽̩F4&WmmϺjq9*δWrU]v5΋m%c/?pڤ(~E 5ʑ ݅9g;s4fDL1)!Ӝ "6XT|<~Y="'%& JjD+&>f/{5u۫Q0L`Lq^.m x*!)[%<:N_Yz8g2LsjDI[Z!eeØ+@CSx{9 yI܈@̳VE=Q*jY3Ww߹>-@oQ\4v-G.&e-mBhFaڗdl|)gC|tH_6ܻ, 6[!Y'/)G-`3G߯)ڟ*6΢ޙ۟1%dYmMAmT{eRlvGoiK2_#mOzxIrU%+qՈ70v)2tFӓrX%%ΨmձrTO*sR+<`밝m> wWrv- MEMZwâN^'m;' S?뱭"4Ѷaͣhng{}jm݊첁i륅N)*7δn7ӭ7&{3FޖIzf!pRl<jP PKטܟ|WB ] 574y8Wb/eҗ-+bo-!{ӻBCt\A㮡k{`FU/8} st B7-=lX[?/ښ{4>:GCףu,[hUCf>%k[^/&԰~^8t2 wyCu't= x%Sxާ?i@8^U-Ϋ z®Щ94ts%J7E?7 ]C T=ȍ?E`UZ&YϹWk5U<`:zWh9: e.o&3Q`b軷 3~%tG:OP0ƢX_6Yt%n(H5ѽ^Ŏm`pgmA^h xN _b,Ln`75*a[c: k 9R KPJz:Bnv&50_}r:/ֲX;ѹ ,fuaVj|}&EKlB6Ѐ_TĽ[^c%}y]3MTK,\&;+X2R1,'%iC M$W3_+[{mVX/5t2l߃GwQş:/ $ HxvDO;pM} P~"_ja96 ]+ ߡCUӄGkK)mw &z\aӱl?be%ؤlĎؽ;)eM6HSfE6" wD@`ĹE$D#FMlo#}dzwIA-bNšaApHNF_Wn}R@@j[9?kN7v_HȞ3 ` g .|;I1[u" nkc',]v[/d*8Sruupz48l:]YoN콾nm^Y2gM1];~;i* w뿫ȫV{,ҧ'd\X8= 1K4c!~{.*vms{^^t}QX'v# }YSϮЁ=IEd \zSVcb]_Sf0t`Dzѕ!W!vizJbnφ8)A@&Z Py4pA/ߍɯ!G潂ם:V C؟ nN٦z,+4F_)0#Ǧ)8A05Pt !^Z{ֵ}eЅn.ocG[WGC +X[l =vY1Y/I␳bKB`Jz[ sxf1t]Tpt:zr}$TV^PB5#ΛhTdW~X$TdoY|9= %4R}uWEWOOLQB(h&w043#"#w>O 0.ԒڿD *:}:NEMI3=F;dExvD`2^LIQ=Zݣ6H](z/BM|J^?("2M_F@T}sšQ/LH k=D J8t^"& XJ Kί-&ΒLU^FjF@5$XDJKU̙k]dQHb+T=ݵ^DuJn <,4B#ojE]t ;pga9flt~` "${匈u/ b>DJi/G8uBa]㷬a^T6{MH3)ma`h-rTi_vEL ~iD^7qhW42D K'&~eXJo *9ݦ{!JCEtZ_@5X*,oDWJ/V1A9;IZS9ALű&\k?%OQ/mYHA1ii8hvi9ԖxnCHi#_ߛ#<  ſ W=|*0 a ݬGXD#N~-.t#P> |#TRڄJ vIi`?g7^2h̴$1(B1>uKLs2x>]ӋEݺ+AX]4oYS3q_T(6L# \hnbmmtE5-uk)9e,W"H4;O0N߲'w`"@5.~Ecbp=8.,YDvGD@*8x F螲u&/v/ PT(*+BJ S<[0gDƱq Jɘ r\ۤeRy$[٭S.Qv=>% \wdG6iv < ;^F4,/l.xF7Aj^9<ޔ@BVP `[Y^1x@ @ Id" !#be``4[2"ݡgaߍ Z\1׊H8"D]mqxP.I ʴ<؂mVs2(>Gk'O \FYe;"}+du&F;3A܃gO@ <Χ J4KPwBpipO5hudՑ؛N t|M +=7.I˖j"Ram!R"u 5lG\k$QZ_1oQ%^4vkmL[km҄kXs ]k6@|ҔbH-bF\uϿtƗ_exUTi Y.m92\Zr@ c t+em(pY3)Kas͡km &WV r !Xflǂ"sSSAF/_c n T3P f׌[@0 |z?vN Uk.=oOwR*9H]c|dBGvjz-!M]s -ۅΗ_ vKl]NƽX750JiLzVZg΃ͫ*G^t>| ՝&|ފLf868s sR9vqG>gr7V^wQ%tnn*x@dE: S"#/»wz2NyrJE;N4v[@ @@ ̈́ [^]kGc5=oo\pXk qW2+.Mg,t0|8z4889Aٰ24xg d%ubb1tgp*'čXb!x܎g\|x )0c O]qCfehn}dq X5H:Je1-/ci<Tѭ7FQ+BK Յl/«&"ۃ# "QOGB'!ʶB )5d+Gdfy! <24wE; {s4?CZ؂Ý<:T>!oKx!𵒳PI "'1fK»:_ _s(tl4-p"TjoBK:ĤH 4xjgo8r9|< 6p :th4-έbP?ę֡7qG~<[4qf.j oP *7` B6<:F0Ǡ[v zJ::+n6A_@s"; FxkuAM\ [1pow¨wpL$Ci+&B9cۮHlбJshk˙hőat*uL<}C`:As:Б9ӑxjOxGFk>]3.L iX ՔXGغCN<`Ƨ6)Z0s A !\A}[͔Q{4byp`lٺLǙC'^ĩH \٧z FUoԽ N|xz}L,2:@ Y.^?3t?PIߒnlpxЁ D6`C']+0 ]FJC7ڃWcϖq:OXuIFpAьc! ^+S:|C 9h6 Va Y4PKQp9iP(MpZk*FW T~BW-zI׫2YE`Ih!ldw9s7?%tuWd֖3ڊBy7:zsC3h(RHBu(; mXܜEQbCovgERp4686tE_#g ͽ[a1!ɂ_ WAŔm2K-]VUd4+[^d0oEq|Y)gϩM_~˼m֕6\  …O ڞ ?˼H n0j"bd6Xop :1ǂر{/X9]_7R6r'q͚U̯NnN;7-sЖҤs"8r&3}Va~`*㠯oJNݭH <âB5φ&wju G"}qe<2nl[g1ӆ.8lMB8e#؊H]mb&fo!Xtlgp/[?ՄI_ ڍc.4j1ʨ7&c΢(u2)6-'Vӆe97756Bӥ|l }q,1# H ‡xpC*ݭ=I &4{m /"IAGjEF2;32V H\!b.lab+D:ÏOo}1Y5dQuKDl)>< ^`Rr. y(HPqSmhA.Rӡ2H s݁*bbk L3;vYjONG8H?o!̠80N7x&ܸ2K"E|sł'Pf)xpNYSӣ%hxgvG$%jDNrun׿GЄ6L i+eOC+ݥ/զJ*\#sE_5r[-oYS o-5t:d\&;u_ \D:daFƚBw '?QPʼnqcE%aW,1?\;';1Y<\-vI3MCO :YK0Б8 nrsb"cձ]XzShM (pdD+zRt(bG9JN;%EHK]]06lԥV]G?F?dHv5F(톴k+XS z~p}ƢIܨɰ8~=Vr8K0\Zi[c !XiyLBi.uw#Y-f2 ǃ0M+Iإ2ݖBu\9/f?n 4`@qfvP@{CcKY$0u.@/FE!᫓SE-iʶF_g&p<4xnHah6SQ&ݖH{ l۳fY౳F3>Y _- hn"]umF0MNC'nCx^Y H*bX& ސʹ=Ʋ2/f~X7'늑y3 P]8gL\C:0o#dh峓q.8>2w,\v! o <1z o3@8 p*8f&~X_'b{,˺ؾ/׵[L1/ViUDra|cp g _'0-;!j%Rۗ ~*Ǭ=ie] .IE^Su gz+dsOϰ)%/eپ.xpNPL%@_$*۞l/dl{%$&e٪|grȂF8[tߕ^pwf\97Eil0++4АI@`&p|@|.a8\O!I׽6A{TgRݠS{>LX9xqpHJPr\m[ z3z3,|7AUKQưr[mP-՛̸~`a6miB뜖]k^]Ԫ*t:38?6/k %!B6+O:L ^?e ll/„,Kcy\cgj t=}itJp )!%䪴t dJ+GXda-˭A%n.,r+/pAbyvC]? |t%+ӳA@y cE 49(n0Qm9]G-:d?؅3G}IعIHx)%).) 촣ε N\+#I0yTHr"d`yh''Ym M38GGuˬAAs:,ٽKK)Lxvq&GW2^ob4h ]Y"]v _Ԉ^i]CVŹcUc^ب5̌Mu?h%;{\'4jރF>c) 'ڭ~n? Nc Ʃ~.ddˉx*E[/G@S^L22{\,(6׫I.ehVY{q'n;'RNPЮFLC'ڐ[f:c;?XeR(;nϥjc3YV.V2? Asoh6v`Y?j;\d4z%տ&kxk^\_ Py_wOO/BH~Xf}Ro9ag$shl?0;8xNֶ^Q vϩx(Nz+Xz%~hۭuH?E:[V~N˗d|Of^껐5i+ tVr䰻\ L!Sv;s{̱:)5j,}CwwS[$X|ښ$8sU4}* 0x]wj2hSNwX >/.fu6t<1">)*6̠E4 +~ܮY?!S;RYM~G/> B&]ڼph[̵sv%{D'#ER)?\ :tcF+t clK/fnK;v" +G0l9]04 [)D8x&bK]̓u:pyq C}\'J$5b<݉KV7El֙Az;OLzRwa,oX4F0doq9OX³nc!f*ǣcp%ۮy qMv:%I{FUlD$ZX(c!H@(cU#t#03\UdƃLqvy[ښ _CpU- =رL'MtyCY?eiUu:~P.{zBZ]: 0_nOz A}BC2٧5!sCI>D.ؗuY"4zOcE,@>Yg Ɠn4 0(;nowHJ,uJu-917߶%Tpf.2 UB8ԣGCF- xnڮUa(Xp$o|!xҝj T RJ/6KyoS7Ark?d Uk-&vC;+;tMqpJ?bU4q:jƢ4sX,/ ; 6=7dU.ծl{ɕ@ZMÚ)mB(iӬ(EV.uՄS&7ڊ00bGOFqLBfg[ApzX_YnPR_2 HZo-lqp M٭t\qj #5)!DP͏k'nJ"IP$Ku|&ǺC0~<|NN(T~j/uouDsnFHx-/ )_ZfwZ!6|oB3+θB SBJS^J㷶Pp/rC+Ej̄]Z,^l~tcA)EKc:]u FX ~{3-`I˞eNL ./ ȇ%s"V(w)8X5ú63/(-O;o}g{ ckS1^Ofbͺրkuh0u`-:m >6ûgԳ<["UvrΌu=s(M8y#}WSg!H(Xv [>wAOHUi)b8#H7IU_S$h.v4v 'ݪ>{l['{!>Nӹr[~|,^)lj pHV `ۼ<)nnwlfAe%ٮZF:;(z"Z9+<%ԮVuzRvti2F/ݮtΘjJLzޓ0< % ?l+dtvuxg^đ[MCl8rj72ysy^O3o4wTOAgPeKG@M=ou)Ikp\^~o׍Z3gn}UJrsHI҇-d\) ǬNʪ\08b5<Sfc1/Vis} [a z )` zC5;̃a"i_՝U[!rЀvK[n Dq-JXgrm/j *\?4ljLgCA\z !IXݚBk?5O45귕0#9;4-u)Re|OHRxIK$l ä&n؅O4O.7G\Ms%>>ϿrS^C{t<'g5|)CA׼Fg9>>hO?RYEWyUx0- >>YxMD8mXr'[Ywy1w>WDtTu`co_!-2݆LAs6DϤmF O\+#I(Q#ɩ-Rp>=sqڮלiv:)X6Ef[\EvX8qUˊmJ]<θN:9xya(]Am Pb;Z@ڛSaF 6$Sc L >;j'w4jo ?z r]Ǜ]ؿ+Ab^慶q#Qܷcf1W`ɠh_ص03>n;ЪԋE_v[;338wf k̹/n\3Y6L۟Y86ʼXjzU>0fЮ=X,UkY@yDHN1"u? ^~?aL+yť;XUUݽWl$.zϠph䆒ZxΐS\-i^Jx-Z@ a_&{R;ﲮv(!9m#p\,; ~U oDGFj#eO*}Zn~> jj~?d-}SP*7 )U::,~iVM~ 6h[C,uqx:>FW}Tzy&uM-|jaeEGX/5ؓmr]\I|Nl2:C:kXelu5~4ޟ)S!g6LBK 9HتEne-|uWawHAV:DC6v;:hlāqñ}%x^tFoWo8n8*[4NwIEc!bH0)ul(ըR!4)*Z,3}LK#mXܖbF'BqzC\ +qG/:[&ej(-~n;mRBT/jԠ䭺[|*_Y7I:~_iuI4Kx!{M`pfW^ZXeIf=$L>nT|lr`\&[};nB JHW};Q;Zt‘k1'lB8Mt1kY02-UOjq )/t[&eoҋ,t 1eT{LQzMe{cC6E-DK?f?]/&$L ֑Ϧ=>Wz9.m p1 xcv(?v!Yd۴%#7&ݾVC0z2= & n,qdm*>ذ`*EϿuݽT>pdpqe5hb*Ë-L={p%{ŐߐabcrDӡ7բ% {Z{{u>"+u"5}EtO/?*l5oĨR*#)MH4uݘFr7AzR"5!BkvB&O_ekO+;{*V-ON,wEW <%'(H ]WiͅńHa-X$(8S||\mNRd*}J^׳MlLeY:Ws޴X֯l,d-qDp:t#-lF::j.t@-0-<6@>O˅#goҭݖ>E2̓)b"%V}Ydu<xeDx,n\J-E̜\КEҩuU*{,6SC*5o5['Ι8ע#y_?-3eniZeOsjVnbeyD_ hi.8e_Ӥ|-&p 7+*Bו9xCx[1~[^o$䬆eE-a5}&gN׋H bkI}T/[vyɹ #"D7A5Ex=%g3V-|: BY)[v2o-SI9g9]2̾jT8ޢߘl.QY9)W.I=u+yˋ2>?o?|:N*Ј/L4W,|>g~&ƼT^dJw@߲Χ3(i@-'yZw(2$69G/D3V:N".[/W$d K"u-|K_H8lLbAS}:VX+/6WɉfGt^;Ȥ$" *Us #Gg܏&t ]w5ue{p. c֠SE߲Χ۬I^:ɫnS2_/};q۷rwoNV;{8Q88-iľ.kGrHe?i_,{E>xu>ǿT׼3Km7#U1ݰ("1V&7~K`ViԒG*%ysL\szy5`} Վ?zH1Zu}}Q@ C^ED,Gɮ<".`]v= PGm 5Ꙗg1Ɛ11&L >#;"w|2KO:xt6'cn/#'Fi)̰2fQSJ9_M3j?#E4ɟH~_ٮ-/>̹oYC.S3%]Gشo)&Oߍk~O_~笺v60iUB:siL3{:2V}?%og< <=eɆͭ"5?bY\z?ѶNYg=+foV铉E~wER3tB{&uԸL-1ipI=hDo[C<}G3ޢIT"䄻 oY)?Աzy ciXo?#oY)3*)Y>Q1٧/]ŷ?[Mpʔfe~oAB7Lj5jon=o\ӌH禣k*hH !m\(g|^?7ȫ G`"Ez4poᢶPSO߲'']*˫ek2OOQ߆rf`}F5[}wso}x~E6unˇ>]O̚ǯ<*'X}*Rj:j߲Χ~dϛgq?EDc[)91o o#zx.^;8rjI1&tQ N>3 >V拍7FI{FN?vyh>X]BEP]cRncT[.8Oްͯ|&֮?}:񢼿֍~?.>ҊQrs!1eJVʨ U٦3?4У+twK#;P\uh?gɪH~r9_W L)*ܳNpv'ʼ'0/ӾtcBΤx7{{WIpu>d_yRn342 K߲ΧKT$Jd;Ԉi;<-|:uu<ߧln`|ݖn3lVFtssVZ3w9CLY0YszHv/mjf_S&l4NKdG2[ KZ/ߌnMV2߫tv>jXD=Rևd䃍W!:Q`6؏8IW<}$w!Ҧ^~L rg!X&k)o4%zя"ϙnj_hٍCDp? NEM1H.F; /F";I{3Ck)M8Ы[=pM@׼-|z'ܥۦ E׮eOwsVnIүoGx :>0OD B_ —B8~+^}ݨ0}m zMd+ $uq.W3o{Mòx;P:Ǫ*\6{x!C}]:͔Ok?ߩI}>IWdvy묿nƳ^Pm*nO.uɾCߊ|#neS +=DU;`t%9 (}^Mdb8xz߷Δqr*dtoYSkK_5ߔVZ NuIgEZD:Ґ2n Gгd}$ǂ߲Χk]Փ}~x&\//*!|`, ̙biXK&+@^c E5:I_sECMk:ciT#!\9LtG@vEnFEч]]q9:4%UbyZ>|vd~"1:HsVm<75/OP,tGvS~J+<ws#h`}fm7[{Zz} {_L4-Ћ? o6VG k\g WMjjtyӿ,Fc?~1;RʢP9fxj)-|52FCPZ,sIE%JV_tX^<_^:M= cR-mpaeq"ӟ/%ᖋG~O0Ds}uL8:ov JЁ|U1,v}R[7q穌~l/+6)-W[rίJܪq2c#cz]v5-fNf\Ө#LE]4L)1'eO=>v5/]~C5]gLc3馧ԚH,pI)43 Vϼ$Lψz}rcdlv#T+Ź+Ĵv;IJ[u2,m$ ٥p\fȌ_4@kĢ-|:qTcw^ٳCvW7 r؉J592ߥenJs3(1o9≯_^-JOXYa7D~(%XsMJIΪRtmA^4 ??Gފї=-|:>oa  ]4\O.8?}:NJqlҫV?&/%}t"T"hIL6)CiIsX{ >E$nykvK%B ﻶг?ff9o,bnt9-FﶪZ. p{mOܣq&=:yY59-E1+>_);,ydx7d 5IuU3Wvs<]NcCS`ƕ "wXׄlJwqgSjj'B?YixѤX*beOMJcELagEbS#~&)iySvoYS{c_. $&і3ѩ9/Srm>;su^xObgۣ 6YgEO6jw'Ωu>+%Sa_g{3?lbX:lͧoZSYK5$6UVK?3+'r4\|ԜFcS.?pmgvW%0+~S„pQȷC{*HEHuéJ꾍۸\kA"ß{rFJ ߕ0t#;$>]ݻoYS>_p~M癇oYI`E;WϬvJw|D;dVhLͮ|wHM. FB1d]TGG; [))^@/^N*bshbS' <7Y1{p 6FZQr+3/tPܚa~׵?X95N?9U?Ꞗ^hޟ=˷FOƜ[ë5k;f~3>~ˤ?W@=oJ:x§\羯UW&A.&mv0%- w^YYKO:$i}YW\?}a9i{|5hVa9pj2^~PzUZϪ?+)M|('^qbQe: =?wvP]^B+yE= 8KE߆y ~c6 |ϩqz}g~mZ165+޹q&>ȐW0v8R3>*/h;er1.kGR[[0ζ,-.~l/kUpDkeWh_#6rY;rT疘[<>ZyfFXltTO ȯ?g"ARiFX1?M+F{3#RTE"9CoW6Gr4)k\8 '揞&wùSo䌏']l"V1U\Hd}T8Κn Sݧ4S< tZj+L$bͼ(7tl<[NX G_ͣxunNx^?bČ-kn~Gsߛ(^7?Qz>U_jx[yy}Ͽ){4"G"4ѧ.Otw&Q"ڝHb^ds1BqQq3X1xs$|늖9kl!OFe [r`8]qYm &dNд}[bie~WI}c%n޲ߨӺk-ֺQh2YoFrҷ?Nr߭yJhyw,Ǔ *wl\vřA**@WDĶ-n$Y4n@2ƅKo5Vy{e9ez/g y.s>o_<ˠxß=%fnS׬l.ϡ6:}:N5tY^^K�ɍn?|͞L|E"n&uwOVC"Mv|E-aslʒ;+ztisU4ZS9]ȶp`;wuگH{z1ӎztֶ_Ji/U1guo ;QEgW}ڂ.fQYODi8Ʊ,Ŝ4D2ݕvE]H;=p\([ gMod#͜.'o&rAXL9|C|6J?W8d=,n>8<}xg(}Vr*)ӅoQOwZmNЃ]y~Z:>bd7?/Zug ;J96~YY!!5^yx&8]I^?0ꢶf쁝gP"߲Χ Ot< <]ǤEi>pF~yruDJ9ui=33æOv+N T[b|Vܧ鎏|$oRÒt,4غteO/ڭ_'f|&G~^cl/gh?s|d>?ҢL0Bg[R:w$Մ0Mma'QXGee˫Qk[PO?y(go-toml-2.1.1/benchmark/testdata/config.toml.gz000066400000000000000000000671701453566013500214400ustar00rootroot000000000000003c`config.toml[ks:>;5SUC˲X$Lˇo74[ROwo+/?9oCOn'7{r#KG&b3'/I|>E2IӬ_%yE-ҍ(Dʇd?Di+}}4Bdru?<t QT*VRxQqfm\E^hձBעxب??ɳL";Uj5œώϓ+z7͆Vxťe?:#Ud܈έ,wQ5'6X8|HȺq v,2#[m^CMd]ӛ$G_[[,qm:T%>iqC y~}iQ^h*o$mJh^t~3ڙЖvG&lӟ[(|oZ̬-⏕ILHR,/b[ gJ$lEN7Wtwzq'j FVr-{複̉G2aoS,=4Fw]Dr&z*$[Xo^?|t>0^ 1EN;yWyz(~ti:?:G'н>#FD0b$H%s!9Nr8q;Crꐁp`΁9sWG80LVs90s90/c<+ojީSvP`΁9Όs90s90s`΁9s`΁9@ s`΁9s`΁9= NJbsgQSh1iB<֏l0zReEŬ"3ZytҢ>$9yVU2Sɕ.ܨD7Tt42B(pG}nB842@aQ!f{Euq_ M&X2+.mRy)LV HAnp|1Ad̻2mAl8zm)V̻l[ƻt%s=NéH>L"ېSm%rRwIeTC]Mb}T΂. xιqcȄA[޺{.|"ejrjn%o6A9i7†.y ۣn *ib}l34Z'۱̋|^$'w*ߎdJcC?Sa^Aش.|ECA!/Ki:u#g 3 V:#+Nux'gz ntrbE>K SK< "Ee1eӥU35u.,8 $6}ac T1C*T[qe -1JAj\])"BJ8ot|lv*(8~7U<,nm3ˉݸBasL+@v:ΨRپY <3M/|)~W{YBiq`8zzcAr|i7tMfߛ$o= .d6!d啼̺I4N׎_1z!arI0lZTll&])&/"p5]/|bm-ʃ*l.{, )41$J"Zfy%Ԭ/3pe逇f:D=}ٔqcbɻJ0',Ό$8>L0[Նp &C4MAE=? /ox?L;$U6}[)PATuw҉MƬM̽I@U^:޹m${UA ۄv}@456~N*m*k\f&gvKuׯ [zu1/5#Yܾ֓oҏ:,A<8Gt>/h\Fxo^m"uwkvڬu!}}!Y/YoҺqLy鱞[F}O81mO?#̟~ >w'KB"MV;* 2c9@~#O1<J~\ɳ_`_,J$xk᛹wW"ߧIהlyz[#V8 UP6* D%EB0A|+wGMJʒcv Wk,j}뵭"Ђ,1͢Tb?1A ]KKo)J:v׷"KKKKK/*GAu=:Dq&4{B"B\>4PLȋ^yA.SoR2!|e1ZewKvoތ?w~$e=;˸$-*1[(اuu([,pL^4%36x~篂FxY"B Xr+-mHSCrj:ۚeMk i%%o mȀzҮॼ *wkNi(Aw9?$x @hac}]_zg|TA5Xt 7nE/C? rB=R`' k hZA[ 5^ /A'U+ Y,ފWiԻ>.]҉A=yt|g\цvAVT&_V,pk~:wɧi^]qCG~6YVIWcU2kywjh(w$>&^ؖӺoYh,#JBlpxO,!QǪ2>4dGlgΉi_h)閶Ɇ%fc |Bh$!3 H?4zLib(􉪁q*qƣ*㔃Lri#v"q!msj3:lN7I$cwjW@(_tnlUBTR{? l]PePuxu] -jS@Di {!nf]"Πl~nقT;x"5>ЃpkcX4q4Oi@^p釚#'B(4<;P LKxmvywD t[f:& 셈BBܭKw2 T^5A(\VopVhؓX B2f-AS6*]-gA?&e"Ir 32xBrjB0&;%uBj@!dO>`BFucwE`"\ZaEN<[ (7[ H]MNJ:D+h2!Ny`y?hW-{ʤU`;H\3f'PDèspzH" (5uXY(+ $\mDb7Rl~ ~S)Q8J.3=D c!#LPTWȊe0njINhXS=&HWmJUXf2A]iŠPGfıKGԥ*)ឪs~XX^!T?g9~ LwysH@(:gDZt'Zk0 |B؂Ň1@x؛u.|UYϿ}V4)gi[bzэ@RbR蕻p][uS73{Ǵ<޷}]@l_92M :shCwF ZVo ˖`pB bGTMǜA[ u٢Ă*81q?H1fSCPV dHH0n 4~!dm_?T՝|;IɁ:ay[ P γ,#>ӭA ]av H|cm.Tyh oS"bY8"a !Fcz.抌mC%oީaҚ &i>5$H:;5N9YLf*OKL@&:}LC̱G<vEK C i,ql|ʡBKI'/'.V~r: om_؋eV= $ $hp7nF\fm7nŶ{/*X bAT5HЗETAGCo_u{#G( *j!d d9C; Qp1) a)ṱ\Ep!CG^b> QtӒSa߇ Cm1n[vkcT|\o0$SZ|]]]]]_.)7z⋊/ G#hT2fI:d*P:U#(9f^D%H?a{ГAKГAkXF/ lF*luA#wdMIҐVY^~F5[ jB8=Mr;U-D|آζ-&CjZHҺj-1Hr|NHB|O$"ЀM[1Nm!mt#bgF5a07!`'2A}Vk#|L~@ tf*a>a%́l-lZi/2cBBr)s7$]IDk C{ C{eE^o#:ܕKɽH[)ТbI񐿼4(8fG1}_%O7;:jH 3\`H/Y$h=ă 5}7AMQJ藍P#T)MCh)FEa8GlL3 ICdg_d"{؝H„mCg;a(X9˿B t@X CNi]`!0."(Oc 4>3*8|pvl[南*iX0 K qlvrB.Wz_)(}P&uΑhB8Q$)-zf6{aD+& gnX[?[H# f!螭 =SGwWw.IU$X"0F8z [`@$kZ֥$f18 F#8'pZ43 /O6ps@Ȉժ?],39V" AԔM[XZ {7I(q hzE ,RД#\Iȋ)E]1dkwJʴEw=ћNÝr'1ǩSCUf3;e}rISq64P;K1*p)M(Hh gV tP4iHD5ѐ[?ո 4hN-%CRv/*@ h|G[!ӗ&\6Z,TWN1bA˰w >7=yDW 8m0R=g(7rGMT7nE U%\U+|P 3fCwcP>~#b]QӪdʀ1fIA(i cF]d.CWx\_+V8"\}{$1\1aFĝ.cсE0FEC^7M4˩N&(wbg.w"e'>-?86H}9GҤX$yJ+ W͊+tl1N$V0BD37JYi:輖 o#NKKΑ>9y)kҘ"hjaSN#t| B#i'F!Icc=ZUBw7Ƀ頛Oӆ@Lc,ĹN.53b3E"q{=Pс۸x2i_0o1)AWw&m+ߧSѕdZfnJ-1Eꐢm/a nhU BTÞܹJ4D2t -Pa"W!ϾNGSaҁbeP(iHJN㚄hG"sB<[J^l)F;2Urs\]؋t}6/cb[3i# A Ȼ '3] -kOXQ v F$UE?_P*?!6a-6j j3E|;h@eFw<:1N-H],Q-g0-$/tw;,[ B8.9kF4mF1H: vګ =(e{ur@JI yɻMq;c"IʇV%wq d4`l6n߻Y^kA+dGf|(P* 4,܏DK RQUjEժf޷ w nTe/ne]kC^͚GViP3>Int-Mc׭Cm`:}GPpI_FPG?$ bXrYR "gUq@hEA]EG(Q an oȰr$W5PwʁHœW4bdR/_RY]'4Du_5Ϲ!t{8D0fV4R; 6O=4b5`g etIzٳCZh} ռ/2Βu,䠂!yRQCffr0NDzŗ·,R'@e\+T¯$ݍ/\}@=ܓ0q]HS,ɡut1O˔7gNSI~ocI+8'WsӔ▉ۓٜb0]'g7^KA_N׽FAAÃf{Vq8>*m !{%yw&/5kfUŒx|8xI,,V:)At۸`vjUygLoqA-}Yɧ-;gLۓ),+;"Q X|ɘ kw7n} dłGd؞}]C$%JlIR]7˔QJ(8h;]: -*^u. aY:B\-!I?Xi,10.(kw[g+dkh _FafY>ۆlÑRkxۓ]K =ɐ+ַ2mvY.lrxLVGAK&,DBCF!l`|0'f$diCB(F39 NV@JXB׻dF"(;&*T"=* rwێ#rPj"fѿǞ:O*,CO*bvu%D#I6.~!6NNY/;k0 |Sko|Wy-5z@@6Png HD™SH=n2;Y =p;0[4Y- *K'CvѽhC^/oЬ;jI sa+L΍Yw$`{6i0KC+oYiwE1F}\́l>?ݦcnKyqBZ)^۝2(P=w8w`M*P-CKo/4-^ª5$B }?›}mޭr&)y#S"C U 2k6|#a??VlV,1F4Q#Κ!Np3MqZrK-w%Cr!ogH4SYz5ۥjЦ+p !yN~A~ y|V+XF<ԚfכMɬo_1<`E4sj̅?Aym]dM >bHhP3g^ rTRrm(DU$p\_ݏv$)M > [?!7چ^qB)zAVjW#8JW2 7M sUX}K!"g#oIY2`֐#$Y - r{ۏ7lyҭ6/y6}tBf$ۼ- )1MxAXjG]dj<>$WBɴITK[5eW"FmY1|"U(&d 9Kz֦j@Ne z"KKY ~G]zB2Q:Db`a yKBcdXn#vDwY"%d" ~ Ad4LӅhZV![XQs94iDnJ1D[ւlш&*ka+q+7_>{"nnILD#)Cɳ%G.b!:Q H'(-5iGR/U鶧d93QIi5j-Vmӽ pM_Dզ jPj6-KTRl^j hugItl"Qjx}HD"Q,_}٫(|JPJjIpCiBm&E\-/g y 2,*H+䤘v/ѣ|dgdOfrNSD9`~1~I47j> gBDr~^VDb//M,<>av8* r_.ŏjp j{S )Y}f4tϝc a!/ˏC0/!!-`i! g?jéVY@Hݯ** vkm /Xˆ̃(OCOMoCAAAAA Mb ԑa`JR RMx G?4"'?^.⬷|xڈuܽS*9tlsdJP>8Sy3Y|Z:Ğq4M#q$L.)X]݇o=?Emy}{kit\&6gN%{Hxv` GVGFhƎO0x' m#4 +)tWJlo#68Mpyg:BEŏa l-*b̐@4_;#9FYgV»HV&6~CF]vZ&TBշ/J.B2f_ k A1:|2IY-(tT{Fjy$8vI ߴD`P;c7Ng`ޓyD oG-I2DI p "5)%: [D$KaU!Q*U~|5E5fN/xk0r!J=Ohw8b_ӑ;_q(Xl4$5#F):tc7;خ"# l(c7<8^IôMFT1%ɔ on8Ьߊ. 2X ZdPa!ن*KFTy6G8sJYy+󝋥p#OOz^b|k NN?m `yAy| 1]^\fm"d8 fgg[==s|ˇ]C\VHc8LYsn96_l?ٞt607&v|^ y8[8?GOeo?|[57xM_ aֶ$?cxu;0Նj/v,~8=4,YmM^wM'cs0lC>z˄OO,c[{bhx=Ɠqswoc#?6>|6i ldq0ƛ1p9k')^x0| ?̆ __poh6{14b£k'o߅vﱒ]&{w'*kG~q4 do< +=_qw|YÝWw6vz}thZՅ0koz{\Oo_l{}ճmoN|rzm_} 5ޝAoqG/N|Zk[_j}@x`cZP񃑯;O>uuI;a ënhɳG]v}/& JzZƾF~쇩̏;0.~toxwȅFI߅@_ېonTy0zsxz8e!W^ZqQ|t4~}қ>W?٬Nk&lh|L-X4|soz͝)Ύf| /UOk>;n\ONG\Oo6|sھ3r짽/_MzϓY)_.Q.n{B] ߼zb^MM8~6aIMgoÜE%~nMİwXO0No׳}}&ߌyȟ'xYb͛kW׷wH߼uϧ> i|gxvݝG?o~n#n{2̿Η-wY54{q|co˟ 2?i|m ҏ˃wMp껯nN3{8]Na ig5t1@݋,0>oQS^ ig<]8lZ/$3~?no/S_zrxis]kېK /ލ_[]#sgust/.S80 bIpǵw\o\Ori _O_.XCz|2pEf`ݣ!oٴ??̎G^_8V\3}sexg)oM_~5~X: k됞fw=YlIgfO|+^>,?~fCjpwB_V/ٙO 6GG:e'Cn^\17I>-Of k^._^?,K޻u^/Na%_M))v ^4\q47Fo{޴~=;=;㛲rdX?;8ߊ:;8X5rnnh܋瓶_Nػv}|icڟ7Y~e^<3m!٣a!זXVV\S+_"hV]_>-fo7~: FqcK=w~f__ttqv4Zct'_&p{]?W^ۅ0~ FepWOgl]ֆM7^ K!G$?0mmxݣI~6Y[:a|%ǭI+? a)#0tn~01<orֿ^mtbn]H/Ng%럮wpunV!O{zY2U0zJ#Ko?~3i5tvt6n&ks' Nl.G?tw|Az9{h6~isJjmX%5rai?oMyhjxsn'va৛{ϧ=nvxzQ̄/)f>;9Xz1lmMpʍ=H4ޛvv388No~x~ˏuBnoz}wWwڙtѤqK?|x~27ț-y9b' k.P.^op8~@;|޿1<>uxkyftijҵ߆ޭV+lOCYP?p`6o"Dx& 7a":@&Mě??2ޝxd3o"Dx&Mě7o"2x&Mě7o"Dx&Mě7?)x&Mě4o"DSE&7Dx&MqD;ENǘF7o$HA#F7_h$Hx#Gf#_޼3o$lF7o$Hx#F_f4o$Hx#F7o$Hx#F'3o$Hx#?F7qh$HzB#F87o$H/n27O@#c<#FH3o$dF7o$Hx#_|_f$[#_|a7E>#7o$Hx#F5?rfc$r6#炦pF7o$Hߗ@_R7o$Hx#Fį~s7#g$|HF7e$g豑x#Fuh$HџFg$HH-#F_}yH7o$Hx#F7Hx#F7o$Hx#FHx#F7o$Hũx# `b$Hx#F⿸h$H?ď7o$~_#FH7o$Hx#Fm$~=om$H_kh$Hx##FHșٌk$ F7o$Hx#_1qH1o$Hx#F7a$H-#7o$HgFd$$H A $H A $H A $H A $H A $H A $H A $H A $G9r9n9f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R99f6G})͑ccFJms瘑R9:?YMngo-toml-2.1.1/benchmark/testdata/example.toml.gz000066400000000000000000000037221453566013500216170ustar00rootroot00000000000000E`example.tomlmo8WY4åE-]+,M!02m".IMJq,Nn_fp 5f??L1j8Ϥ@l>q0|.?Dz+zB $8  =lRm^3sLJX/lkвRb~(Wlrt8T dTځ̹1L9|$$ߔ%+BAEȢs|eMBg4١0 Xx:ULk~;) ib,hɴm*0QNun\y,g=~+}Ju{JK+^ }9D%3tL q-ӂiE O77-؆1P܃}RNS7ЎYrio&OHL8A$4qoO|m!^D~ކ&2WD:R{xvf~ߪ| ;_Ra?ЋR,BQjf#ʲګ?jk aY7$g"]WM\S  H9mGfnK~f|3t /]4B_AAeTN&ޯIzw2} `yKWײk鸎nh1Uo@"}gOHB\?]Q@VzV .s9-)D(tS4>ulbdXM6!7F=%hCVho$:TNCt.D{צf:if B#L1XGE*Do3E%9ݭruv|; auU>LzЂ L П'xa%O룝'"gxrݡN>m`ߏ &YKtlۓsp; ? UQdohp6z3p;+2zqBL54@f(_n ~>O\$w3g([N_|4=kaM>zwELe. 7`Ћj@Z2 +&A;&g;.LށvG&zܔ>G.J*i3l\C`0zJD$&S>}u;Jw_;#%;m\{$:=$ژ$I&`Ё kc7G[$.n/[f}7~>r0 c7_c﯂k˹($;|:2}V!kF# !+#}p>!#BNk;ѯEQ3YJ"|}s/ \H5f~Q3*RWT/ $&+L]n`wajñ﷋ձMtа(z[ša8H&A:}$uCD &::W<}ܸYw]] {'sdz ár.OFǃq ; P ]/5 )Z<'۝=W h7&,w}t-~S3<]pWmECU/x+t]n>vFXhR;콃GA=#,]=hjP1ugլJp(Nbz,k#45Fe,֔h rsJhgo-toml-2.1.1/benchmark/testdata/twitter.toml.gz000066400000000000000000001303661453566013500216730ustar00rootroot00000000000000E`twitter.toml\{S׶*}U: urnP$9IZI*U=0AA$D߄#ܵwϣgyckk#GF/7,{Tt4HF[ "Hj( ?MRlgDB(Q, ȟ zHNXfI)1sG0=U(AwXwՐ;͓' #zOWxb &c!{r CV;ZT|K;}C.]hxVb)Is!۹G4'p$b - zB(c )L:ybQ? tIlWAn.5j4YL < 1DD9;~OK`d,#TBy'< <` h aĭ$iesPҖ0l6 4rpdqo8ԑ͂YRh?hW$$rż ".${?5{b(7Cyye aZ,uAjv,-gmժ%b`ݧ`j@wC~{oqHA"Ajh!/Hq^emG,=49sтnWelEfI8?XR =q#1 61\ПkD=M.!lER 'O- 6^5o`T=h9#jeLz:yFl.0m&F`} (Rvi4Xh`mZvyf/l[b/3Tl.qIQ.kQ >Y|-^[d&ڭh1F"X>)B$i(VYd9P ;^p)b5q k<$v2EH$m?Æh wamzݡe͒r:(0::[}~uvvVo0%Cf:M 24in)༭UNhk%)Jت6-9ξ|c[ƫ4G"h)FYSݓ陘m }R3 ,V篱e -aϰ޾gh3fMs:bg\įeS~C #6`YӯC5 ?,[FO9˕X֥P-}(]eĭm}QŜO>L*̭釯 Y<~G<“?i3zn 㫛# @4-Hr$IJv$6t? WXUUOX$ItԿ$mYwL󋝾 reoΎWqc48:iz+egV{C *s> yņgNՖҖVEQB6hmg2P-d\Y%.y!¥xlݍӃn3WcC Dg㠖.+Xh؃if]Jo5>1*3AVY1H8{,|~ B&+G!_i[BhXJk #(Fv@ "ϰ|Xɇh 5M37'{Së6o6~U,H#;1<怆 JaDZ锷+Rq^kY*GlFBm䛈*βy:[apA"6f/V6]d=ij [xD$/I<\+~ķPHJ(rj S#^2_aeadPY2\Yikˆg,evkL,z':mzzgގ+75ц>Uiӯ 6~F|2뭉ԋq)xXeeb = ?dCắ&[%H9ėɻQr|>R$rUfsy~yDCGdH9fY4A!J<:rL嘷БjHi ;,gUJRR%tr.~ϼ ~WܝE4I aXe%FUlwfhKFa7[/G ~'cF~^Ɠ]"?n*O(6|6W^b$F!<;}ZCfu)LLejX~'Sm< JSX ~m}Vc֟:AV/)dssɻ"m>"}},uK:BYhv^U͉ ?J6T%pT`L|\yYH] X#7xZXiDjr 0Ē"M+Ф[?^`C WysbxtE«~WN`(ض z&:~n 7ԷVךWDrez?m Y:* Z[N4;(ɞ=,*_`Wf5`p ;*H`Gcc(AY;mG"S HI"O4|c -QVCo'sEE`~T"^A,)yyagF[I%g7kduНIz~ǭO( <>cWT\)庆{RthW[ IQwq6c$,?0)b1+1Ȕoc$m#?֞Nc!ayU:R4[[N-OP$Sғ#;N98Hе6lq8'<{#}X$tb]Iڟz6S>ʭֶ&- zEd&ٝv™{w׷+uWͭRXF\yWG^'Dr[xh= cC9O|Gzr.;xct^IC###K>|T'ʒxcNJ/_I-\ECU=J=nX L†t8D/bY.dC5j9kSgQsgzfgnAebkCvs=JeYN?;fk7XtsazJ-?&HX>0\7qίDzm0 8;r/ugptg ݇ uLO; ;ʛ㎘h_;Nڇ!h\*d8b(.[)dp,{r(FEpnMMMk8$7b؀c#? ~?1i;aT[5,uKZjI-mcJ9k}^3P_kއVvm&=Rm,aWWn^ Fy o=c>N,G zuX}֧~& Fµ\-_z-yէ}40nv|;ڲ_W\o0EлBc@_GJ' &ӻ*d <-U6ڤ`-ӕjWbx2/%9ť,kK,'2+ $ؾC%uO]{ڜؚX "/*,&c_m2yT< '|kp PFW%RH0BqmA5- ߔ> +ϖRP\,B5oe%݀;no [7cօ&Pպ& N7K$".(qY~t7ؘwb;Ac.k~r疁iSiUJ% 6聉RxXL@CKbgݶ$ ) M#6"M=XxMdBw6KwiJ0] ӺÝoCh*VBʮ[)ʌ(|"؞lLIFbAw91tHxOYMlOnU`y#|қK~m;g6"٤1 /Dۻy K^XR/TTle6*l'76glŞ Q*߉Ü FqN;x0(r9Xlmjhs67ɓ?Tni?x`pCsU6K9z9ldFx~0eb,c򬒣 Gݜ n'̩gP o0ѱ 2Բ٣)׌7 Ée oX|`-+NBRsB8 }nc$$0V}W$d#։}ub &lv1ݶ5E=ݧ" OGO/ꛑlļD"25a&͖ H$dl CoTd 'APkV],9HҾ$W6=f(INED KV/@r-zHqoVWy44^yLN:y3SkK#Oxi@Zm q!0@@xߞ=7. G134Z4nPB=4i3 mNhs[c(~7orp6:sp(/JT Hςx31吘9Lj̔Z4 ҝhw\1˔7,ﳅְ;\4*L`˪hx>%MMcCiF?i=>t͔fZQo \4aCޙC಺y>bb7COy " ]P{'\31rPegY,$scn$Ez_ Ӳ'A;R4QM,z+ %VI(I>Sը& 27~]Gڳin*1 ӄ%ڗ~S9RK ׃0UoXECY6 -5I_xCgmx[}F+2_<_hu$plїWmJɑgN){G(B9!\dGVjg"/(WT]!(\%< ;dReɿ}勥fV|ܱ$*9@Ffh~;F9ӯB@0afh,A'4v;~qB^1a]H{mB 1nD1:+2%HJXQHUZLlyVРdh`2]m((q WdEaҕO|{hɵmi_9|d{*2r$&2i&w W ΩXs#ň'(:TGwW"sW"s\%2u%2WU"s>*}99bcYg]9oj4 LQ cQKuu3B a0_ a7J)5;vttI&v  9g%X+(DEaE8,%*}b[Bx#b |q"(pMx$Qϗ\5bC*bT%QZ5O)(ϥ2Xm?e4O?"jwԹ~gs qAK.pz<_GigY!p_(R;Ckj|d_{Lڋj`0fIY&R a2,^s56c[+'&:SD`V(pȤL ,},23Z&DίhZFC? ,hyO9ST12be6a9EtNR}yEqvp|s`nʘO`R_AypRZ76N璛e;bHǏ.Kj9߭b.ůAH'ʲ(#0d 2y~]>1k| Y5>&ky.5I.L0KH 5煡c Q _|N<0(=$M8WBЯ[k `QXa6=!VXʘJ?drˡJʈshK4{ݞfx(H M^x=cX3~Ї9FSq8xEXnuXhܶ%մFk88M^YcwWԑ^je4hZ>ZYɾڦ!yQQ9Q9BTn:[M5}:(GG1M/!n+M㽊aSV@5ע$F$Y8E;;=,E455,$Y5ߎI-0MUaRIN'[8$Ο8PbFiw]Kc=i+%[_gg6oܸQTEOE.bHE'pY.!UGOUgۯ5{w٩蹪ꂋ$sHW}Wz8AAxڽ%GH`16Fg#ӃڟSEjӘYA'| 9XנoZJI粔0V5<8^M pMjaPtv+PztQ}Td!&Mrc"/DF+A%Y!,o.W/'˫_XÖ(J"sey)rkN4LC^I̻}H_x ';di fDaMN3+.^qh-ρp o#eytj"y+I$J>ԕ$J@% ?)$=}I:pɮ,I(+caLV$ v`\i]z`3~ޮFx!5sX?WTʀKpZTxַ`;xAr8_ش0H׈^ Õ< ;uQ "of#Ox^WgD^= ^})"M(FA[:)HШV㧎Q h}}.V=X+aKd}3]@D0 _J9h6UG݀:Yӹ0 Y~bSנF)$#p2*>J)|[@}"c Q:p)S8~f~KоQ%@4:9wSG45??Y482^ۍkGgQ!9?m|/+ 9GBn.mlvMH2AmmE_ޅ^R=@8/H,2OxQdX#:Pj\-+v%[Q6,:*Tx([|6M*KH @Ep~C}@п \p]}ړydtg1(D# A>e|U,u#a\9&EUlw)A ةl ̈́ fVbrDfV̄K])߾ aS›r|xoWEQde8RareWDE~'mi_rG{MFdIO:TqG[V0eFd ~z{un~zuN#?oܨw[W>{^WcǶg g#C`|YT~u&yOTk47`.>ً9PY5ngxw?nl߅~;2l7zVdd[qRPDDoM뵥׬ϳ}5ᛂCV}Llrτd"gWo_;XzF~B,ߜΫ6m/<[6XSCtAWu+[, U7G(c ])# v7Tٕ zՑAJ{I\0>ޢssn̏EQEkS-7a=}Z?4~;G)'肉!wYF~ uFM&r%]hᒦz4VD=@Q7V >S[:uދ*Wcep/+n7.o WSXzAVk.>&rZ- Q0$aiv(B(tX}*FXS{hI 4By:>LZ!Cs`TVseee)OJ/EÊu*WCj22SU&LL恺&(BcH8A2m uA6M$Qo/cZmޡ@Q6P{'p P}ݍ m~4}` hJpp9VX7lU!t8v,d yTv=^0/Ϯ}ko(ezk ~ZR">fܵxpõםޕ~5%g{9^i\2 3s=^N A *~ 8`@dwEQ1tO o-I'tH"9 v']t[RYEC?/J4$0Y&"u_HꂒV7rۮdHmj(*+-ɯIH۱G@j[i@Cs;uLnBF5:r?nl-8> $9_%ЉyG>!q>QrF",QvYIѹlOOʬ,],d,M*BΟNس쵇4PV~kg-QwSܜ z5"~;g ASa1B0y fi^Mv}Bw9a \‚zrJ@Y*aޔ ->s?GdbU܃Fg?_([8R~삓q9+] 4L(Y,kXŁ^_V p ̲䰆5#?f'#MStE9(jjvD5,#Qݺ@b!kQ쯹4'߰=! $˩p]}8k* €00' ^€۟kp͝n45N8Q H{EBEj3h\1&"- po.1B5Y1{Cm̅&6a5C-6a Mm *ݶ?מ,$>}F+Q9pB9̌UĆH EgrlLV^mqܛԭZi޴3TqJMƪz6!oNjX{+9Ea+V )*HSʹHo[\#5@]$GNBˠ!ȸ~PN2Rs?'#6^'_`5ص3 }*/q@eTޥO-1LMŌk?&X=ZtDL}I6e4~:D[i³(CJ\cxD۔;dl^kD[ÍngqOc3M-q:\8?mL}tq-!9tCZ;l@V  f'M+R/'s7[mJIa2&2O :$*ΖhM3?7 N$'qݭc}A GV<ȇAKNT(6G̿%_e6h,NQ 6a9Ho ɅԅQI&/Nf2kc/qN"jt'nn:1Fd㳛_l&ی$ZHGȔnju!2 !bB-*  >v)rF3oG2utBjuƫ觵/JgMGtE R#H"3'ZR)t[G;vwqM (X8 GMI>TRN(IH-/'_=QDZݨDlc h= w `vIY j xhm u%#i'uȞ`oк"`/~o=d^?[m{lϰ:|\,56'"l\A? Ya:4[LKvRgi>m$dˋ1kJji65$#Uլ[}گ]j $w\z1u})1Vx"--AT" }. 9a Cja CNrkP1& )!g,>Y ^qД5ONؑ~zX/ 칠i-?aԂI h um`og8pI*}pϯ!Q߾?.Vnj%qRڊ `ܸHoŴ /^dL,f^=e.cTTmg-~OX\_C$S)Jற^Χn2d\[J&|Tu᷹it-;gZ %GOXa_{k& (z" 0+_Ж .f<=>ANJ1vv/ɻ[Ol pqaIj\Dvj,l╿]D 5|@3t;9ϠrbrLNPd Ԣ^&i^ 2WѰ !:ߵ>뺦vT]DQtJp* u`EQf; ~5w:Nt|=]tyԖ.3wCLgL1`әb:SLgy$3t^4AB}'[OP͉o64LCe^dx擢z $g9H{wVMz Q|?!2rI^in>ѷin8˃$}&_J/rD`d|ʪ'`G0"eެ.3?lBkjFMܹ`2$z ܸ]iSSٺ+o9${=ʺmkwխ"DI M"C $ 8ڨ·;!Zdu;R@,rEPgqn^c$fC{"E. HV8!7(ؾ ܖDauy?UAw'*bɒL\¹>4;U6$Tj˓0rAQ_sy.x<>S,~FLRC$$"&)Mob y1)tU6B:1JYS' ]")!"(td(HhEiQ;r@gQY3Uu@(Җc)C]lҫ.eDc3S"2\ŷ !JUv)ZeDb.ĖܘepbZrWJx'p!"8QVISGi:uJ%wKSUEd(+厌$`0%s.k Xеi 3%e02W A'Jn6SpF] j T L@T` GZJr6k_ l™O8lx|EOM~H1n^rֱ2fmmFG<=7]\'u~։dȭ@F}BRXo/&ZlohwkDmcye 'kom# vK-RTUPAW,QW8UHe~g΀@ߩ.Ҕ6r?|i/0 iLI4@2 iUJǘ?؞Ӣ8ށ%X nw7bUzzwhx~!{7doЃz+3>/^Jyjb^Jy(Y"orY`_W!V2ЖD͆EeS9K=dem~:}Į ^&zޮX$6tauǎԞ<~&>u;:zF{=}VYX'mݗm +.Y9&ON|OnS[ -5}a?^RHy ^7B#o?G\_{vM[&:!jv]togSu@0' QjkTQ6McqvC _/ҵН>zI}@I oSEđ2$(:o O)NU`8Gii4sPTQ@C$a6TH=M,>ڕSRUL֏ Ɋ˒J`ktj?;UVSݧG&FxvPS~E9ؙC/4D`(X KW#t7^v{\>֫\$| 8 &p?voxx|/aT,6F*,6FH!d{(62aG# @`6>,R$X c&Ƚ0ݽd&6әLRK$+7pLq^/3:K;UmVxyD&ɛ[_hgrz`=ܒ/6RU EdDYߚؾ El9aNb)(\/HVTx,ǜRTUTF++2\#4Cj-wŕZ0Dd6BŻѕ{/ǣf˅okX 4遜i,'' "@W9|Fl| kP}mqހ%:8!ID28>?^ta ~kk} 8>\υgҊImt(4 {w$]іv+/pfrģ[`R<%7ǒAG|Hs!H!/lh*=U*'`r؆8'eNI_b7hW!3wo"s{cikp(QF\V88RN4н؍Z+mvFHx$3xl{ Ejc#WαIHIŝ5"/?*/#'.ekJS_0k#‘~*+u:^8KݍVe[ND43nw5^z);[~9!oj^sQ>0 zC|b!HޖĂD"M/ `;భ?| pg6>,nC{{]΋ i>OGA5 gi>DB/$L$xCC(DzYѻ_ɼ j=ӰUmh*:Ƈw Vp 6_X~OkS->X ZDD&^W9tkӌ6gf8~}&|[]L>3]Ʉą;vj M\Q`yr'nLyي\p]յϚlZ1ckcUddN&,kZ;melrsP$8L7Ȋ|:1iua`o E="KXԛH>Uri*Rz}IGvi母.zIk.*`KBѽ;m)%a Ax|aE1ք93$`UdADh57p3RTUpʡ2aN@)]u?;صtָ uޖ?ǣ&998_BËnvWq @!pu][l=$K?4Gp$}-MiCi٘Yk ivT-o& VlXqrtx0Obdi*H` +,RvR<٨[>eIp*ƒm'Gw{x$"J-^Ytv#b+7/6!HBX-rD9*G<Ӎ>]sğy8pw@Vxj1-]Y^T.#d 'XG @J0シ=9^}`p/%$?`=l:=ܚ橐Hj*|<"YRv²TXJMj²TX RR}eO}xg$Ar`lu/ibRt,QT<5RMRU>YAFNHi1laCPOM_K ~=É! 6ą (B/$DǗSӦI(e,ߗ`o CRw!20I 4<@N&7^UEW_,]Ꝫ LRrn%M%ªx^({e'ݭVwc.-$- ӜxisW/7L}I{Lc^Mj&1iI{LګR6Ƥ}Л$caȕ$3ze보K2"8%:C"6~vXY9fmݶP$lpKb~\H U,q!f ,@MTQRXVEY0JJ9%RTU.$!+4:` ɎN/;[snzNی[0Ȏ Tv3itLI4:1itUJǘF?CT|YD!Lꕓ7>= ua.jw#)ndθQtpV5-J'A}ӯ.G{'t'+ FÅ|XH A.lNfD+2BQ6BAQg'+Ƃ*$ څ;rWڝ @D'3$2*)-f>c}o r Z__|`IzLc^Mj&1IIzLҫRF$pG$=pX=$gZ$+j"_%Ϋ7GEtYΜ @L8,2yɂQEM2.#A ;+st dr3L[*_I5bQgir$0odPYך(w7E)ìOyo|BYbH ³axɂ3rӔl/-~TRҢObE`%'ˀ`%쵞3.7ȍ/ϑ_ut`y%Q0GzBf9Ĵ؞\ЪG?.[`5bY`V(XV( fY`iY`;6`#`đ*$ɐzX;S0)f 8IcHL5ex"eۉڵ"dep0"u4U;4HpdZ6@-sf6 )ocVdlʃϮ?_ IJ ŚUS%jsrstmH~P7c[R?QBS·^g$iG|qj֫>*3`֪3C1mZ)RHhqY m=Oioiq]yNU]~gK6%5 ]f3Y:hulW0m|dύ_O7k*j1Pyύr=&;o<9~nW47xn' -eΧ2Ay'LtaPV+c ['2cs $7XV懨uQĖ^^E 8hX`+PIcM[YBg',DEg٠$ ` e,Li]CJΖ)EjJOrE!F';FFCMMRjQArp%VJwU$yOOTMB-1:>$!Ę2l/?$f:ӏQT܇2<}<|巴߲^joi--<巴Goi t8F*߲_+?)}JȳOYT֋'0M"ʁDxM( ;&FVXND8!2DżI`%NOؑVt?\>_/H$Ju-Nt 𱿑 >R$9rTl BI;FB /wg=6oŸ]z 5YVjnTqvRgG"G=架Аn:N5R.GSww:o 9t՜QBd0lz:h/Oyz{N58-kq-ym8qro/QAfEf\#5_Hf#7kLf%ċRVNDs BJ0ͳM{*Jojg KOXbQdA`XxI΁jS}J>1(r@n2Kg[ކS%t<.fӡbik2M=ݠ49 X 3 ZT|T@ E}`-o  >g+l:ic$Z1Shp%j7(؂7+RWG§5|QH`V\?EwILUB ?NAnƖ7hG1Fm3ORz}%ICFɎuh%LotkCxG"|#;{URenh7Q|p/s4H'N@\A{.>`Rx RDaROgQXQ\7U2:):f8ỶF1I(UEp($F)wn:q]y<нr'sLaEF0,qb\|XD}ޱh:| E}ݭ.{<Z"g%F1!}~Qp\|Fmgs8,l:HGԧoՉ b?~҅˻ၳN_:K\?Pn/U%'?q?'mJd#p5| ,R@yPFnWNpb E@HbFKH_pp7.28"aSt2o[9^)a>^P槒agӪ:ݺ/3,6u4>{;om7< i&vo/Ho6/,|;8Y|K( _;UneC2}&͉>ĥy/VCHu6O5&ʔNcji H`H;Cz{RH!H~Q0I z/zڹ_l$ $F6xB`?._VHX#0@ayr,',|ELNo.S^ė ?'O` blG(mM]B%MI<E%6 Kɐ)5668d&Ԅx995 *W Sɫ՝#*յzVWg7G<.BOou3eIOG(^)vM&8K,`a2d@ϋfI4W-(HX s<_$H:óHvǭ_ϴն$9RUD:dZ~Jq): yp+~{ xײ:ET iSwFYLUKm1uSg1uSWFK#3u sdA-ְ< jj^'l&mZ`(C0|4Y"yzH~|>Z6Q7mnH%_t:8Eah>@ׁs"qnFrrrwB]D ċD{"}lAF #p)dH[d%!F${wuo+9dw #dkV:xRAۜKm/-lsݬ<=fKso?][Wٶ+iϋ1uU/⥷ն[Т%U" E7* crr^ gU{BSHqUVR}sIb/ h=hD2{hD;&1ѮL=0&ڕE$H( 8kEQ湍Ig(;*%nZn*s; -̽vs(OӵwpU/]Tm]t9Jpkrp i[jrL4j6$w_Arx(hdG6$ i$IJdAuzr"piEn3dFvh )JiIr'DHrٞz1e;=OmM{ ^0nQs*>>@X_P;\buFi buObuOw):-s}xЇ}m:Qئ&\6=%cQc"O sZU/ik;g#fױRf׫?mz2p2.pr۾P[E7w5ŌY*為gȝ{ǹ]74&qϊ"kڞ)i"qY5G/9r>$? 3Nlu=j_E0p|oigSè67l#`듟&v%MW_>Bަ7WN/&PnR$-F9Wk:"UI,H~jSDŚգ{Ə 씚9Qcb =ژ3,m6af++Nvy8@TY$>O1y=*8;ެRy *lQ0VP0]owQGe\^D;z6OG?l||^]7܅vZ#zߩg-GB O4f#:!xK4mP`k}pVuڌB徹>`E8i_;E(fswo P$bFhn톧3UWh6!Tnd6l"N?QiOK'\xvF1] }Mt퇮Cn5 ^\ Wk8^;Z5?5D?߂=%U^Sж$2dLC Jmcw8 ?ĨSeRR!HPycU剆;qKO|s/7emwykxnx.6ر<3KxྯVjKX Mׯ]B_5do~6 :DQyUd^ӆ$XttWh!^8?ZyO z/0:Ciӈ>їџ*bwaЗ 8ΡiL7hOh=ؖg1x,2=0F" 53䤝c3bV >xkYB1|WKJgzL7ppb9x J QJlMfɜ!znC RnnꤹDTkܟNC] 9c'Nf^1jԣp6LV[y>:=p w'+;oh=-ulBcurmC?5 >@'3ޢC[F8wƫO /a)5hڦ|)4R8^:vB#FDUTyZn8dD+V&ԎCQ-x}2PXgݒjn^?ALY[] i?tO&/tYɧ&39iCk~ghSsgtWskύTө5c! Dmw |DʉIcnL`? &iN5Rj{fkF0X2EL0hIK=0e=!(c-u[+cδ=]؛41{IdS#B-BJrfi] ҫ.HG$x::N6NɋɫM k'JRh'YV&NgFn{bZ~NTӇ!7-R5GĐ9MJ1oŘvmrSﲿv2B_|A5qjZ)c#[+re\-W.-Wf˕re\<0\DZTHb\O(G2)* Y}$6dƞ-h8(j.qbE!$1i}dNj\,hU͜ɢ(s Fܗ3׎_ռ͙ 6&;АK=|V%D<+DSۋYVmƇח[ Oi.֧^3o}}&1 xeP3 xLc^zbL+QOl xzA4N@W&!h,]11&>}u* ? ~LYӕvf)Xhxh!eݷ웺c&>yMRD9M8NTH\qm3 "TWƫZ^Zѝڦf<<m+$HxfYϚ+SΔ61j1)mLi+O7)m%{ פ~\+dBV* ̇cgC2۰& MdxIv$ucawN_./T0$U2Q#K[TVWPgV)cʢʒ7W[Kn/ŭxlmZ,ҺmB&?D6&1j&1lLd+O_l% Nvq|,Uj1@"12wqj'֖x/Am<!y!ܵJVҦnm9E#eg%w߰o2#O\ezmgI"wYrg*/#) y" %Nn;RWlpyy|5fv^Gr6|]Y {+\|[ !RmW.Z3~zu0n p`4)U9U,!9HQ E$lxa\5*u ;Q4G Tj$%KfhɄ1ۘ~.hZaX\h 8[L&<ٍq}m h4:aջ- Ӄ"lVoA>6c f xbXhy^j "߯fGb|/2#qqilp&); pD8AM?J5*gn{BdXZAAQAٞ.ݩ^W77ڮc f1 Hxw{3wL+wLcSb]i_^K NIT{Zwm||0 agq$؛[-m:Uy_L (nYPOȖ_dIsM5?̿sߺ,eI/n,poRןΛmTV?Z~7'w.06ZM[nvKa2C^Ň hl,-:`>zAxe«Ew/6p8Klfanqg!Jx %wS2{<1L1#tq'$0.I>FF Dh.mb0Zb[6,4ż-\ Ci]; G|L2j9婒sR$i2rnm;s|Me V3/ ;E1W5A F-n E@TVRC,-OYe9vV_sVpKKnU%UY  iwu|ѣ+!X$X.H흖&,2EZ+yzr%唘k3ƗEBHcc3]gO?z<:D`$t +/wlXgP, zņEO@4{={E֐3זזXl{`,b,]f5m6mvy],]n׾mxB1W+P@,J]ƫ>ς}6gZ8_Qv$bMwOEU8]؉I⭤($[;u 6`1bpÊINw.3I0]#\}yo#:ʱ3B8dO?xe.ȼH0P*. #k&`A)`naXwJ&u3*E*2,$~(3RJ);.#Ʀ) Y|*rG;ή`K8\, Y)sR Ȃ)F*s΍y/ac(t-_%Qa[{*~,GVWcv>H𒿹'bǜht"@Dh ` %2Us>ŮP^6KۛR /(<>$5~ &)o==;Vq!홛@HX2T 4^_f.9Sɰ S2X|iNu}m VJQ)S;`W3~) $! )+=}z_g2[FP3oDPRA# UVQZF\(dZoH !DA(DiQN_F5hDQݕJ WvZHR# (C)3o xirUROyg(Tbr]z=,95ȩ:95ȩAN rjN2N MPЈ6-m) mn6]JN.%gzչ#_g`NBBl"^NXq!:*3.aukVi9XdeOEJ'*G L++Pry2 "Rn?2TJvGWOQUE" Az:~8WJ(;}"SN`Ul|֓Gw/D2w(q^zuQs^z^a;FءN@.=P J"~ ۈilj,G(f\Ge7V֟ ɋQeN=\n'Vn{ADۢpJ\;b_F *{% +@2D-H6|}Y}Ty&~Nhb  ok|{)onGli8kFN \Ajbs;xsns[}Ԝs8V܎u97ƹ%A4FiL=e$s*0l= <6on#&om)/ N 3m" jJAEeۼUEr_2xpLd'1pF{ ~0 fF2RXo=M~O;VJdy";/!x70Ko57YpW;\wQ.{g7ruq3puQs3p \Z_;סgLJBP<=Q'T l-yZALO[xϞa{>4"+7ֻs95(:`zғ7ŧ£QZ3:ŻꄂCNU::(کW'Ŷ3%̴K$~2]O֠}uW+]bUrϓJy57D={DqaTubn?$TYikk;x{^}Ԝ'8W'Av=Sz<(ti՝aT`e¨mL_EEH$ -ڈucjyKy|nH=n4 #QF"sx/:ZJʛ!;/@)M ,ﯹm#/ A3f-ѼzZaGs;xsns[}Ԝs8V܎5v9, `byX"P k+¾i(DsCCB" SI:e%V J+Tj>qzsW"J¹Yf9"aEI"*E"$" ``MVyRV^NG`vBS))_h;;-$vuB}eog_v)޵?9fK[G]4m.K[$%?"Wq~DvhKʌ699b\l{mXi7٨!ۍ[޸F@Ƨ줟icکsIiS> m&ףo=^Kz?㡒s-#ҏRK3qd6П66K2رc.38{멓Za`n74K,yDCHtmLn0ADHn+*9LLyhm;*yk&KxsCE"UvI5CE;!TeG XH[v^PUhhWF$5 RY |K ɘ +o )"",p-CC 5Yh(_8;ڂ~ O>Lc'<#2xaVuQ0+fìxUzy0lHDQ 34?ݜxiiyxE\qH UZpf/irA\ًd/bY Đr7gs]g/VyfN-2 (Ȋlga[(] ]l4Zd23`t|K[mo>H-Ygg8ƙ:58v4 ìJx@zrƘ@ QCLIDk&"ӓLS3%Q Cys+?'Fw t-!dEPVUT-QnQNa^ i̳љS5KE2 Ċ㙦_JGv}+;DLT*-QD`_&\wM*p${G͹JiA=ȟm!g!Y!U$53&'jsq{L􅯄.>wB8cDtQ߭(:jN#fkCa#C}J?d 5oMذ&:3ab#¤%O/gu|ǯtgz!m&btbS a-1hs3'iKF~/FOPe/)\CLyX`3IXL1m^]QȚ[$eI3I-ː FS%۾&yn.9r |f/"Hp %` u|>^ |umf߯@#AIP % @$)jy$G[ V_v""'⃉D4/)qsqs0DhŏCdPrIC[Y^_OzMS/iņ';vj&ykY:@ ' owO_^VH{e&ABc4mklN~O=&za̋ԵBf\4ޥ{mj/UDc'&k z_{bm^ f 0R}t|+;Ɗ2-Dܠ 㖶̴,gɴ `ʴ4=ȡE5;XC@ZZfҟZ[5N@cWG(ӵnĤ-z5IF2ƟŷsyJ/TW8P!iT].Z H.=Zr٥xUu P#6Qgw/K `5Ŏ ɺC.@".(ە\+T}u.`'bO nvwQd ӷV -n%^[[EP z ܐs='a0*LŦ*cH% $ _멞s8ɫJE@i+DfRc o4> hF DrAgA- šg$8 ǵU(t&/p1Sf3j&z0f6u Ն68rF@ĶC4 eyo:͉7JE7ZJߟ2M-,7RW_Ƴ.}NjcX=Ǝ$Kp7Ǝ+4v]d5vyBT[-yku;@āBc`Vu2"$#{y(G:(O/B ("]ISIWbվ81603L3i!j/%a$KB1f0'HYU:/Lf҆pXʭ2{{m6響|jBޚ>~"a}t9\0hoA27)}Veu5H&E8E@d L,ڂ2=!(G2}+}.0$2덛?x}?t|~]i9ՆSlK:ݸ8<q_7?YBTEK5+t B'8_[_}֮%/֢zR!p/5v~҄%l4Z}΢oqݖ>Dр4E P NDC5{]?bw YHj";VB*w $K{Y3}ŬsnAwt}Q}2+I&ɺ] WԖ[v,'!<(/>XQ `oDo{K&վ5^kc22jXĩ1UX?JVn(>aGM>h"DX0ņ1=+SEWrBlI[^CG#Aʱ<ر<}R;cehlއpUJ'V'(_=X50L&@0ׂ/Aa |nhg +z|hJj-02*t4 Pj)3'(齨`K}Z1_+FU_f9-O8sJ^1L8HJ~Y?| G=5,TK'U,EW`2ecE]ڬT<ɜ!P[9JQ,#;v8(ݝOPHM OHnfcqX{]}+Z]FXug9R D@-Ќ1 B^r ?;%Ȕ:(8>BfY@qx>L+",#$v$Yk (hTK,EJo{Stv n9S[Ѷcڈ*zT/<梖UQDTLkT/VetzcO@7RGBеؓ #1[$۰`bJz|@ TJ& fI LϟG|ĥ]ıPqb$wSHęcj1!M 5o$fM҇/M"h֠Z\R/v<9Qoh0 uoFOORD$L,kÑ lFFkGLa~6H&dPr}E7 qJ1Pk#9B|X%g;{`DY_B^| tYY:FO0JQ׹Vbݖf%2!M1I2V:Y@ P&Y Q,;(׿ӷ40)B鍢dYM,zluT,k-DX֤zܴT4j_kQ+@w %.za9ܷKvc1,*su1rb#B78&v=w\Z$,j'HxS&f<=Fs,}6s /n|EdǷ7REk]8[WXl7$"/4=h}_ꙮo.ݹzxM(pS†\rغ๊P7gAwrK,j3ur=d{cXKh=Cgo-toml-2.1.1/ci.sh000077500000000000000000000153371453566013500140440ustar00rootroot00000000000000#!/usr/bin/env bash stderr() { echo "$@" 1>&2 } usage() { b=$(basename "$0") echo $b: ERROR: "$@" 1>&2 cat 1>&2 < coverage.out go tool cover -func=coverage.out echo "Coverage profile for ${branch}: ${dir}/coverage.out" >&2 popd if [ "${branch}" != "HEAD" ]; then git worktree remove --force "$dir" fi } coverage() { case "$1" in -d) shift target="${1?Need to provide a target branch argument}" output_dir="$(mktemp -d)" target_out="${output_dir}/target.txt" head_out="${output_dir}/head.txt" cover "${target}" > "${target_out}" cover "HEAD" > "${head_out}" cat "${target_out}" cat "${head_out}" echo "" target_pct="$(tail -n2 ${target_out} | head -n1 | sed -E 's/.*total.*\t([0-9.]+)%.*/\1/')" head_pct="$(tail -n2 ${head_out} | head -n1 | sed -E 's/.*total.*\t([0-9.]+)%/\1/')" echo "Results: ${target} ${target_pct}% HEAD ${head_pct}%" delta_pct=$(echo "$head_pct - $target_pct" | bc -l) echo "Delta: ${delta_pct}" if [[ $delta_pct = \-* ]]; then echo "Regression!"; target_diff="${output_dir}/target.diff.txt" head_diff="${output_dir}/head.diff.txt" cat "${target_out}" | grep -E '^github.com/pelletier/go-toml' | tr -s "\t " | cut -f 2,3 | sort > "${target_diff}" cat "${head_out}" | grep -E '^github.com/pelletier/go-toml' | tr -s "\t " | cut -f 2,3 | sort > "${head_diff}" diff --side-by-side --suppress-common-lines "${target_diff}" "${head_diff}" return 1 fi return 0 ;; esac cover "${1-HEAD}" } bench() { branch="${1}" out="${2}" replace="${3}" dir="$(mktemp -d)" stderr "Executing benchmark for ${branch} at ${dir}" if [ "${branch}" = "HEAD" ]; then cp -r . "${dir}/" else git worktree add "$dir" "$branch" fi pushd "$dir" if [ "${replace}" != "" ]; then find ./benchmark/ -iname '*.go' -exec sed -i -E "s|github.com/pelletier/go-toml/v2|${replace}|g" {} \; go get "${replace}" fi export GOMAXPROCS=2 go test '-bench=^Benchmark(Un)?[mM]arshal' -count=10 -run=Nothing ./... | tee "${out}" popd if [ "${branch}" != "HEAD" ]; then git worktree remove --force "$dir" fi } fmktemp() { if mktemp --version &> /dev/null; then # GNU mktemp --suffix=-$1 else # BSD mktemp -t $1 fi } benchstathtml() { python3 - $1 <<'EOF' import sys lines = [] stop = False with open(sys.argv[1]) as f: for line in f.readlines(): line = line.strip() if line == "": stop = True if not stop: lines.append(line.split(',')) results = [] for line in reversed(lines[2:]): if len(line) < 8 or line[0] == "": continue v2 = float(line[1]) results.append([ line[0].replace("-32", ""), "%.1fx" % (float(line[3])/v2), # v1 "%.1fx" % (float(line[7])/v2), # bs ]) # move geomean to the end results.append(results[0]) del results[0] def printtable(data): print(""" """) for r in data: print(" ".format(*r)) print("""
Benchmarkgo-toml v1BurntSushi/toml
{}{}{}
""") def match(x): return "ReferenceFile" in x[0] or "HugoFrontMatter" in x[0] above = [x for x in results if match(x)] below = [x for x in results if not match(x)] printtable(above) print("

See more") print("""

The table above has the results of the most common use-cases. The table below contains the results of all benchmarks, including unrealistic ones. It is provided for completeness.

""") printtable(below) print('

This table can be generated with ./ci.sh benchmark -a -html.

') print("
") EOF } benchmark() { case "$1" in -d) shift target="${1?Need to provide a target branch argument}" old=`fmktemp ${target}` bench "${target}" "${old}" new=`fmktemp HEAD` bench HEAD "${new}" benchstat "${old}" "${new}" return 0 ;; -a) shift v2stats=`fmktemp go-toml-v2` bench HEAD "${v2stats}" "github.com/pelletier/go-toml/v2" v1stats=`fmktemp go-toml-v1` bench HEAD "${v1stats}" "github.com/pelletier/go-toml" bsstats=`fmktemp bs-toml` bench HEAD "${bsstats}" "github.com/BurntSushi/toml" cp "${v2stats}" go-toml-v2.txt cp "${v1stats}" go-toml-v1.txt cp "${bsstats}" bs-toml.txt if [ "$1" = "-html" ]; then tmpcsv=`fmktemp csv` benchstat -format csv go-toml-v2.txt go-toml-v1.txt bs-toml.txt > $tmpcsv benchstathtml $tmpcsv else benchstat go-toml-v2.txt go-toml-v1.txt bs-toml.txt fi rm -f go-toml-v2.txt go-toml-v1.txt bs-toml.txt return $? esac bench "${1-HEAD}" `mktemp` } case "$1" in coverage) shift; coverage $@;; benchmark) shift; benchmark $@;; *) usage "bad argument $1";; esac go-toml-2.1.1/cmd/000077500000000000000000000000001453566013500136445ustar00rootroot00000000000000go-toml-2.1.1/cmd/gotoml-test-decoder/000077500000000000000000000000001453566013500175255ustar00rootroot00000000000000go-toml-2.1.1/cmd/gotoml-test-decoder/main.go000066400000000000000000000006221453566013500210000ustar00rootroot00000000000000package main import ( "flag" "log" "os" "path" "github.com/pelletier/go-toml/v2/internal/testsuite" ) func main() { log.SetFlags(0) flag.Usage = usage flag.Parse() if flag.NArg() != 0 { flag.Usage() } err := testsuite.DecodeStdin() if err != nil { log.Fatal(err) } } func usage() { log.Printf("Usage: %s < toml-file\n", path.Base(os.Args[0])) flag.PrintDefaults() os.Exit(1) } go-toml-2.1.1/cmd/gotoml-test-encoder/000077500000000000000000000000001453566013500175375ustar00rootroot00000000000000go-toml-2.1.1/cmd/gotoml-test-encoder/gotoml-test-encoder.go000066400000000000000000000006221453566013500237610ustar00rootroot00000000000000package main import ( "flag" "log" "os" "path" "github.com/pelletier/go-toml/v2/internal/testsuite" ) func main() { log.SetFlags(0) flag.Usage = usage flag.Parse() if flag.NArg() != 0 { flag.Usage() } err := testsuite.EncodeStdin() if err != nil { log.Fatal(err) } } func usage() { log.Printf("Usage: %s < toml-file\n", path.Base(os.Args[0])) flag.PrintDefaults() os.Exit(1) } go-toml-2.1.1/cmd/jsontoml/000077500000000000000000000000001453566013500155115ustar00rootroot00000000000000go-toml-2.1.1/cmd/jsontoml/main.go000066400000000000000000000015751453566013500167740ustar00rootroot00000000000000// Package jsontoml is a program that converts JSON to TOML. // // # Usage // // Reading from stdin: // // cat file.json | jsontoml > file.toml // // Reading from a file: // // jsontoml file.json > file.toml // // # Installation // // Using Go: // // go install github.com/pelletier/go-toml/v2/cmd/jsontoml@latest package main import ( "encoding/json" "io" "github.com/pelletier/go-toml/v2" "github.com/pelletier/go-toml/v2/internal/cli" ) const usage = `jsontoml can be used in two ways: Reading from stdin: cat file.json | jsontoml > file.toml Reading from a file: jsontoml file.json > file.toml ` func main() { p := cli.Program{ Usage: usage, Fn: convert, } p.Execute() } func convert(r io.Reader, w io.Writer) error { var v interface{} d := json.NewDecoder(r) err := d.Decode(&v) if err != nil { return err } e := toml.NewEncoder(w) return e.Encode(v) } go-toml-2.1.1/cmd/jsontoml/main_test.go000066400000000000000000000012631453566013500200250ustar00rootroot00000000000000package main import ( "bytes" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestConvert(t *testing.T) { examples := []struct { name string input string expected string errors bool }{ { name: "valid json", input: ` { "mytoml": { "a": 42 } }`, expected: `[mytoml] a = 42.0 `, }, { name: "invalid json", input: `{ foo`, errors: true, }, } for _, e := range examples { b := new(bytes.Buffer) err := convert(strings.NewReader(e.input), b) if e.errors { require.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, e.expected, b.String()) } } } go-toml-2.1.1/cmd/tomljson/000077500000000000000000000000001453566013500155115ustar00rootroot00000000000000go-toml-2.1.1/cmd/tomljson/main.go000066400000000000000000000021321453566013500167620ustar00rootroot00000000000000// Package tomljson is a program that converts TOML to JSON. // // # Usage // // Reading from stdin: // // cat file.toml | tomljson > file.json // // Reading from a file: // // tomljson file.toml > file.json // // # Installation // // Using Go: // // go install github.com/pelletier/go-toml/v2/cmd/tomljson@latest package main import ( "encoding/json" "errors" "fmt" "io" "github.com/pelletier/go-toml/v2" "github.com/pelletier/go-toml/v2/internal/cli" ) const usage = `tomljson can be used in two ways: Reading from stdin: cat file.toml | tomljson > file.json Reading from a file: tomljson file.toml > file.json ` func main() { p := cli.Program{ Usage: usage, Fn: convert, } p.Execute() } func convert(r io.Reader, w io.Writer) error { var v interface{} d := toml.NewDecoder(r) err := d.Decode(&v) if err != nil { var derr *toml.DecodeError if errors.As(err, &derr) { row, col := derr.Position() return fmt.Errorf("%s\nerror occurred at row %d column %d", derr.String(), row, col) } return err } e := json.NewEncoder(w) e.SetIndent("", " ") return e.Encode(v) } go-toml-2.1.1/cmd/tomljson/main_test.go000066400000000000000000000016421453566013500200260ustar00rootroot00000000000000package main import ( "bytes" "fmt" "io" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestConvert(t *testing.T) { examples := []struct { name string input io.Reader expected string errors bool }{ { name: "valid toml", input: strings.NewReader(` [mytoml] a = 42`), expected: `{ "mytoml": { "a": 42 } } `, }, { name: "invalid toml", input: strings.NewReader(`bad = []]`), errors: true, }, { name: "bad reader", input: &badReader{}, errors: true, }, } for _, e := range examples { b := new(bytes.Buffer) err := convert(e.input, b) if e.errors { require.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, e.expected, b.String()) } } } type badReader struct{} func (r *badReader) Read([]byte) (int, error) { return 0, fmt.Errorf("reader failed on purpose") } go-toml-2.1.1/cmd/tomll/000077500000000000000000000000001453566013500147735ustar00rootroot00000000000000go-toml-2.1.1/cmd/tomll/main.go000066400000000000000000000020001453566013500162360ustar00rootroot00000000000000// Package tomll is a linter program for TOML. // // # Usage // // Reading from stdin, writing to stdout: // // cat file.toml | tomll // // Reading and updating a list of files in place: // // tomll a.toml b.toml c.toml // // # Installation // // Using Go: // // go install github.com/pelletier/go-toml/v2/cmd/tomll@latest package main import ( "io" "github.com/pelletier/go-toml/v2" "github.com/pelletier/go-toml/v2/internal/cli" ) const usage = `tomll can be used in two ways: Reading from stdin, writing to stdout: cat file.toml | tomll > file.toml Reading and updating a list of files in place: tomll a.toml b.toml c.toml When given a list of files, tomll will modify all files in place without asking. ` func main() { p := cli.Program{ Usage: usage, Fn: convert, Inplace: true, } p.Execute() } func convert(r io.Reader, w io.Writer) error { var v interface{} d := toml.NewDecoder(r) err := d.Decode(&v) if err != nil { return err } e := toml.NewEncoder(w) return e.Encode(v) } go-toml-2.1.1/cmd/tomll/main_test.go000066400000000000000000000012421453566013500173040ustar00rootroot00000000000000package main import ( "bytes" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestConvert(t *testing.T) { examples := []struct { name string input string expected string errors bool }{ { name: "valid toml", input: ` mytoml.a = 42.0 `, expected: `[mytoml] a = 42.0 `, }, { name: "invalid toml", input: `[what`, errors: true, }, } for _, e := range examples { b := new(bytes.Buffer) err := convert(strings.NewReader(e.input), b) if e.errors { require.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, e.expected, b.String()) } } } go-toml-2.1.1/cmd/tomltestgen/000077500000000000000000000000001453566013500162115ustar00rootroot00000000000000go-toml-2.1.1/cmd/tomltestgen/main.go000066400000000000000000000073551453566013500174760ustar00rootroot00000000000000// tomltestgen retrieves a given version of the language-agnostic TOML test suite in // https://github.com/BurntSushi/toml-test and generates go-toml unit tests. // // Within the go-toml package, run `go generate`. Otherwise, use: // // go run github.com/pelletier/go-toml/cmd/tomltestgen -o toml_testgen_test.go package main import ( "bytes" "flag" "fmt" "go/format" "log" "os" "path/filepath" "strconv" "strings" "text/template" "time" ) type invalid struct { Name string Input string } type valid struct { Name string Input string JsonRef string } type testsCollection struct { Ref string Timestamp string Invalid []invalid Valid []valid Count int } const srcTemplate = "// Generated by tomltestgen for toml-test ref {{.Ref}} on {{.Timestamp}}\n" + "package toml_test\n" + " import (\n" + " \"testing\"\n" + ")\n" + "{{range .Invalid}}\n" + "func TestTOMLTest_Invalid_{{.Name}}(t *testing.T) {\n" + " input := {{.Input|gostr}}\n" + " testgenInvalid(t, input)\n" + "}\n" + "{{end}}\n" + "\n" + "{{range .Valid}}\n" + "func TestTOMLTest_Valid_{{.Name}}(t *testing.T) {\n" + " input := {{.Input|gostr}}\n" + " jsonRef := {{.JsonRef|gostr}}\n" + " testgenValid(t, input, jsonRef)\n" + "}\n" + "{{end}}\n" func kebabToCamel(kebab string) string { camel := "" nextUpper := true for _, c := range kebab { if nextUpper { camel += strings.ToUpper(string(c)) nextUpper = false } else if c == '-' { nextUpper = true } else if c == '/' { nextUpper = true camel += "_" } else { camel += string(c) } } return camel } func templateGoStr(input string) string { return strconv.Quote(input) } var ( ref = flag.String("r", "master", "git reference") out = flag.String("o", "", "output file") ) func usage() { _, _ = fmt.Fprintf(os.Stderr, "usage: tomltestgen [flags]\n") flag.PrintDefaults() } func main() { flag.Usage = usage flag.Parse() collection := testsCollection{ Ref: *ref, Timestamp: time.Now().Format(time.RFC3339), } dirContent, _ := filepath.Glob("tests/invalid/**/*.toml") for _, f := range dirContent { filename := strings.TrimPrefix(f, "tests/valid/") name := kebabToCamel(strings.TrimSuffix(filename, ".toml")) log.Printf("> [%s] %s\n", "invalid", name) tomlContent, err := os.ReadFile(f) if err != nil { fmt.Printf("failed to read test file: %s\n", err) os.Exit(1) } collection.Invalid = append(collection.Invalid, invalid{ Name: name, Input: string(tomlContent), }) collection.Count++ } dirContent, _ = filepath.Glob("tests/valid/**/*.toml") for _, f := range dirContent { filename := strings.TrimPrefix(f, "tests/valid/") name := kebabToCamel(strings.TrimSuffix(filename, ".toml")) log.Printf("> [%s] %s\n", "valid", name) tomlContent, err := os.ReadFile(f) if err != nil { fmt.Printf("failed reading test file: %s\n", err) os.Exit(1) } filename = strings.TrimSuffix(f, ".toml") jsonContent, err := os.ReadFile(filename + ".json") if err != nil { fmt.Printf("failed reading validation json: %s\n", err) os.Exit(1) } collection.Valid = append(collection.Valid, valid{ Name: name, Input: string(tomlContent), JsonRef: string(jsonContent), }) collection.Count++ } log.Printf("Collected %d tests from toml-test\n", collection.Count) funcMap := template.FuncMap{ "gostr": templateGoStr, } t := template.Must(template.New("src").Funcs(funcMap).Parse(srcTemplate)) buf := new(bytes.Buffer) err := t.Execute(buf, collection) if err != nil { panic(err) } outputBytes, err := format.Source(buf.Bytes()) if err != nil { panic(err) } if *out == "" { fmt.Println(string(outputBytes)) return } err = os.WriteFile(*out, outputBytes, 0o644) if err != nil { panic(err) } } go-toml-2.1.1/decode.go000066400000000000000000000310471453566013500146600ustar00rootroot00000000000000package toml import ( "fmt" "math" "strconv" "time" "github.com/pelletier/go-toml/v2/unstable" ) func parseInteger(b []byte) (int64, error) { if len(b) > 2 && b[0] == '0' { switch b[1] { case 'x': return parseIntHex(b) case 'b': return parseIntBin(b) case 'o': return parseIntOct(b) default: panic(fmt.Errorf("invalid base '%c', should have been checked by scanIntOrFloat", b[1])) } } return parseIntDec(b) } func parseLocalDate(b []byte) (LocalDate, error) { // full-date = date-fullyear "-" date-month "-" date-mday // date-fullyear = 4DIGIT // date-month = 2DIGIT ; 01-12 // date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year var date LocalDate if len(b) != 10 || b[4] != '-' || b[7] != '-' { return date, unstable.NewParserError(b, "dates are expected to have the format YYYY-MM-DD") } var err error date.Year, err = parseDecimalDigits(b[0:4]) if err != nil { return LocalDate{}, err } date.Month, err = parseDecimalDigits(b[5:7]) if err != nil { return LocalDate{}, err } date.Day, err = parseDecimalDigits(b[8:10]) if err != nil { return LocalDate{}, err } if !isValidDate(date.Year, date.Month, date.Day) { return LocalDate{}, unstable.NewParserError(b, "impossible date") } return date, nil } func parseDecimalDigits(b []byte) (int, error) { v := 0 for i, c := range b { if c < '0' || c > '9' { return 0, unstable.NewParserError(b[i:i+1], "expected digit (0-9)") } v *= 10 v += int(c - '0') } return v, nil } func parseDateTime(b []byte) (time.Time, error) { // offset-date-time = full-date time-delim full-time // full-time = partial-time time-offset // time-offset = "Z" / time-numoffset // time-numoffset = ( "+" / "-" ) time-hour ":" time-minute dt, b, err := parseLocalDateTime(b) if err != nil { return time.Time{}, err } var zone *time.Location if len(b) == 0 { // parser should have checked that when assigning the date time node panic("date time should have a timezone") } if b[0] == 'Z' || b[0] == 'z' { b = b[1:] zone = time.UTC } else { const dateTimeByteLen = 6 if len(b) != dateTimeByteLen { return time.Time{}, unstable.NewParserError(b, "invalid date-time timezone") } var direction int switch b[0] { case '-': direction = -1 case '+': direction = +1 default: return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset character") } if b[3] != ':' { return time.Time{}, unstable.NewParserError(b[3:4], "expected a : separator") } hours, err := parseDecimalDigits(b[1:3]) if err != nil { return time.Time{}, err } if hours > 23 { return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset hours") } minutes, err := parseDecimalDigits(b[4:6]) if err != nil { return time.Time{}, err } if minutes > 59 { return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset minutes") } seconds := direction * (hours*3600 + minutes*60) if seconds == 0 { zone = time.UTC } else { zone = time.FixedZone("", seconds) } b = b[dateTimeByteLen:] } if len(b) > 0 { return time.Time{}, unstable.NewParserError(b, "extra bytes at the end of the timezone") } t := time.Date( dt.Year, time.Month(dt.Month), dt.Day, dt.Hour, dt.Minute, dt.Second, dt.Nanosecond, zone) return t, nil } func parseLocalDateTime(b []byte) (LocalDateTime, []byte, error) { var dt LocalDateTime const localDateTimeByteMinLen = 11 if len(b) < localDateTimeByteMinLen { return dt, nil, unstable.NewParserError(b, "local datetimes are expected to have the format YYYY-MM-DDTHH:MM:SS[.NNNNNNNNN]") } date, err := parseLocalDate(b[:10]) if err != nil { return dt, nil, err } dt.LocalDate = date sep := b[10] if sep != 'T' && sep != ' ' && sep != 't' { return dt, nil, unstable.NewParserError(b[10:11], "datetime separator is expected to be T or a space") } t, rest, err := parseLocalTime(b[11:]) if err != nil { return dt, nil, err } dt.LocalTime = t return dt, rest, nil } // parseLocalTime is a bit different because it also returns the remaining // []byte that is didn't need. This is to allow parseDateTime to parse those // remaining bytes as a timezone. func parseLocalTime(b []byte) (LocalTime, []byte, error) { var ( nspow = [10]int{0, 1e8, 1e7, 1e6, 1e5, 1e4, 1e3, 1e2, 1e1, 1e0} t LocalTime ) // check if b matches to have expected format HH:MM:SS[.NNNNNN] const localTimeByteLen = 8 if len(b) < localTimeByteLen { return t, nil, unstable.NewParserError(b, "times are expected to have the format HH:MM:SS[.NNNNNN]") } var err error t.Hour, err = parseDecimalDigits(b[0:2]) if err != nil { return t, nil, err } if t.Hour > 23 { return t, nil, unstable.NewParserError(b[0:2], "hour cannot be greater 23") } if b[2] != ':' { return t, nil, unstable.NewParserError(b[2:3], "expecting colon between hours and minutes") } t.Minute, err = parseDecimalDigits(b[3:5]) if err != nil { return t, nil, err } if t.Minute > 59 { return t, nil, unstable.NewParserError(b[3:5], "minutes cannot be greater 59") } if b[5] != ':' { return t, nil, unstable.NewParserError(b[5:6], "expecting colon between minutes and seconds") } t.Second, err = parseDecimalDigits(b[6:8]) if err != nil { return t, nil, err } if t.Second > 60 { return t, nil, unstable.NewParserError(b[6:8], "seconds cannot be greater 60") } b = b[8:] if len(b) >= 1 && b[0] == '.' { frac := 0 precision := 0 digits := 0 for i, c := range b[1:] { if !isDigit(c) { if i == 0 { return t, nil, unstable.NewParserError(b[0:1], "need at least one digit after fraction point") } break } digits++ const maxFracPrecision = 9 if i >= maxFracPrecision { // go-toml allows decoding fractional seconds // beyond the supported precision of 9 // digits. It truncates the fractional component // to the supported precision and ignores the // remaining digits. // // https://github.com/pelletier/go-toml/discussions/707 continue } frac *= 10 frac += int(c - '0') precision++ } if precision == 0 { return t, nil, unstable.NewParserError(b[:1], "nanoseconds need at least one digit") } t.Nanosecond = frac * nspow[precision] t.Precision = precision return t, b[1+digits:], nil } return t, b, nil } //nolint:cyclop func parseFloat(b []byte) (float64, error) { if len(b) == 4 && (b[0] == '+' || b[0] == '-') && b[1] == 'n' && b[2] == 'a' && b[3] == 'n' { return math.NaN(), nil } cleaned, err := checkAndRemoveUnderscoresFloats(b) if err != nil { return 0, err } if cleaned[0] == '.' { return 0, unstable.NewParserError(b, "float cannot start with a dot") } if cleaned[len(cleaned)-1] == '.' { return 0, unstable.NewParserError(b, "float cannot end with a dot") } dotAlreadySeen := false for i, c := range cleaned { if c == '.' { if dotAlreadySeen { return 0, unstable.NewParserError(b[i:i+1], "float can have at most one decimal point") } if !isDigit(cleaned[i-1]) { return 0, unstable.NewParserError(b[i-1:i+1], "float decimal point must be preceded by a digit") } if !isDigit(cleaned[i+1]) { return 0, unstable.NewParserError(b[i:i+2], "float decimal point must be followed by a digit") } dotAlreadySeen = true } } start := 0 if cleaned[0] == '+' || cleaned[0] == '-' { start = 1 } if cleaned[start] == '0' && len(cleaned) > start+1 && isDigit(cleaned[start+1]) { return 0, unstable.NewParserError(b, "float integer part cannot have leading zeroes") } f, err := strconv.ParseFloat(string(cleaned), 64) if err != nil { return 0, unstable.NewParserError(b, "unable to parse float: %w", err) } return f, nil } func parseIntHex(b []byte) (int64, error) { cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:]) if err != nil { return 0, err } i, err := strconv.ParseInt(string(cleaned), 16, 64) if err != nil { return 0, unstable.NewParserError(b, "couldn't parse hexadecimal number: %w", err) } return i, nil } func parseIntOct(b []byte) (int64, error) { cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:]) if err != nil { return 0, err } i, err := strconv.ParseInt(string(cleaned), 8, 64) if err != nil { return 0, unstable.NewParserError(b, "couldn't parse octal number: %w", err) } return i, nil } func parseIntBin(b []byte) (int64, error) { cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:]) if err != nil { return 0, err } i, err := strconv.ParseInt(string(cleaned), 2, 64) if err != nil { return 0, unstable.NewParserError(b, "couldn't parse binary number: %w", err) } return i, nil } func isSign(b byte) bool { return b == '+' || b == '-' } func parseIntDec(b []byte) (int64, error) { cleaned, err := checkAndRemoveUnderscoresIntegers(b) if err != nil { return 0, err } startIdx := 0 if isSign(cleaned[0]) { startIdx++ } if len(cleaned) > startIdx+1 && cleaned[startIdx] == '0' { return 0, unstable.NewParserError(b, "leading zero not allowed on decimal number") } i, err := strconv.ParseInt(string(cleaned), 10, 64) if err != nil { return 0, unstable.NewParserError(b, "couldn't parse decimal number: %w", err) } return i, nil } func checkAndRemoveUnderscoresIntegers(b []byte) ([]byte, error) { start := 0 if b[start] == '+' || b[start] == '-' { start++ } if len(b) == start { return b, nil } if b[start] == '_' { return nil, unstable.NewParserError(b[start:start+1], "number cannot start with underscore") } if b[len(b)-1] == '_' { return nil, unstable.NewParserError(b[len(b)-1:], "number cannot end with underscore") } // fast path i := 0 for ; i < len(b); i++ { if b[i] == '_' { break } } if i == len(b) { return b, nil } before := false cleaned := make([]byte, i, len(b)) copy(cleaned, b) for i++; i < len(b); i++ { c := b[i] if c == '_' { if !before { return nil, unstable.NewParserError(b[i-1:i+1], "number must have at least one digit between underscores") } before = false } else { before = true cleaned = append(cleaned, c) } } return cleaned, nil } func checkAndRemoveUnderscoresFloats(b []byte) ([]byte, error) { if b[0] == '_' { return nil, unstable.NewParserError(b[0:1], "number cannot start with underscore") } if b[len(b)-1] == '_' { return nil, unstable.NewParserError(b[len(b)-1:], "number cannot end with underscore") } // fast path i := 0 for ; i < len(b); i++ { if b[i] == '_' { break } } if i == len(b) { return b, nil } before := false cleaned := make([]byte, 0, len(b)) for i := 0; i < len(b); i++ { c := b[i] switch c { case '_': if !before { return nil, unstable.NewParserError(b[i-1:i+1], "number must have at least one digit between underscores") } if i < len(b)-1 && (b[i+1] == 'e' || b[i+1] == 'E') { return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore before exponent") } before = false case '+', '-': // signed exponents cleaned = append(cleaned, c) before = false case 'e', 'E': if i < len(b)-1 && b[i+1] == '_' { return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore after exponent") } cleaned = append(cleaned, c) case '.': if i < len(b)-1 && b[i+1] == '_' { return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore after decimal point") } if i > 0 && b[i-1] == '_' { return nil, unstable.NewParserError(b[i-1:i], "cannot have underscore before decimal point") } cleaned = append(cleaned, c) default: before = true cleaned = append(cleaned, c) } } return cleaned, nil } // isValidDate checks if a provided date is a date that exists. func isValidDate(year int, month int, day int) bool { return month > 0 && month < 13 && day > 0 && day <= daysIn(month, year) } // daysBefore[m] counts the number of days in a non-leap year // before month m begins. There is an entry for m=12, counting // the number of days before January of next year (365). var daysBefore = [...]int32{ 0, 31, 31 + 28, 31 + 28 + 31, 31 + 28 + 31 + 30, 31 + 28 + 31 + 30 + 31, 31 + 28 + 31 + 30 + 31 + 30, 31 + 28 + 31 + 30 + 31 + 30 + 31, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31, } func daysIn(m int, year int) int { if m == 2 && isLeap(year) { return 29 } return int(daysBefore[m] - daysBefore[m-1]) } func isLeap(year int) bool { return year%4 == 0 && (year%100 != 0 || year%400 == 0) } func isDigit(r byte) bool { return r >= '0' && r <= '9' } go-toml-2.1.1/doc.go000066400000000000000000000001141453566013500141710ustar00rootroot00000000000000// Package toml is a library to read and write TOML documents. package toml go-toml-2.1.1/errors.go000066400000000000000000000135501453566013500147500ustar00rootroot00000000000000package toml import ( "fmt" "strconv" "strings" "github.com/pelletier/go-toml/v2/internal/danger" "github.com/pelletier/go-toml/v2/unstable" ) // DecodeError represents an error encountered during the parsing or decoding // of a TOML document. // // In addition to the error message, it contains the position in the document // where it happened, as well as a human-readable representation that shows // where the error occurred in the document. type DecodeError struct { message string line int column int key Key human string } // StrictMissingError occurs in a TOML document that does not have a // corresponding field in the target value. It contains all the missing fields // in Errors. // // Emitted by Decoder when DisallowUnknownFields() was called. type StrictMissingError struct { // One error per field that could not be found. Errors []DecodeError } // Error returns the canonical string for this error. func (s *StrictMissingError) Error() string { return "strict mode: fields in the document are missing in the target struct" } // String returns a human readable description of all errors. func (s *StrictMissingError) String() string { var buf strings.Builder for i, e := range s.Errors { if i > 0 { buf.WriteString("\n---\n") } buf.WriteString(e.String()) } return buf.String() } type Key []string // Error returns the error message contained in the DecodeError. func (e *DecodeError) Error() string { return "toml: " + e.message } // String returns the human-readable contextualized error. This string is multi-line. func (e *DecodeError) String() string { return e.human } // Position returns the (line, column) pair indicating where the error // occurred in the document. Positions are 1-indexed. func (e *DecodeError) Position() (row int, column int) { return e.line, e.column } // Key that was being processed when the error occurred. The key is present only // if this DecodeError is part of a StrictMissingError. func (e *DecodeError) Key() Key { return e.key } // decodeErrorFromHighlight creates a DecodeError referencing a highlighted // range of bytes from document. // // highlight needs to be a sub-slice of document, or this function panics. // // The function copies all bytes used in DecodeError, so that document and // highlight can be freely deallocated. // //nolint:funlen func wrapDecodeError(document []byte, de *unstable.ParserError) *DecodeError { offset := danger.SubsliceOffset(document, de.Highlight) errMessage := de.Error() errLine, errColumn := positionAtEnd(document[:offset]) before, after := linesOfContext(document, de.Highlight, offset, 3) var buf strings.Builder maxLine := errLine + len(after) - 1 lineColumnWidth := len(strconv.Itoa(maxLine)) // Write the lines of context strictly before the error. for i := len(before) - 1; i > 0; i-- { line := errLine - i buf.WriteString(formatLineNumber(line, lineColumnWidth)) buf.WriteString("|") if len(before[i]) > 0 { buf.WriteString(" ") buf.Write(before[i]) } buf.WriteRune('\n') } // Write the document line that contains the error. buf.WriteString(formatLineNumber(errLine, lineColumnWidth)) buf.WriteString("| ") if len(before) > 0 { buf.Write(before[0]) } buf.Write(de.Highlight) if len(after) > 0 { buf.Write(after[0]) } buf.WriteRune('\n') // Write the line with the error message itself (so it does not have a line // number). buf.WriteString(strings.Repeat(" ", lineColumnWidth)) buf.WriteString("| ") if len(before) > 0 { buf.WriteString(strings.Repeat(" ", len(before[0]))) } buf.WriteString(strings.Repeat("~", len(de.Highlight))) if len(errMessage) > 0 { buf.WriteString(" ") buf.WriteString(errMessage) } // Write the lines of context strictly after the error. for i := 1; i < len(after); i++ { buf.WriteRune('\n') line := errLine + i buf.WriteString(formatLineNumber(line, lineColumnWidth)) buf.WriteString("|") if len(after[i]) > 0 { buf.WriteString(" ") buf.Write(after[i]) } } return &DecodeError{ message: errMessage, line: errLine, column: errColumn, key: de.Key, human: buf.String(), } } func formatLineNumber(line int, width int) string { format := "%" + strconv.Itoa(width) + "d" return fmt.Sprintf(format, line) } func linesOfContext(document []byte, highlight []byte, offset int, linesAround int) ([][]byte, [][]byte) { return beforeLines(document, offset, linesAround), afterLines(document, highlight, offset, linesAround) } func beforeLines(document []byte, offset int, linesAround int) [][]byte { var beforeLines [][]byte // Walk the document backward from the highlight to find previous lines // of context. rest := document[:offset] backward: for o := len(rest) - 1; o >= 0 && len(beforeLines) <= linesAround && len(rest) > 0; { switch { case rest[o] == '\n': // handle individual lines beforeLines = append(beforeLines, rest[o+1:]) rest = rest[:o] o = len(rest) - 1 case o == 0: // add the first line only if it's non-empty beforeLines = append(beforeLines, rest) break backward default: o-- } } return beforeLines } func afterLines(document []byte, highlight []byte, offset int, linesAround int) [][]byte { var afterLines [][]byte // Walk the document forward from the highlight to find the following // lines of context. rest := document[offset+len(highlight):] forward: for o := 0; o < len(rest) && len(afterLines) <= linesAround; { switch { case rest[o] == '\n': // handle individual lines afterLines = append(afterLines, rest[:o]) rest = rest[o+1:] o = 0 case o == len(rest)-1: // add last line only if it's non-empty afterLines = append(afterLines, rest) break forward default: o++ } } return afterLines } func positionAtEnd(b []byte) (row int, column int) { row = 1 column = 1 for _, c := range b { if c == '\n' { row++ column = 1 } else { column++ } } return } go-toml-2.1.1/errors_test.go000066400000000000000000000101031453566013500157760ustar00rootroot00000000000000package toml import ( "bytes" "errors" "fmt" "strings" "testing" "github.com/pelletier/go-toml/v2/unstable" "github.com/stretchr/testify/assert" ) //nolint:funlen func TestDecodeError(t *testing.T) { examples := []struct { desc string doc [3]string msg string expected string }{ { desc: "no context", doc: [3]string{"", "morning", ""}, msg: "this is wrong", expected: ` 1| morning | ~~~~~~~ this is wrong`, }, { desc: "one line", doc: [3]string{"good ", "morning", " everyone"}, msg: "this is wrong", expected: ` 1| good morning everyone | ~~~~~~~ this is wrong`, }, { desc: "exactly 3 lines", doc: [3]string{`line1 line2 line3 before `, "highlighted", ` after post line 1 post line 2 post line 3`}, msg: "this is wrong", expected: ` 1| line1 2| line2 3| line3 4| before highlighted after | ~~~~~~~~~~~ this is wrong 5| post line 1 6| post line 2 7| post line 3`, }, { desc: "more than 3 lines", doc: [3]string{`should not be seen1 should not be seen2 line1 line2 line3 before `, "highlighted", ` after post line 1 post line 2 post line 3 should not be seen3 should not be seen4`}, msg: "this is wrong", expected: ` 3| line1 4| line2 5| line3 6| before highlighted after | ~~~~~~~~~~~ this is wrong 7| post line 1 8| post line 2 9| post line 3`, }, { desc: "more than 10 total lines", doc: [3]string{`should not be seen 0 should not be seen1 should not be seen2 should not be seen3 line1 line2 line3 before `, "highlighted", ` after post line 1 post line 2 post line 3 should not be seen3 should not be seen4`}, msg: "this is wrong", expected: ` 5| line1 6| line2 7| line3 8| before highlighted after | ~~~~~~~~~~~ this is wrong 9| post line 1 10| post line 2 11| post line 3`, }, { desc: "last line of more than 10", doc: [3]string{`should not be seen should not be seen should not be seen should not be seen should not be seen should not be seen should not be seen line1 line2 line3 before `, "highlighted", ``}, msg: "this is wrong", expected: ` 8| line1 9| line2 10| line3 11| before highlighted | ~~~~~~~~~~~ this is wrong `, }, { desc: "handle empty lines in the before/after blocks", doc: [3]string{ `line1 line 2 before `, "highlighted", ` after line 3 line 4 line 5`, }, expected: `1| line1 2| 3| line 2 4| before highlighted after | ~~~~~~~~~~~ 5| line 3 6| 7| line 4`, }, { desc: "handle remainder of the error line when there is only one line", doc: [3]string{`P=`, `[`, `#`}, msg: "array is incomplete", expected: `1| P=[# | ~ array is incomplete`, }, } for _, e := range examples { e := e t.Run(e.desc, func(t *testing.T) { b := bytes.Buffer{} b.Write([]byte(e.doc[0])) start := b.Len() b.Write([]byte(e.doc[1])) end := b.Len() b.Write([]byte(e.doc[2])) doc := b.Bytes() hl := doc[start:end] err := wrapDecodeError(doc, &unstable.ParserError{ Highlight: hl, Message: e.msg, }) var derr *DecodeError if !errors.As(err, &derr) { t.Errorf("error not in expected format") return } assert.Equal(t, strings.Trim(e.expected, "\n"), derr.String()) }) } } func TestDecodeError_Accessors(t *testing.T) { e := DecodeError{ message: "foo", line: 1, column: 2, key: []string{"one", "two"}, human: "bar", } assert.Equal(t, "toml: foo", e.Error()) r, c := e.Position() assert.Equal(t, 1, r) assert.Equal(t, 2, c) assert.Equal(t, Key{"one", "two"}, e.Key()) assert.Equal(t, "bar", e.String()) } func ExampleDecodeError() { doc := `name = 123__456` s := map[string]interface{}{} err := Unmarshal([]byte(doc), &s) fmt.Println(err) var derr *DecodeError if errors.As(err, &derr) { fmt.Println(derr.String()) row, col := derr.Position() fmt.Println("error occurred at row", row, "column", col) } // Output: // toml: number must have at least one digit between underscores // 1| name = 123__456 // | ~~ number must have at least one digit between underscores // error occurred at row 1 column 11 } go-toml-2.1.1/example_text_marshaling_test.go000066400000000000000000000010061453566013500213700ustar00rootroot00000000000000package toml_test import ( "fmt" "log" "strconv" "github.com/pelletier/go-toml/v2" ) type customInt int func (i *customInt) UnmarshalText(b []byte) error { x, err := strconv.ParseInt(string(b), 10, 32) if err != nil { return err } *i = customInt(x * 100) return nil } type doc struct { Value customInt } func ExampleUnmarshal_textUnmarshal() { var x doc data := []byte(`value = "42"`) err := toml.Unmarshal(data, &x) if err != nil { log.Fatal(err) } fmt.Println(x) // Output: // {4200} } go-toml-2.1.1/fast_test.go000066400000000000000000000043301453566013500154240ustar00rootroot00000000000000package toml_test import ( "testing" "github.com/pelletier/go-toml/v2" "github.com/stretchr/testify/require" ) func TestFastSimpleInt(t *testing.T) { m := map[string]int64{} err := toml.Unmarshal([]byte(`a = 42`), &m) require.NoError(t, err) require.Equal(t, map[string]int64{"a": 42}, m) } func TestFastSimpleFloat(t *testing.T) { m := map[string]float64{} err := toml.Unmarshal([]byte("a = 42\nb = 1.1\nc = 12341234123412341234123412341234"), &m) require.NoError(t, err) require.Equal(t, map[string]float64{"a": 42, "b": 1.1, "c": 1.2341234123412342e+31}, m) } func TestFastSimpleString(t *testing.T) { m := map[string]string{} err := toml.Unmarshal([]byte(`a = "hello"`), &m) require.NoError(t, err) require.Equal(t, map[string]string{"a": "hello"}, m) } func TestFastSimpleInterface(t *testing.T) { m := map[string]interface{}{} err := toml.Unmarshal([]byte(` a = "hello" b = 42`), &m) require.NoError(t, err) require.Equal(t, map[string]interface{}{ "a": "hello", "b": int64(42), }, m) } func TestFastMultipartKeyInterface(t *testing.T) { m := map[string]interface{}{} err := toml.Unmarshal([]byte(` a.interim = "test" a.b.c = "hello" b = 42`), &m) require.NoError(t, err) require.Equal(t, map[string]interface{}{ "a": map[string]interface{}{ "interim": "test", "b": map[string]interface{}{ "c": "hello", }, }, "b": int64(42), }, m) } func TestFastExistingMap(t *testing.T) { m := map[string]interface{}{ "ints": map[string]int{}, } err := toml.Unmarshal([]byte(` ints.one = 1 ints.two = 2 strings.yo = "hello"`), &m) require.NoError(t, err) require.Equal(t, map[string]interface{}{ "ints": map[string]interface{}{ "one": int64(1), "two": int64(2), }, "strings": map[string]interface{}{ "yo": "hello", }, }, m) } func TestFastArrayTable(t *testing.T) { b := []byte(` [root] [[root.nested]] name = 'Bob' [[root.nested]] name = 'Alice' `) m := map[string]interface{}{} err := toml.Unmarshal(b, &m) require.NoError(t, err) require.Equal(t, map[string]interface{}{ "root": map[string]interface{}{ "nested": []interface{}{ map[string]interface{}{ "name": "Bob", }, map[string]interface{}{ "name": "Alice", }, }, }, }, m) } go-toml-2.1.1/fuzz_test.go000066400000000000000000000022131453566013500154630ustar00rootroot00000000000000//go:build go1.18 || go1.19 || go1.20 || go1.21 // +build go1.18 go1.19 go1.20 go1.21 package toml_test import ( "io/ioutil" "strings" "testing" "github.com/pelletier/go-toml/v2" "github.com/stretchr/testify/require" ) func FuzzUnmarshal(f *testing.F) { file, err := ioutil.ReadFile("benchmark/benchmark.toml") if err != nil { panic(err) } f.Add(file) f.Fuzz(func(t *testing.T, b []byte) { if strings.Contains(string(b), "nan") { // Current limitation of testify. // https://github.com/stretchr/testify/issues/624 t.Skip("can't compare NaNs") } t.Log("INITIAL DOCUMENT ===========================") t.Log(string(b)) var v interface{} err := toml.Unmarshal(b, &v) if err != nil { return } t.Log("DECODED VALUE ===========================") t.Logf("%#+v", v) encoded, err := toml.Marshal(v) if err != nil { t.Fatalf("cannot marshal unmarshaled document: %s", err) } t.Log("ENCODED DOCUMENT ===========================") t.Log(string(encoded)) var v2 interface{} err = toml.Unmarshal(encoded, &v2) if err != nil { t.Fatalf("failed round trip: %s", err) } require.Equal(t, v, v2) }) } go-toml-2.1.1/go.mod000066400000000000000000000001341453566013500142050ustar00rootroot00000000000000module github.com/pelletier/go-toml/v2 go 1.16 require github.com/stretchr/testify v1.8.4 go-toml-2.1.1/go.sum000066400000000000000000000027561453566013500142460ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= go-toml-2.1.1/internal/000077500000000000000000000000001453566013500147155ustar00rootroot00000000000000go-toml-2.1.1/internal/characters/000077500000000000000000000000001453566013500170345ustar00rootroot00000000000000go-toml-2.1.1/internal/characters/ascii.go000066400000000000000000000011201453566013500204450ustar00rootroot00000000000000package characters var invalidAsciiTable = [256]bool{ 0x00: true, 0x01: true, 0x02: true, 0x03: true, 0x04: true, 0x05: true, 0x06: true, 0x07: true, 0x08: true, // 0x09 TAB // 0x0A LF 0x0B: true, 0x0C: true, // 0x0D CR 0x0E: true, 0x0F: true, 0x10: true, 0x11: true, 0x12: true, 0x13: true, 0x14: true, 0x15: true, 0x16: true, 0x17: true, 0x18: true, 0x19: true, 0x1A: true, 0x1B: true, 0x1C: true, 0x1D: true, 0x1E: true, 0x1F: true, // 0x20 - 0x7E Printable ASCII characters 0x7F: true, } func InvalidAscii(b byte) bool { return invalidAsciiTable[b] } go-toml-2.1.1/internal/characters/utf8.go000066400000000000000000000127401453566013500202550ustar00rootroot00000000000000package characters import ( "unicode/utf8" ) type utf8Err struct { Index int Size int } func (u utf8Err) Zero() bool { return u.Size == 0 } // Verified that a given string is only made of valid UTF-8 characters allowed // by the TOML spec: // // Any Unicode character may be used except those that must be escaped: // quotation mark, backslash, and the control characters other than tab (U+0000 // to U+0008, U+000A to U+001F, U+007F). // // It is a copy of the Go 1.17 utf8.Valid implementation, tweaked to exit early // when a character is not allowed. // // The returned utf8Err is Zero() if the string is valid, or contains the byte // index and size of the invalid character. // // quotation mark => already checked // backslash => already checked // 0-0x8 => invalid // 0x9 => tab, ok // 0xA - 0x1F => invalid // 0x7F => invalid func Utf8TomlValidAlreadyEscaped(p []byte) (err utf8Err) { // Fast path. Check for and skip 8 bytes of ASCII characters per iteration. offset := 0 for len(p) >= 8 { // Combining two 32 bit loads allows the same code to be used // for 32 and 64 bit platforms. // The compiler can generate a 32bit load for first32 and second32 // on many platforms. See test/codegen/memcombine.go. first32 := uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24 second32 := uint32(p[4]) | uint32(p[5])<<8 | uint32(p[6])<<16 | uint32(p[7])<<24 if (first32|second32)&0x80808080 != 0 { // Found a non ASCII byte (>= RuneSelf). break } for i, b := range p[:8] { if InvalidAscii(b) { err.Index = offset + i err.Size = 1 return } } p = p[8:] offset += 8 } n := len(p) for i := 0; i < n; { pi := p[i] if pi < utf8.RuneSelf { if InvalidAscii(pi) { err.Index = offset + i err.Size = 1 return } i++ continue } x := first[pi] if x == xx { // Illegal starter byte. err.Index = offset + i err.Size = 1 return } size := int(x & 7) if i+size > n { // Short or invalid. err.Index = offset + i err.Size = n - i return } accept := acceptRanges[x>>4] if c := p[i+1]; c < accept.lo || accept.hi < c { err.Index = offset + i err.Size = 2 return } else if size == 2 { } else if c := p[i+2]; c < locb || hicb < c { err.Index = offset + i err.Size = 3 return } else if size == 3 { } else if c := p[i+3]; c < locb || hicb < c { err.Index = offset + i err.Size = 4 return } i += size } return } // Return the size of the next rune if valid, 0 otherwise. func Utf8ValidNext(p []byte) int { c := p[0] if c < utf8.RuneSelf { if InvalidAscii(c) { return 0 } return 1 } x := first[c] if x == xx { // Illegal starter byte. return 0 } size := int(x & 7) if size > len(p) { // Short or invalid. return 0 } accept := acceptRanges[x>>4] if c := p[1]; c < accept.lo || accept.hi < c { return 0 } else if size == 2 { } else if c := p[2]; c < locb || hicb < c { return 0 } else if size == 3 { } else if c := p[3]; c < locb || hicb < c { return 0 } return size } // acceptRange gives the range of valid values for the second byte in a UTF-8 // sequence. type acceptRange struct { lo uint8 // lowest value for second byte. hi uint8 // highest value for second byte. } // acceptRanges has size 16 to avoid bounds checks in the code that uses it. var acceptRanges = [16]acceptRange{ 0: {locb, hicb}, 1: {0xA0, hicb}, 2: {locb, 0x9F}, 3: {0x90, hicb}, 4: {locb, 0x8F}, } // first is information about the first byte in a UTF-8 sequence. var first = [256]uint8{ // 1 2 3 4 5 6 7 8 9 A B C D E F as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x00-0x0F as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x10-0x1F as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x20-0x2F as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x30-0x3F as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x40-0x4F as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x50-0x5F as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x60-0x6F as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x70-0x7F // 1 2 3 4 5 6 7 8 9 A B C D E F xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x80-0x8F xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x90-0x9F xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xA0-0xAF xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xB0-0xBF xx, xx, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xC0-0xCF s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xD0-0xDF s2, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s4, s3, s3, // 0xE0-0xEF s5, s6, s6, s6, s7, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xF0-0xFF } const ( // The default lowest and highest continuation byte. locb = 0b10000000 hicb = 0b10111111 // These names of these constants are chosen to give nice alignment in the // table below. The first nibble is an index into acceptRanges or F for // special one-byte cases. The second nibble is the Rune length or the // Status for the special one-byte case. xx = 0xF1 // invalid: size 1 as = 0xF0 // ASCII: size 1 s1 = 0x02 // accept 0, size 2 s2 = 0x13 // accept 1, size 3 s3 = 0x03 // accept 0, size 3 s4 = 0x23 // accept 2, size 3 s5 = 0x34 // accept 3, size 4 s6 = 0x04 // accept 0, size 4 s7 = 0x44 // accept 4, size 4 ) go-toml-2.1.1/internal/cli/000077500000000000000000000000001453566013500154645ustar00rootroot00000000000000go-toml-2.1.1/internal/cli/cli.go000066400000000000000000000032371453566013500165670ustar00rootroot00000000000000package cli import ( "bytes" "errors" "flag" "fmt" "io" "io/ioutil" "os" "github.com/pelletier/go-toml/v2" ) type ConvertFn func(r io.Reader, w io.Writer) error type Program struct { Usage string Fn ConvertFn // Inplace allows the command to take more than one file as argument and // perform conversion in place on each provided file. Inplace bool } func (p *Program) Execute() { flag.Usage = func() { fmt.Fprintf(os.Stderr, p.Usage) } flag.Parse() os.Exit(p.main(flag.Args(), os.Stdin, os.Stdout, os.Stderr)) } func (p *Program) main(files []string, input io.Reader, output, error io.Writer) int { err := p.run(files, input, output) if err != nil { var derr *toml.DecodeError if errors.As(err, &derr) { fmt.Fprintln(error, derr.String()) row, col := derr.Position() fmt.Fprintln(error, "error occurred at row", row, "column", col) } else { fmt.Fprintln(error, err.Error()) } return -1 } return 0 } func (p *Program) run(files []string, input io.Reader, output io.Writer) error { if len(files) > 0 { if p.Inplace { return p.runAllFilesInPlace(files) } f, err := os.Open(files[0]) if err != nil { return err } defer f.Close() input = f } return p.Fn(input, output) } func (p *Program) runAllFilesInPlace(files []string) error { for _, path := range files { err := p.runFileInPlace(path) if err != nil { return err } } return nil } func (p *Program) runFileInPlace(path string) error { in, err := ioutil.ReadFile(path) if err != nil { return err } out := new(bytes.Buffer) err = p.Fn(bytes.NewReader(in), out) if err != nil { return err } return ioutil.WriteFile(path, out.Bytes(), 0600) } go-toml-2.1.1/internal/cli/cli_test.go000066400000000000000000000101041453566013500176150ustar00rootroot00000000000000package cli import ( "bytes" "fmt" "io" "io/ioutil" "os" "path" "strings" "testing" "github.com/pelletier/go-toml/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func processMain(args []string, input io.Reader, stdout, stderr io.Writer, f ConvertFn) int { p := Program{Fn: f} return p.main(args, input, stdout, stderr) } func TestProcessMainStdin(t *testing.T) { stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) input := strings.NewReader("this is the input") exit := processMain([]string{}, input, stdout, stderr, func(r io.Reader, w io.Writer) error { return nil }) assert.Equal(t, 0, exit) assert.Empty(t, stdout.String()) assert.Empty(t, stderr.String()) } func TestProcessMainStdinErr(t *testing.T) { stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) input := strings.NewReader("this is the input") exit := processMain([]string{}, input, stdout, stderr, func(r io.Reader, w io.Writer) error { return fmt.Errorf("something bad") }) assert.Equal(t, -1, exit) assert.Empty(t, stdout.String()) assert.NotEmpty(t, stderr.String()) } func TestProcessMainStdinDecodeErr(t *testing.T) { stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) input := strings.NewReader("this is the input") exit := processMain([]string{}, input, stdout, stderr, func(r io.Reader, w io.Writer) error { var v interface{} return toml.Unmarshal([]byte(`qwe = 001`), &v) }) assert.Equal(t, -1, exit) assert.Empty(t, stdout.String()) assert.Contains(t, stderr.String(), "error occurred at") } func TestProcessMainFileExists(t *testing.T) { tmpfile, err := ioutil.TempFile("", "example") require.NoError(t, err) defer os.Remove(tmpfile.Name()) _, err = tmpfile.Write([]byte(`some data`)) require.NoError(t, err) stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) exit := processMain([]string{tmpfile.Name()}, nil, stdout, stderr, func(r io.Reader, w io.Writer) error { return nil }) assert.Equal(t, 0, exit) assert.Empty(t, stdout.String()) assert.Empty(t, stderr.String()) } func TestProcessMainFileDoesNotExist(t *testing.T) { stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) exit := processMain([]string{"/lets/hope/this/does/not/exist"}, nil, stdout, stderr, func(r io.Reader, w io.Writer) error { return nil }) assert.Equal(t, -1, exit) assert.Empty(t, stdout.String()) assert.NotEmpty(t, stderr.String()) } func TestProcessMainFilesInPlace(t *testing.T) { dir, err := ioutil.TempDir("", "") require.NoError(t, err) defer os.RemoveAll(dir) path1 := path.Join(dir, "file1") path2 := path.Join(dir, "file2") err = ioutil.WriteFile(path1, []byte("content 1"), 0600) require.NoError(t, err) err = ioutil.WriteFile(path2, []byte("content 2"), 0600) require.NoError(t, err) p := Program{ Fn: dummyFileFn, Inplace: true, } exit := p.main([]string{path1, path2}, os.Stdin, os.Stdout, os.Stderr) require.Equal(t, 0, exit) v1, err := ioutil.ReadFile(path1) require.NoError(t, err) require.Equal(t, "1", string(v1)) v2, err := ioutil.ReadFile(path2) require.NoError(t, err) require.Equal(t, "2", string(v2)) } func TestProcessMainFilesInPlaceErrRead(t *testing.T) { p := Program{ Fn: dummyFileFn, Inplace: true, } exit := p.main([]string{"/this/path/is/invalid"}, os.Stdin, os.Stdout, os.Stderr) require.Equal(t, -1, exit) } func TestProcessMainFilesInPlaceFailFn(t *testing.T) { dir, err := ioutil.TempDir("", "") require.NoError(t, err) defer os.RemoveAll(dir) path1 := path.Join(dir, "file1") err = ioutil.WriteFile(path1, []byte("content 1"), 0600) require.NoError(t, err) p := Program{ Fn: func(io.Reader, io.Writer) error { return fmt.Errorf("oh no") }, Inplace: true, } exit := p.main([]string{path1}, os.Stdin, os.Stdout, os.Stderr) require.Equal(t, -1, exit) v1, err := ioutil.ReadFile(path1) require.NoError(t, err) require.Equal(t, "content 1", string(v1)) } func dummyFileFn(r io.Reader, w io.Writer) error { b, err := ioutil.ReadAll(r) if err != nil { return err } v := strings.SplitN(string(b), " ", 2)[1] _, err = w.Write([]byte(v)) return err } go-toml-2.1.1/internal/danger/000077500000000000000000000000001453566013500161555ustar00rootroot00000000000000go-toml-2.1.1/internal/danger/danger.go000066400000000000000000000032241453566013500177450ustar00rootroot00000000000000package danger import ( "fmt" "reflect" "unsafe" ) const maxInt = uintptr(int(^uint(0) >> 1)) func SubsliceOffset(data []byte, subslice []byte) int { datap := (*reflect.SliceHeader)(unsafe.Pointer(&data)) hlp := (*reflect.SliceHeader)(unsafe.Pointer(&subslice)) if hlp.Data < datap.Data { panic(fmt.Errorf("subslice address (%d) is before data address (%d)", hlp.Data, datap.Data)) } offset := hlp.Data - datap.Data if offset > maxInt { panic(fmt.Errorf("slice offset larger than int (%d)", offset)) } intoffset := int(offset) if intoffset > datap.Len { panic(fmt.Errorf("slice offset (%d) is farther than data length (%d)", intoffset, datap.Len)) } if intoffset+hlp.Len > datap.Len { panic(fmt.Errorf("slice ends (%d+%d) is farther than data length (%d)", intoffset, hlp.Len, datap.Len)) } return intoffset } func BytesRange(start []byte, end []byte) []byte { if start == nil || end == nil { panic("cannot call BytesRange with nil") } startp := (*reflect.SliceHeader)(unsafe.Pointer(&start)) endp := (*reflect.SliceHeader)(unsafe.Pointer(&end)) if startp.Data > endp.Data { panic(fmt.Errorf("start pointer address (%d) is after end pointer address (%d)", startp.Data, endp.Data)) } l := startp.Len endLen := int(endp.Data-startp.Data) + endp.Len if endLen > l { l = endLen } if l > startp.Cap { panic(fmt.Errorf("range length is larger than capacity")) } return start[:l] } func Stride(ptr unsafe.Pointer, size uintptr, offset int) unsafe.Pointer { // TODO: replace with unsafe.Add when Go 1.17 is released // https://github.com/golang/go/issues/40481 return unsafe.Pointer(uintptr(ptr) + uintptr(int(size)*offset)) } go-toml-2.1.1/internal/danger/danger_test.go000066400000000000000000000067211453566013500210110ustar00rootroot00000000000000package danger_test import ( "testing" "unsafe" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/pelletier/go-toml/v2/internal/danger" ) func TestSubsliceOffsetValid(t *testing.T) { examples := []struct { desc string test func() ([]byte, []byte) offset int }{ { desc: "simple", test: func() ([]byte, []byte) { data := []byte("hello") return data, data[1:] }, offset: 1, }, } for _, e := range examples { t.Run(e.desc, func(t *testing.T) { d, s := e.test() offset := danger.SubsliceOffset(d, s) assert.Equal(t, e.offset, offset) }) } } func TestSubsliceOffsetInvalid(t *testing.T) { examples := []struct { desc string test func() ([]byte, []byte) }{ { desc: "unrelated arrays", test: func() ([]byte, []byte) { return []byte("one"), []byte("two") }, }, { desc: "slice starts before data", test: func() ([]byte, []byte) { full := []byte("hello world") return full[5:], full[1:] }, }, { desc: "slice starts after data", test: func() ([]byte, []byte) { full := []byte("hello world") return full[:3], full[5:] }, }, { desc: "slice ends after data", test: func() ([]byte, []byte) { full := []byte("hello world") return full[:5], full[3:8] }, }, } for _, e := range examples { t.Run(e.desc, func(t *testing.T) { d, s := e.test() require.Panics(t, func() { danger.SubsliceOffset(d, s) }) }) } } func TestStride(t *testing.T) { a := []byte{1, 2, 3, 4} x := &a[1] n := (*byte)(danger.Stride(unsafe.Pointer(x), unsafe.Sizeof(byte(0)), 1)) require.Equal(t, &a[2], n) n = (*byte)(danger.Stride(unsafe.Pointer(x), unsafe.Sizeof(byte(0)), -1)) require.Equal(t, &a[0], n) } func TestBytesRange(t *testing.T) { type fn = func() ([]byte, []byte) examples := []struct { desc string test fn expected []byte }{ { desc: "simple", test: func() ([]byte, []byte) { full := []byte("hello world") return full[1:3], full[6:8] }, expected: []byte("ello wo"), }, { desc: "full", test: func() ([]byte, []byte) { full := []byte("hello world") return full[0:1], full[len(full)-1:] }, expected: []byte("hello world"), }, { desc: "end before start", test: func() ([]byte, []byte) { full := []byte("hello world") return full[len(full)-1:], full[0:1] }, }, { desc: "nils", test: func() ([]byte, []byte) { return nil, nil }, }, { desc: "nils start", test: func() ([]byte, []byte) { return nil, []byte("foo") }, }, { desc: "nils end", test: func() ([]byte, []byte) { return []byte("foo"), nil }, }, { desc: "start is end", test: func() ([]byte, []byte) { full := []byte("hello world") return full[1:3], full[1:3] }, expected: []byte("el"), }, { desc: "end contained in start", test: func() ([]byte, []byte) { full := []byte("hello world") return full[1:7], full[2:4] }, expected: []byte("ello w"), }, { desc: "different backing arrays", test: func() ([]byte, []byte) { one := []byte("hello world") two := []byte("hello world") return one, two }, }, } for _, e := range examples { t.Run(e.desc, func(t *testing.T) { start, end := e.test() if e.expected == nil { require.Panics(t, func() { danger.BytesRange(start, end) }) } else { res := danger.BytesRange(start, end) require.Equal(t, e.expected, res) } }) } } go-toml-2.1.1/internal/danger/typeid.go000066400000000000000000000011731453566013500200040ustar00rootroot00000000000000package danger import ( "reflect" "unsafe" ) // typeID is used as key in encoder and decoder caches to enable using // the optimize runtime.mapaccess2_fast64 function instead of the more // expensive lookup if we were to use reflect.Type as map key. // // typeID holds the pointer to the reflect.Type value, which is unique // in the program. // // https://github.com/segmentio/encoding/blob/master/json/codec.go#L59-L61 type TypeID unsafe.Pointer func MakeTypeID(t reflect.Type) TypeID { // reflect.Type has the fields: // typ unsafe.Pointer // ptr unsafe.Pointer return TypeID((*[2]unsafe.Pointer)(unsafe.Pointer(&t))[1]) } go-toml-2.1.1/internal/imported_tests/000077500000000000000000000000001453566013500177625ustar00rootroot00000000000000go-toml-2.1.1/internal/imported_tests/marshal_imported_test.go000066400000000000000000000104541453566013500247060ustar00rootroot00000000000000package imported_tests // Those tests have been imported from v1, but adjust to match the new // defaults of v2. import ( "fmt" "testing" "time" "github.com/pelletier/go-toml/v2" "github.com/stretchr/testify/require" ) func TestDocMarshal(t *testing.T) { type testDoc struct { Title string `toml:"title"` BasicLists testDocBasicLists `toml:"basic_lists"` SubDocPtrs []*testSubDoc `toml:"subdocptrs"` BasicMap map[string]string `toml:"basic_map"` Subdocs testDocSubs `toml:"subdoc"` Basics testDocBasics `toml:"basic"` SubDocList []testSubDoc `toml:"subdoclist"` err int `toml:"shouldntBeHere"` unexported int `toml:"shouldntBeHere"` Unexported2 int `toml:"-"` } var docData = testDoc{ Title: "TOML Marshal Testing", unexported: 0, Unexported2: 0, Basics: testDocBasics{ Bool: true, Date: time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC), Float32: 123.4, Float64: 123.456782132399, Int: 5000, Uint: 5001, String: &biteMe, unexported: 0, }, BasicLists: testDocBasicLists{ Floats: []*float32{&float1, &float2, &float3}, Bools: []bool{true, false, true}, Dates: []time.Time{ time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC), time.Date(1980, 5, 27, 7, 32, 0, 0, time.UTC), }, Ints: []int{8001, 8001, 8002}, Strings: []string{"One", "Two", "Three"}, UInts: []uint{5002, 5003}, }, BasicMap: map[string]string{ "one": "one", "two": "two", }, Subdocs: testDocSubs{ First: testSubDoc{"First", 0}, Second: &subdoc, }, SubDocList: []testSubDoc{ {"List.First", 0}, {"List.Second", 0}, }, SubDocPtrs: []*testSubDoc{&subdoc}, } marshalTestToml := `title = 'TOML Marshal Testing' [basic_lists] floats = [12.3, 45.6, 78.9] bools = [true, false, true] dates = [1979-05-27T07:32:00Z, 1980-05-27T07:32:00Z] ints = [8001, 8001, 8002] uints = [5002, 5003] strings = ['One', 'Two', 'Three'] [[subdocptrs]] name = 'Second' [basic_map] one = 'one' two = 'two' [subdoc] [subdoc.second] name = 'Second' [subdoc.first] name = 'First' [basic] uint = 5001 bool = true float = 123.4 float64 = 123.456782132399 int = 5000 string = 'Bite me' date = 1979-05-27T07:32:00Z [[subdoclist]] name = 'List.First' [[subdoclist]] name = 'List.Second' ` result, err := toml.Marshal(docData) require.NoError(t, err) require.Equal(t, marshalTestToml, string(result)) } func TestBasicMarshalQuotedKey(t *testing.T) { result, err := toml.Marshal(quotedKeyMarshalTestData) require.NoError(t, err) expected := `'Z.string-àéù' = 'Hello' 'Yfloat-𝟘' = 3.5 ['Xsubdoc-àéù'] String2 = 'One' [['W.sublist-𝟘']] String2 = 'Two' [['W.sublist-𝟘']] String2 = 'Three' ` require.Equal(t, string(expected), string(result)) } func TestEmptyMarshal(t *testing.T) { type emptyMarshalTestStruct struct { Title string `toml:"title"` Bool bool `toml:"bool"` Int int `toml:"int"` String string `toml:"string"` StringList []string `toml:"stringlist"` Ptr *basicMarshalTestStruct `toml:"ptr"` Map map[string]string `toml:"map"` } doc := emptyMarshalTestStruct{ Title: "Placeholder", Bool: false, Int: 0, String: "", StringList: []string{}, Ptr: nil, Map: map[string]string{}, } result, err := toml.Marshal(doc) require.NoError(t, err) expected := `title = 'Placeholder' bool = false int = 0 string = '' stringlist = [] [map] ` require.Equal(t, string(expected), string(result)) } type textMarshaler struct { FirstName string LastName string } func (m textMarshaler) MarshalText() ([]byte, error) { fullName := fmt.Sprintf("%s %s", m.FirstName, m.LastName) return []byte(fullName), nil } func TestTextMarshaler(t *testing.T) { type wrap struct { TM textMarshaler } m := textMarshaler{FirstName: "Sally", LastName: "Fields"} t.Run("at root", func(t *testing.T) { _, err := toml.Marshal(m) // in v2 we do not allow TextMarshaler at root require.Error(t, err) }) t.Run("leaf", func(t *testing.T) { res, err := toml.Marshal(wrap{m}) require.NoError(t, err) require.Equal(t, "TM = 'Sally Fields'\n", string(res)) }) } go-toml-2.1.1/internal/imported_tests/unmarshal_imported_test.go000066400000000000000000001523221453566013500252520ustar00rootroot00000000000000package imported_tests // Those tests were imported directly from go-toml v1 // https://raw.githubusercontent.com/pelletier/go-toml/a2e52561804c6cd9392ebf0048ca64fe4af67a43/marshal_test.go // They have been cleaned up to only include Unmarshal tests, and only depend // on the public API. Tests related to strict mode have been commented out and // marked as skipped until we figure out if that's something we want in v2. import ( "bytes" "errors" "fmt" "reflect" "strconv" "testing" "time" "github.com/pelletier/go-toml/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) type basicMarshalTestStruct struct { String string `toml:"Zstring"` StringList []string `toml:"Ystrlist"` BasicMarshalTestSubAnonymousStruct Sub basicMarshalTestSubStruct `toml:"Xsubdoc"` SubList []basicMarshalTestSubStruct `toml:"Wsublist"` } type basicMarshalTestSubStruct struct { String2 string } type BasicMarshalTestSubAnonymousStruct struct { String3 string } var basicTestData = basicMarshalTestStruct{ String: "Hello", StringList: []string{"Howdy", "Hey There"}, BasicMarshalTestSubAnonymousStruct: BasicMarshalTestSubAnonymousStruct{"One"}, Sub: basicMarshalTestSubStruct{"Two"}, SubList: []basicMarshalTestSubStruct{{"Three"}, {"Four"}}, } var basicTestToml = []byte(`String3 = "One" Ystrlist = ["Howdy", "Hey There"] Zstring = "Hello" [[Wsublist]] String2 = "Three" [[Wsublist]] String2 = "Four" [Xsubdoc] String2 = "Two" `) var marshalTestToml = []byte(`title = "TOML Marshal Testing" [basic] bool = true date = 1979-05-27T07:32:00Z float = 123.4 float64 = 123.456782132399 int = 5000 string = "Bite me" uint = 5001 [basic_lists] bools = [true, false, true] dates = [1979-05-27T07:32:00Z, 1980-05-27T07:32:00Z] floats = [12.3, 45.6, 78.9] ints = [8001, 8001, 8002] strings = ["One", "Two", "Three"] uints = [5002, 5003] [basic_map] one = "one" two = "two" [subdoc] [subdoc.first] name = "First" [subdoc.second] name = "Second" [[subdoclist]] name = "List.First" [[subdoclist]] name = "List.Second" [[subdocptrs]] name = "Second" `) type Conf struct { Name string Age int Inter interface{} } type NestedStruct struct { FirstName string LastName string Age int } var doc = []byte(`Name = "rui" Age = 18 [Inter] FirstName = "wang" LastName = "jl" Age = 100`) func TestInterface(t *testing.T) { var config Conf config.Inter = &NestedStruct{} err := toml.Unmarshal(doc, &config) require.NoError(t, err) expected := Conf{ Name: "rui", Age: 18, Inter: map[string]interface{}{ "FirstName": "wang", "LastName": "jl", "Age": int64(100), }, } assert.Equal(t, expected, config) } func TestBasicUnmarshal(t *testing.T) { result := basicMarshalTestStruct{} err := toml.Unmarshal(basicTestToml, &result) require.NoError(t, err) require.Equal(t, basicTestData, result) } type quotedKeyMarshalTestStruct struct { String string `toml:"Z.string-àéù"` Float float64 `toml:"Yfloat-𝟘"` Sub basicMarshalTestSubStruct `toml:"Xsubdoc-àéù"` SubList []basicMarshalTestSubStruct `toml:"W.sublist-𝟘"` } // TODO: Remove nolint once var is used by a test // //nolint:deadcode,unused,varcheck var quotedKeyMarshalTestData = quotedKeyMarshalTestStruct{ String: "Hello", Float: 3.5, Sub: basicMarshalTestSubStruct{"One"}, SubList: []basicMarshalTestSubStruct{{"Two"}, {"Three"}}, } // TODO: Remove nolint once var is used by a test // //nolint:deadcode,unused,varcheck var quotedKeyMarshalTestToml = []byte(`"Yfloat-𝟘" = 3.5 "Z.string-àéù" = "Hello" [["W.sublist-𝟘"]] String2 = "Two" [["W.sublist-𝟘"]] String2 = "Three" ["Xsubdoc-àéù"] String2 = "One" `) type testDoc struct { Title string `toml:"title"` BasicLists testDocBasicLists `toml:"basic_lists"` SubDocPtrs []*testSubDoc `toml:"subdocptrs"` BasicMap map[string]string `toml:"basic_map"` Subdocs testDocSubs `toml:"subdoc"` Basics testDocBasics `toml:"basic"` SubDocList []testSubDoc `toml:"subdoclist"` err int `toml:"shouldntBeHere"` // nolint:structcheck,unused unexported int `toml:"shouldntBeHere"` Unexported2 int `toml:"-"` } type testMapDoc struct { Title string `toml:"title"` BasicMap map[string]string `toml:"basic_map"` LongMap map[string]string `toml:"long_map"` } type testDocBasics struct { Uint uint `toml:"uint"` Bool bool `toml:"bool"` Float32 float32 `toml:"float"` Float64 float64 `toml:"float64"` Int int `toml:"int"` String *string `toml:"string"` Date time.Time `toml:"date"` unexported int `toml:"shouldntBeHere"` } type testDocBasicLists struct { Floats []*float32 `toml:"floats"` Bools []bool `toml:"bools"` Dates []time.Time `toml:"dates"` Ints []int `toml:"ints"` UInts []uint `toml:"uints"` Strings []string `toml:"strings"` } type testDocSubs struct { Second *testSubDoc `toml:"second"` First testSubDoc `toml:"first"` } type testSubDoc struct { Name string `toml:"name"` unexported int `toml:"shouldntBeHere"` } var ( biteMe = "Bite me" float1 float32 = 12.3 float2 float32 = 45.6 float3 float32 = 78.9 subdoc = testSubDoc{"Second", 0} ) var docData = testDoc{ Title: "TOML Marshal Testing", unexported: 0, Unexported2: 0, Basics: testDocBasics{ Bool: true, Date: time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC), Float32: 123.4, Float64: 123.456782132399, Int: 5000, Uint: 5001, String: &biteMe, unexported: 0, }, BasicLists: testDocBasicLists{ Bools: []bool{true, false, true}, Dates: []time.Time{ time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC), time.Date(1980, 5, 27, 7, 32, 0, 0, time.UTC), }, Floats: []*float32{&float1, &float2, &float3}, Ints: []int{8001, 8001, 8002}, Strings: []string{"One", "Two", "Three"}, UInts: []uint{5002, 5003}, }, BasicMap: map[string]string{ "one": "one", "two": "two", }, Subdocs: testDocSubs{ First: testSubDoc{"First", 0}, Second: &subdoc, }, SubDocList: []testSubDoc{ {"List.First", 0}, {"List.Second", 0}, }, SubDocPtrs: []*testSubDoc{&subdoc}, } // TODO: Remove nolint once var is used by a test // //nolint:deadcode,unused,varcheck var mapTestDoc = testMapDoc{ Title: "TOML Marshal Testing", BasicMap: map[string]string{ "one": "one", "two": "two", }, LongMap: map[string]string{ "h1": "8", "i2": "9", "b3": "2", "d4": "4", "f5": "6", "e6": "5", "a7": "1", "c8": "3", "j9": "10", "g10": "7", }, } func TestDocUnmarshal(t *testing.T) { result := testDoc{} err := toml.Unmarshal(marshalTestToml, &result) expected := docData require.NoError(t, err) assert.Equal(t, expected, result) } type unexportedMarshalTestStruct struct { String string `toml:"string"` StringList []string `toml:"strlist"` Sub basicMarshalTestSubStruct `toml:"subdoc"` SubList []basicMarshalTestSubStruct `toml:"sublist"` unexported int `toml:"shouldntBeHere"` Unexported2 int `toml:"-"` } var unexportedTestData = unexportedMarshalTestStruct{ String: "Hello", StringList: []string{"Howdy", "Hey There"}, Sub: basicMarshalTestSubStruct{"One"}, SubList: []basicMarshalTestSubStruct{{"Two"}, {"Three"}}, unexported: 0, Unexported2: 0, } var unexportedTestToml = []byte(`string = "Hello" strlist = ["Howdy","Hey There"] unexported = 1 shouldntBeHere = 2 [subdoc] String2 = "One" [[sublist]] String2 = "Two" [[sublist]] String2 = "Three" `) func TestUnexportedUnmarshal(t *testing.T) { result := unexportedMarshalTestStruct{} err := toml.Unmarshal(unexportedTestToml, &result) require.NoError(t, err) assert.Equal(t, unexportedTestData, result) } type errStruct struct { Bool bool `toml:"bool"` Date time.Time `toml:"date"` Float float64 `toml:"float"` Int int16 `toml:"int"` String *string `toml:"string"` } type mapErr struct { Vals map[string]float64 } type intErr struct { Int1 int Int2 int8 Int3 int16 Int4 int32 Int5 int64 UInt1 uint UInt2 uint8 UInt3 uint16 UInt4 uint32 UInt5 uint64 Flt1 float32 Flt2 float64 } var intErrTomls = []string{ "Int1 = []\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", "Int1 = 1\nInt2 = []\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", "Int1 = 1\nInt2 = 2\nInt3 = []\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = []\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = []\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = []\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = []\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = []\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = []\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0", "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = []\nFlt1 = 1.0\nFlt2 = 2.0", "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = []\nFlt2 = 2.0", "Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = []", } func TestErrUnmarshal(t *testing.T) { errTomls := []string{ "bool = truly\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"", "bool = true\ndate = 1979-05-27T07:3200Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"", "bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123a4\nint = 5000\nstring = \"Bite me\"", "bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = j000\nstring = \"Bite me\"", "bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = Bite me", "bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = Bite me", "bool = 1\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"", "bool = true\ndate = 1\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"", "bool = true\ndate = 1979-05-27T07:32:00Z\n\"sorry\"\nint = 5000\nstring = \"Bite me\"", "bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = \"sorry\"\nstring = \"Bite me\"", "bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = 1", } for ind, x := range errTomls { t.Run(fmt.Sprintf("Base Case %d", ind), func(t *testing.T) { result := errStruct{} err := toml.Unmarshal([]byte(x), &result) if err == nil { t.Errorf("Expected err from case %d\n", ind) } }) } result2 := mapErr{} err := toml.Unmarshal([]byte("[Vals]\nfred=\"1.2\""), &result2) if err == nil { t.Errorf("Expected err from map") } for ind, x := range intErrTomls { result3 := intErr{} err := toml.Unmarshal([]byte(x), &result3) if err == nil { t.Errorf("Expected int err from case %d\n", ind) } } } var emptyTestToml = []byte(`bool = false int = 0 string = "" stringlist = [] title = "Placeholder" [map] `) func TestEmptytomlUnmarshal(t *testing.T) { type emptyMarshalTestStruct struct { Title string `toml:"title"` Bool bool `toml:"bool"` Int int `toml:"int"` String string `toml:"string"` StringList []string `toml:"stringlist"` Ptr *basicMarshalTestStruct `toml:"ptr"` Map map[string]string `toml:"map"` } emptyTestData := emptyMarshalTestStruct{ Title: "Placeholder", Bool: false, Int: 0, String: "", StringList: []string{}, Ptr: nil, Map: nil, } result := emptyMarshalTestStruct{} err := toml.Unmarshal(emptyTestToml, &result) require.NoError(t, err) assert.Equal(t, emptyTestData, result) } type pointerMarshalTestStruct struct { Str *string List *[]string ListPtr *[]*string Map *map[string]string MapPtr *map[string]*string EmptyStr *string EmptyList *[]string EmptyMap *map[string]string DblPtr *[]*[]*string } var ( pointerStr = "Hello" pointerList = []string{"Hello back"} pointerListPtr = []*string{&pointerStr} pointerMap = map[string]string{"response": "Goodbye"} pointerMapPtr = map[string]*string{"alternate": &pointerStr} pointerTestData = pointerMarshalTestStruct{ Str: &pointerStr, List: &pointerList, ListPtr: &pointerListPtr, Map: &pointerMap, MapPtr: &pointerMapPtr, EmptyStr: nil, EmptyList: nil, EmptyMap: nil, } ) var pointerTestToml = []byte(`List = ["Hello back"] ListPtr = ["Hello"] Str = "Hello" [Map] response = "Goodbye" [MapPtr] alternate = "Hello" `) func TestPointerUnmarshal(t *testing.T) { result := pointerMarshalTestStruct{} err := toml.Unmarshal(pointerTestToml, &result) require.NoError(t, err) assert.Equal(t, pointerTestData, result) } func TestUnmarshalTypeMismatch(t *testing.T) { result := pointerMarshalTestStruct{} err := toml.Unmarshal([]byte("List = 123"), &result) assert.Error(t, err) } type nestedMarshalTestStruct struct { String [][]string // Struct [][]basicMarshalTestSubStruct StringPtr *[]*[]*string // StructPtr *[]*[]*basicMarshalTestSubStruct } var ( str1 = "Three" str2 = "Four" strPtr = []*string{&str1, &str2} strPtr2 = []*[]*string{&strPtr} ) var nestedTestData = nestedMarshalTestStruct{ String: [][]string{{"Five", "Six"}, {"One", "Two"}}, StringPtr: &strPtr2, } var nestedTestToml = []byte(`String = [["Five", "Six"], ["One", "Two"]] StringPtr = [["Three", "Four"]] `) func TestNestedUnmarshal(t *testing.T) { result := nestedMarshalTestStruct{} err := toml.Unmarshal(nestedTestToml, &result) require.NoError(t, err) assert.Equal(t, nestedTestData, result) } type customMarshalerParent struct { Self customMarshaler `toml:"me"` Friends []customMarshaler `toml:"friends"` } type customMarshaler struct { FirstName string LastName string } func (c customMarshaler) MarshalTOML() ([]byte, error) { fullName := fmt.Sprintf("%s %s", c.FirstName, c.LastName) return []byte(fullName), nil } var customMarshalerData = customMarshaler{FirstName: "Sally", LastName: "Fields"} // TODO: Remove nolint once var is used by a test // //nolint:deadcode,unused,varcheck var customMarshalerToml = []byte(`Sally Fields`) // TODO: Remove nolint once var is used by a test // //nolint:deadcode,unused,varcheck var nestedCustomMarshalerData = customMarshalerParent{ Self: customMarshaler{FirstName: "Maiku", LastName: "Suteda"}, Friends: []customMarshaler{customMarshalerData}, } // TODO: Remove nolint once var is used by a test // //nolint:deadcode,unused,varcheck var nestedCustomMarshalerToml = []byte(`friends = ["Sally Fields"] me = "Maiku Suteda" `) var nestedCustomMarshalerTomlForUnmarshal = []byte(`[friends] FirstName = "Sally" LastName = "Fields"`) type IntOrString string func (x *IntOrString) MarshalTOML() ([]byte, error) { s := *(*string)(x) _, err := strconv.Atoi(s) if err != nil { return []byte(fmt.Sprintf(`"%s"`, s)), nil } return []byte(s), nil } func TestUnmarshalTextMarshaler(t *testing.T) { nested := struct { Friends textMarshaler `toml:"friends"` }{} expected := struct { Friends textMarshaler `toml:"friends"` }{ Friends: textMarshaler{FirstName: "Sally", LastName: "Fields"}, } err := toml.Unmarshal(nestedCustomMarshalerTomlForUnmarshal, &nested) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(nested, expected) { t.Errorf("Bad unmarshal: expected %v, got %v", expected, nested) } } // TODO: Remove nolint once type and methods are used by a test // //nolint:unused type precedentMarshaler struct { FirstName string LastName string } //nolint:unused func (m precedentMarshaler) MarshalText() ([]byte, error) { return []byte("shadowed"), nil } //nolint:unused func (m precedentMarshaler) MarshalTOML() ([]byte, error) { fullName := fmt.Sprintf("%s %s", m.FirstName, m.LastName) return []byte(fullName), nil } // TODO: Remove nolint once type and method are used by a test // //nolint:unused type customPointerMarshaler struct { FirstName string LastName string } //nolint:unused func (m *customPointerMarshaler) MarshalTOML() ([]byte, error) { return []byte(`"hidden"`), nil } // TODO: Remove nolint once type and method are used by a test // //nolint:unused type textPointerMarshaler struct { FirstName string LastName string } //nolint:unused func (m *textPointerMarshaler) MarshalText() ([]byte, error) { return []byte("hidden"), nil } // TODO: Remove nolint once var is used by a test // //nolint:deadcode,unused,varcheck var commentTestToml = []byte(` # it's a comment on type [postgres] # isCommented = "dvalue" noComment = "cvalue" # A comment on AttrB with a # break line password = "bvalue" # A comment on AttrA user = "avalue" [[postgres.My]] # a comment on my on typeC My = "Foo" [[postgres.My]] # a comment on my on typeC My = "Baar" `) type mapsTestStruct struct { Simple map[string]string Paths map[string]string Other map[string]float64 X struct { Y struct { Z map[string]bool } } } // TODO: Remove nolint once var is used by a test // //nolint:deadcode,unused,varcheck var mapsTestData = mapsTestStruct{ Simple: map[string]string{ "one plus one": "two", "next": "three", }, Paths: map[string]string{ "/this/is/a/path": "/this/is/also/a/path", "/heloo.txt": "/tmp/lololo.txt", }, Other: map[string]float64{ "testing": 3.9999, }, X: struct{ Y struct{ Z map[string]bool } }{ Y: struct{ Z map[string]bool }{ Z: map[string]bool{ "is.Nested": true, }, }, }, } // TODO: Remove nolint once var is used by a test // //nolint:deadcode,unused,varcheck var mapsTestToml = []byte(` [Other] "testing" = 3.9999 [Paths] "/heloo.txt" = "/tmp/lololo.txt" "/this/is/a/path" = "/this/is/also/a/path" [Simple] "next" = "three" "one plus one" = "two" [X] [X.Y] [X.Y.Z] "is.Nested" = true `) // TODO: Remove nolint once type is used by a test // //nolint:deadcode,unused type structArrayNoTag struct { A struct { B []int64 C []int64 } } // TODO: Remove nolint once var is used by a test // //nolint:deadcode,unused,varcheck var customTagTestToml = []byte(` [postgres] password = "bvalue" user = "avalue" [[postgres.My]] My = "Foo" [[postgres.My]] My = "Baar" `) // TODO: Remove nolint once var is used by a test // //nolint:deadcode,unused,varcheck var customCommentTagTestToml = []byte(` # db connection [postgres] # db pass password = "bvalue" # db user user = "avalue" `) // TODO: Remove nolint once var is used by a test // //nolint:deadcode,unused,varcheck var customCommentedTagTestToml = []byte(` [postgres] # password = "bvalue" # user = "avalue" `) func TestUnmarshalTabInStringAndQuotedKey(t *testing.T) { type Test struct { Field1 string `toml:"Fie ld1"` Field2 string } type TestCase struct { desc string input []byte expected Test } testCases := []TestCase{ { desc: "multiline string with tab", input: []byte("Field2 = \"\"\"\nhello\tworld\"\"\""), expected: Test{ Field2: "hello\tworld", }, }, { desc: "quoted key with tab", input: []byte("\"Fie\tld1\" = \"key with tab\""), expected: Test{ Field1: "key with tab", }, }, { desc: "basic string tab", input: []byte("Field2 = \"hello\tworld\""), expected: Test{ Field2: "hello\tworld", }, }, } for _, test := range testCases { t.Run(test.desc, func(t *testing.T) { result := Test{} err := toml.Unmarshal(test.input, &result) require.NoError(t, err) assert.Equal(t, test.expected, result) }) } } // TODO: Remove nolint once var is used by a test // //nolint:deadcode,unused,varcheck var customMultilineTagTestToml = []byte(`int_slice = [ 1, 2, 3, ] `) // TODO: Remove nolint once var is used by a test // //nolint:deadcode,unused,varcheck var testDocBasicToml = []byte(` [document] bool_val = true date_val = 1979-05-27T07:32:00Z float_val = 123.4 int_val = 5000 string_val = "Bite me" uint_val = 5001 `) // TODO: Remove nolint once type is used by a test // //nolint:deadcode type testDocCustomTag struct { Doc testDocBasicsCustomTag `file:"document"` } // TODO: Remove nolint once type is used by a test // //nolint:deadcode type testDocBasicsCustomTag struct { Bool bool `file:"bool_val"` Date time.Time `file:"date_val"` Float float32 `file:"float_val"` Int int `file:"int_val"` Uint uint `file:"uint_val"` String *string `file:"string_val"` unexported int `file:"shouldntBeHere"` } // TODO: Remove nolint once var is used by a test // //nolint:deadcode,varcheck var testDocCustomTagData = testDocCustomTag{ Doc: testDocBasicsCustomTag{ Bool: true, Date: time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC), Float: 123.4, Int: 5000, Uint: 5001, String: &biteMe, unexported: 0, }, } func TestUnmarshalMap(t *testing.T) { testToml := []byte(` a = 1 b = 2 c = 3 `) var result map[string]int err := toml.Unmarshal(testToml, &result) if err != nil { t.Errorf("Received unexpected error: %s", err) return } expected := map[string]int{ "a": 1, "b": 2, "c": 3, } if !reflect.DeepEqual(result, expected) { t.Errorf("Bad unmarshal: expected %v, got %v", expected, result) } } func TestUnmarshalMapWithTypedKey(t *testing.T) { testToml := []byte(` a = 1 b = 2 c = 3 `) type letter string var result map[letter]int err := toml.Unmarshal(testToml, &result) if err != nil { t.Errorf("Received unexpected error: %s", err) return } expected := map[letter]int{ "a": 1, "b": 2, "c": 3, } if !reflect.DeepEqual(result, expected) { t.Errorf("Bad unmarshal: expected %v, got %v", expected, result) } } func TestUnmarshalTypeTableHeader(t *testing.T) { testToml := []byte(` [test] a = 1 `) type header string var result map[header]map[string]int err := toml.Unmarshal(testToml, &result) if err != nil { t.Errorf("Received unexpected error: %s", err) return } expected := map[header]map[string]int{ "test": map[string]int{"a": 1}, } if !reflect.DeepEqual(result, expected) { t.Errorf("Bad unmarshal: expected %v, got %v", expected, result) } } func TestUnmarshalNonPointer(t *testing.T) { a := 1 err := toml.Unmarshal([]byte{}, a) if err == nil { t.Fatal("unmarshal should err when given a non pointer") } } func TestUnmarshalInvalidPointerKind(t *testing.T) { t.Skipf("should this really be an error?") a := 1 err := toml.Unmarshal([]byte{}, &a) assert.Error(t, err) } // TODO: Remove nolint once var is used by a test // //nolint:deadcode,unused type testDuration struct { Nanosec time.Duration `toml:"nanosec"` Microsec1 time.Duration `toml:"microsec1"` Microsec2 *time.Duration `toml:"microsec2"` Millisec time.Duration `toml:"millisec"` Sec time.Duration `toml:"sec"` Min time.Duration `toml:"min"` Hour time.Duration `toml:"hour"` Mixed time.Duration `toml:"mixed"` AString string `toml:"a_string"` } // TODO: Remove nolint once var is used by a test // //nolint:deadcode,unused,varcheck var testDurationToml = []byte(` nanosec = "1ns" microsec1 = "1us" microsec2 = "1µs" millisec = "1ms" sec = "1s" min = "1m" hour = "1h" mixed = "1h1m1s1ms1µs1ns" a_string = "15s" `) // TODO: Remove nolint once var is used by a test // //nolint:deadcode,unused,varcheck var testDurationToml2 = []byte(`a_string = "15s" hour = "1h0m0s" microsec1 = "1µs" microsec2 = "1µs" millisec = "1ms" min = "1m0s" mixed = "1h1m1.001001001s" nanosec = "1ns" sec = "1s" `) // TODO: Remove nolint once type is used by a test // //nolint:deadcode,unused type testBadDuration struct { Val time.Duration `toml:"val"` } // TODO: add back camelCase test var testCamelCaseKeyToml = []byte(`fooBar = 10`) //nolint:unused //nolint:unused func TestUnmarshalCamelCaseKey(t *testing.T) { t.Skipf("don't know if it is a good idea to automatically convert like that yet") var x struct { FooBar int B int } if err := toml.Unmarshal(testCamelCaseKeyToml, &x); err != nil { t.Fatal(err) } if x.FooBar != 10 { t.Fatal("Did not set camelCase'd key") } } func TestUnmarshalNegativeUint(t *testing.T) { t.Skipf("not sure if we this should always error") type check struct{ U uint } // nolint:unused err := toml.Unmarshal([]byte("U = -1"), &check{}) assert.Error(t, err) } func TestUnmarshalCheckConversionFloatInt(t *testing.T) { type conversionCheck struct { U uint I int F float64 } type TestCase struct { desc string input string } testCases := []TestCase{ { desc: "unsigned int", input: `U = 1e300`, }, { desc: "int", input: `I = 1e300`, }, } for _, test := range testCases { t.Run(test.desc, func(t *testing.T) { err := toml.Unmarshal([]byte(test.input), &conversionCheck{}) require.Error(t, err) }) } } func TestUnmarshalOverflow(t *testing.T) { type overflow struct { U8 uint8 I8 int8 F32 float32 } type TestCase struct { desc string input string } testCases := []TestCase{ { desc: "byte", input: `U8 = 300`, }, { desc: "int8", input: `I8 = 300`, }, { desc: "float32", input: `F32 = 1e300`, }, } for _, test := range testCases { t.Run(test.desc, func(t *testing.T) { err := toml.Unmarshal([]byte(test.input), &overflow{}) require.Error(t, err) }) } } func TestUnmarshalDefault(t *testing.T) { t.Skipf("don't know if it is a good idea to have `default`") t.Run("main", func(t *testing.T) { type EmbeddedStruct struct { StringField string `default:"c"` } type aliasUint uint var doc struct { StringField string `default:"a"` BoolField bool `default:"true"` UintField uint `default:"1"` Uint8Field uint8 `default:"8"` Uint16Field uint16 `default:"16"` Uint32Field uint32 `default:"32"` Uint64Field uint64 `default:"64"` IntField int `default:"-1"` Int8Field int8 `default:"-8"` Int16Field int16 `default:"-16"` Int32Field int32 `default:"-32"` Int64Field int64 `default:"-64"` Float32Field float32 `default:"32.1"` Float64Field float64 `default:"64.1"` DurationField time.Duration `default:"120ms"` DurationField2 time.Duration `default:"120000000"` NonEmbeddedStruct struct { StringField string `default:"b"` } EmbeddedStruct AliasUintField aliasUint `default:"1000"` } err := toml.Unmarshal([]byte(``), &doc) if err != nil { t.Fatal(err) } if doc.BoolField != true { t.Errorf("BoolField should be true, not %t", doc.BoolField) } if doc.StringField != "a" { t.Errorf("StringField should be \"a\", not %s", doc.StringField) } if doc.UintField != 1 { t.Errorf("UintField should be 1, not %d", doc.UintField) } if doc.Uint8Field != 8 { t.Errorf("Uint8Field should be 8, not %d", doc.Uint8Field) } if doc.Uint16Field != 16 { t.Errorf("Uint16Field should be 16, not %d", doc.Uint16Field) } if doc.Uint32Field != 32 { t.Errorf("Uint32Field should be 32, not %d", doc.Uint32Field) } if doc.Uint64Field != 64 { t.Errorf("Uint64Field should be 64, not %d", doc.Uint64Field) } if doc.IntField != -1 { t.Errorf("IntField should be -1, not %d", doc.IntField) } if doc.Int8Field != -8 { t.Errorf("Int8Field should be -8, not %d", doc.Int8Field) } if doc.Int16Field != -16 { t.Errorf("Int16Field should be -16, not %d", doc.Int16Field) } if doc.Int32Field != -32 { t.Errorf("Int32Field should be -32, not %d", doc.Int32Field) } if doc.Int64Field != -64 { t.Errorf("Int64Field should be -64, not %d", doc.Int64Field) } if doc.Float32Field != 32.1 { t.Errorf("Float32Field should be 32.1, not %f", doc.Float32Field) } if doc.Float64Field != 64.1 { t.Errorf("Float64Field should be 64.1, not %f", doc.Float64Field) } if doc.DurationField != 120*time.Millisecond { t.Errorf("DurationField should be 120ms, not %s", doc.DurationField.String()) } if doc.DurationField2 != 120*time.Millisecond { t.Errorf("DurationField2 should be 120000000, not %d", doc.DurationField2) } if doc.NonEmbeddedStruct.StringField != "b" { t.Errorf("StringField should be \"b\", not %s", doc.NonEmbeddedStruct.StringField) } if doc.EmbeddedStruct.StringField != "c" { t.Errorf("StringField should be \"c\", not %s", doc.EmbeddedStruct.StringField) } if doc.AliasUintField != 1000 { t.Errorf("AliasUintField should be 1000, not %d", doc.AliasUintField) } }) t.Run("failure bool", func(t *testing.T) { var doc struct { Field bool `default:"blah"` } err := toml.Unmarshal([]byte(``), &doc) if err == nil { t.Fatal("should error") } }) t.Run("failure int", func(t *testing.T) { var doc struct { Field int `default:"blah"` } err := toml.Unmarshal([]byte(``), &doc) if err == nil { t.Fatal("should error") } }) t.Run("failure int64", func(t *testing.T) { var doc struct { Field int64 `default:"blah"` } err := toml.Unmarshal([]byte(``), &doc) if err == nil { t.Fatal("should error") } }) t.Run("failure float64", func(t *testing.T) { var doc struct { Field float64 `default:"blah"` } err := toml.Unmarshal([]byte(``), &doc) if err == nil { t.Fatal("should error") } }) t.Run("failure duration", func(t *testing.T) { var doc struct { Field time.Duration `default:"blah"` } err := toml.Unmarshal([]byte(``), &doc) if err == nil { t.Fatal("should error") } }) t.Run("failure unsupported", func(t *testing.T) { var doc struct { Field struct{} `default:"blah"` } err := toml.Unmarshal([]byte(``), &doc) if err == nil { t.Fatal("should error") } }) } func TestUnmarshalNestedAnonymousStructs(t *testing.T) { type Nested struct { Value string `toml:"nested_field"` } type Deep struct { Nested } type Document struct { Deep Value string `toml:"own_field"` } var doc Document err := toml.Unmarshal([]byte(`nested_field = "nested value"`+"\n"+`own_field = "own value"`), &doc) if err != nil { t.Fatal("should not error") } if doc.Value != "own value" || doc.Nested.Value != "nested value" { t.Fatal("unexpected values") } } func TestUnmarshalNestedAnonymousStructs_Controversial(t *testing.T) { t.Skipf("TODO: what does encoding/json do?") type Nested struct { Value string `toml:"nested"` } type Deep struct { Nested } type Document struct { Deep Value string `toml:"own"` } var doc Document err := toml.Unmarshal([]byte(`nested = "nested value"`+"\n"+`own = "own value"`), &doc) if err == nil { t.Fatal("should error") } } type unexportedFieldPreservationTest struct { Exported string `toml:"exported"` unexported string Nested1 unexportedFieldPreservationTestNested `toml:"nested1"` Nested2 *unexportedFieldPreservationTestNested `toml:"nested2"` Nested3 *unexportedFieldPreservationTestNested `toml:"nested3"` Slice1 []unexportedFieldPreservationTestNested `toml:"slice1"` Slice2 []*unexportedFieldPreservationTestNested `toml:"slice2"` } type unexportedFieldPreservationTestNested struct { Exported1 string `toml:"exported1"` unexported1 string } func TestUnmarshalPreservesUnexportedFields(t *testing.T) { doc := ` exported = "visible" unexported = "ignored" [nested1] exported1 = "visible1" unexported1 = "ignored1" [nested2] exported1 = "visible2" unexported1 = "ignored2" [nested3] exported1 = "visible3" unexported1 = "ignored3" [[slice1]] exported1 = "visible3" [[slice1]] exported1 = "visible4" [[slice2]] exported1 = "visible5" ` t.Run("unexported field should not be set from toml", func(t *testing.T) { var actual unexportedFieldPreservationTest err := toml.Unmarshal([]byte(doc), &actual) if err != nil { t.Fatal("did not expect an error") } expect := unexportedFieldPreservationTest{ Exported: "visible", unexported: "", Nested1: unexportedFieldPreservationTestNested{"visible1", ""}, Nested2: &unexportedFieldPreservationTestNested{"visible2", ""}, Nested3: &unexportedFieldPreservationTestNested{"visible3", ""}, Slice1: []unexportedFieldPreservationTestNested{ {Exported1: "visible3"}, {Exported1: "visible4"}, }, Slice2: []*unexportedFieldPreservationTestNested{ {Exported1: "visible5"}, }, } if !reflect.DeepEqual(actual, expect) { t.Fatalf("%+v did not equal %+v", actual, expect) } }) t.Run("unexported field should be preserved", func(t *testing.T) { actual := unexportedFieldPreservationTest{ Exported: "foo", unexported: "bar", Nested1: unexportedFieldPreservationTestNested{"baz", "bax"}, Nested2: nil, Nested3: &unexportedFieldPreservationTestNested{"baz", "bax"}, } err := toml.Unmarshal([]byte(doc), &actual) if err != nil { t.Fatal("did not expect an error") } expect := unexportedFieldPreservationTest{ Exported: "visible", unexported: "bar", Nested1: unexportedFieldPreservationTestNested{"visible1", "bax"}, Nested2: &unexportedFieldPreservationTestNested{"visible2", ""}, Nested3: &unexportedFieldPreservationTestNested{"visible3", "bax"}, Slice1: []unexportedFieldPreservationTestNested{ {Exported1: "visible3"}, {Exported1: "visible4"}, }, Slice2: []*unexportedFieldPreservationTestNested{ {Exported1: "visible5"}, }, } if !reflect.DeepEqual(actual, expect) { t.Fatalf("%+v did not equal %+v", actual, expect) } }) } func TestUnmarshalLocalDate(t *testing.T) { t.Run("ToLocalDate", func(t *testing.T) { type dateStruct struct { Date toml.LocalDate } doc := `date = 1979-05-27` var obj dateStruct err := toml.Unmarshal([]byte(doc), &obj) if err != nil { t.Fatal(err) } if obj.Date.Year != 1979 { t.Errorf("expected year 1979, got %d", obj.Date.Year) } if obj.Date.Month != 5 { t.Errorf("expected month 5, got %d", obj.Date.Month) } if obj.Date.Day != 27 { t.Errorf("expected day 27, got %d", obj.Date.Day) } }) t.Run("ToLocalDate", func(t *testing.T) { type dateStruct struct { Date time.Time } doc := `date = 1979-05-27` var obj dateStruct err := toml.Unmarshal([]byte(doc), &obj) if err != nil { t.Fatal(err) } if obj.Date.Year() != 1979 { t.Errorf("expected year 1979, got %d", obj.Date.Year()) } if obj.Date.Month() != 5 { t.Errorf("expected month 5, got %d", obj.Date.Month()) } if obj.Date.Day() != 27 { t.Errorf("expected day 27, got %d", obj.Date.Day()) } }) } func TestUnmarshalLocalDateTime(t *testing.T) { examples := []struct { name string in string out toml.LocalDateTime }{ { name: "normal", in: "1979-05-27T07:32:00", out: toml.LocalDateTime{ LocalDate: toml.LocalDate{ Year: 1979, Month: 5, Day: 27, }, LocalTime: toml.LocalTime{ Hour: 7, Minute: 32, Second: 0, Nanosecond: 0, }, }, }, { name: "with nanoseconds", in: "1979-05-27T00:32:00.999999", out: toml.LocalDateTime{ LocalDate: toml.LocalDate{ Year: 1979, Month: 5, Day: 27, }, LocalTime: toml.LocalTime{ Hour: 0, Minute: 32, Second: 0, Nanosecond: 999999000, Precision: 6, }, }, }, } for i, example := range examples { doc := fmt.Sprintf(`date = %s`, example.in) t.Run(fmt.Sprintf("ToLocalDateTime_%d_%s", i, example.name), func(t *testing.T) { type dateStruct struct { Date toml.LocalDateTime } var obj dateStruct err := toml.Unmarshal([]byte(doc), &obj) if err != nil { t.Fatal(err) } if obj.Date != example.out { t.Errorf("expected '%s', got '%s'", example.out, obj.Date) } }) t.Run(fmt.Sprintf("ToTime_%d_%s", i, example.name), func(t *testing.T) { type dateStruct struct { Date time.Time } var obj dateStruct err := toml.Unmarshal([]byte(doc), &obj) if err != nil { t.Fatal(err) } if obj.Date.Year() != example.out.Year { t.Errorf("expected year %d, got %d", example.out.Year, obj.Date.Year()) } if obj.Date.Month() != time.Month(example.out.Month) { t.Errorf("expected month %d, got %d", example.out.Month, obj.Date.Month()) } if obj.Date.Day() != example.out.Day { t.Errorf("expected day %d, got %d", example.out.Day, obj.Date.Day()) } if obj.Date.Hour() != example.out.Hour { t.Errorf("expected hour %d, got %d", example.out.Hour, obj.Date.Hour()) } if obj.Date.Minute() != example.out.Minute { t.Errorf("expected minute %d, got %d", example.out.Minute, obj.Date.Minute()) } if obj.Date.Second() != example.out.Second { t.Errorf("expected second %d, got %d", example.out.Second, obj.Date.Second()) } if obj.Date.Nanosecond() != example.out.Nanosecond { t.Errorf("expected nanoseconds %d, got %d", example.out.Nanosecond, obj.Date.Nanosecond()) } }) } } func TestUnmarshalLocalTime(t *testing.T) { examples := []struct { name string in string out toml.LocalTime }{ { name: "normal", in: "07:32:00", out: toml.LocalTime{ Hour: 7, Minute: 32, Second: 0, Nanosecond: 0, }, }, { name: "with nanoseconds", in: "00:32:00.999999", out: toml.LocalTime{ Hour: 0, Minute: 32, Second: 0, Nanosecond: 999999000, Precision: 6, }, }, } for i, example := range examples { doc := fmt.Sprintf(`Time = %s`, example.in) t.Run(fmt.Sprintf("ToLocalTime_%d_%s", i, example.name), func(t *testing.T) { type dateStruct struct { Time toml.LocalTime } var obj dateStruct err := toml.Unmarshal([]byte(doc), &obj) if err != nil { t.Fatal(err) } if obj.Time != example.out { t.Errorf("expected '%s', got '%s'", example.out, obj.Time) } }) } } // test case for issue #339 func TestUnmarshalSameInnerField(t *testing.T) { type InterStruct2 struct { Test string Name string Age int } type Inter2 struct { Name string Age int InterStruct2 InterStruct2 } type Server struct { Name string `toml:"name"` Inter2 Inter2 `toml:"inter2"` } var server Server if err := toml.Unmarshal([]byte(`name = "123" [inter2] name = "inter2" age = 222`), &server); err == nil { expected := Server{ Name: "123", Inter2: Inter2{ Name: "inter2", Age: 222, }, } if !reflect.DeepEqual(server, expected) { t.Errorf("Bad unmarshal: expected %v, got %v", expected, server) } } else { t.Fatalf("unexpected error: %v", err) } } func TestUnmarshalToNilInterface(t *testing.T) { doc := []byte(` PrimitiveField = "Hello" ArrayField = [1,2,3] InterfacePointerField = "World" [StructField] Field1 = 123 Field2 = "Field2" [MapField] MapField1 = [4,5,6] MapField2 = {A = "A"} MapField3 = false [[StructArrayField]] Name = "Allen" Age = 20 [[StructArrayField]] Name = "Jack" Age = 23 `) type OuterStruct struct { PrimitiveField interface{} ArrayField interface{} StructArrayField interface{} MapField map[string]interface{} StructField interface{} NilField interface{} InterfacePointerField *interface{} } var s interface{} = "World" expected := OuterStruct{ PrimitiveField: "Hello", ArrayField: []interface{}{int64(1), int64(2), int64(3)}, StructField: map[string]interface{}{ "Field1": int64(123), "Field2": "Field2", }, MapField: map[string]interface{}{ "MapField1": []interface{}{int64(4), int64(5), int64(6)}, "MapField2": map[string]interface{}{ "A": "A", }, "MapField3": false, }, NilField: nil, InterfacePointerField: &s, StructArrayField: []interface{}{ map[string]interface{}{ "Name": "Allen", "Age": int64(20), }, map[string]interface{}{ "Name": "Jack", "Age": int64(23), }, }, } actual := OuterStruct{} err := toml.Unmarshal(doc, &actual) require.NoError(t, err) assert.Equal(t, expected, actual) } func TestUnmarshalToNonNilInterface(t *testing.T) { doc := []byte(` PrimitiveField = "Allen" ArrayField = [1,2,3] [StructField] InnerField = "After1" [PointerField] InnerField = "After2" [InterfacePointerField] InnerField = "After" [MapField] MapField1 = [4,5,6] MapField2 = {A = "A"} MapField3 = false [[StructArrayField]] InnerField = "After3" [[StructArrayField]] InnerField = "After4" `) type InnerStruct struct { InnerField interface{} } type OuterStruct struct { PrimitiveField interface{} ArrayField interface{} StructArrayField interface{} MapField map[string]interface{} StructField interface{} PointerField interface{} NilField interface{} InterfacePointerField *interface{} } var s interface{} = InnerStruct{"After"} expected := OuterStruct{ PrimitiveField: "Allen", ArrayField: []interface{}{int64(1), int64(2), int64(3)}, StructField: map[string]interface{}{"InnerField": "After1"}, MapField: map[string]interface{}{ "MapField1": []interface{}{int64(4), int64(5), int64(6)}, "MapField2": map[string]interface{}{ "A": "A", }, "MapField3": false, }, PointerField: map[string]interface{}{"InnerField": "After2"}, NilField: nil, InterfacePointerField: &s, StructArrayField: []interface{}{ map[string]interface{}{"InnerField": "After3"}, map[string]interface{}{"InnerField": "After4"}, }, } actual := OuterStruct{ PrimitiveField: "aaa", ArrayField: []int{100, 200, 300, 400}, StructField: InnerStruct{InnerField: "Before1"}, MapField: map[string]interface{}{ "MapField1": []int{4, 5, 6}, "MapField2": map[string]string{ "B": "BBB", }, "MapField3": true, }, PointerField: &InnerStruct{InnerField: "Before2"}, NilField: nil, InterfacePointerField: &s, StructArrayField: []InnerStruct{ {InnerField: "Before3"}, {InnerField: "Before4"}, }, } err := toml.Unmarshal(doc, &actual) require.NoError(t, err) assert.Equal(t, expected, actual) } func TestUnmarshalNil(t *testing.T) { assert.Error(t, toml.Unmarshal([]byte(`whatever = "whatever"`), nil)) assert.Error(t, toml.Unmarshal([]byte(`whatever = "whatever"`), (*struct{})(nil))) } var sliceTomlDemo = []byte(`str_slice = ["Howdy","Hey There"] str_slice_ptr= ["Howdy","Hey There"] int_slice=[1,2] int_slice_ptr=[1,2] [[struct_slice]] String2="1" [[struct_slice]] String2="2" [[struct_slice_ptr]] String2="1" [[struct_slice_ptr]] String2="2" `) type sliceStruct struct { Slice []string ` toml:"str_slice" ` SlicePtr *[]string ` toml:"str_slice_ptr" ` IntSlice []int ` toml:"int_slice" ` IntSlicePtr *[]int ` toml:"int_slice_ptr" ` StructSlice []basicMarshalTestSubStruct ` toml:"struct_slice" ` StructSlicePtr *[]basicMarshalTestSubStruct ` toml:"struct_slice_ptr" ` } type arrayStruct struct { Slice [4]string ` toml:"str_slice" ` SlicePtr *[4]string ` toml:"str_slice_ptr" ` IntSlice [4]int ` toml:"int_slice" ` IntSlicePtr *[4]int ` toml:"int_slice_ptr" ` StructSlice [4]basicMarshalTestSubStruct ` toml:"struct_slice" ` StructSlicePtr *[4]basicMarshalTestSubStruct ` toml:"struct_slice_ptr" ` } type arrayTooSmallStruct struct { Slice [1]string ` toml:"str_slice" ` StructSlice [1]basicMarshalTestSubStruct ` toml:"struct_slice" ` } func TestUnmarshalSlice(t *testing.T) { var actual sliceStruct err := toml.Unmarshal(sliceTomlDemo, &actual) require.NoError(t, err) expected := sliceStruct{ Slice: []string{"Howdy", "Hey There"}, SlicePtr: &[]string{"Howdy", "Hey There"}, IntSlice: []int{1, 2}, IntSlicePtr: &[]int{1, 2}, StructSlice: []basicMarshalTestSubStruct{{"1"}, {"2"}}, StructSlicePtr: &[]basicMarshalTestSubStruct{{"1"}, {"2"}}, } assert.Equal(t, expected, actual) } func TestUnmarshalSliceFail(t *testing.T) { var actual sliceStruct assert.Error(t, toml.Unmarshal([]byte(`str_slice = [1, 2]`), &actual)) } func TestUnmarshalSliceFail2(t *testing.T) { doc := `str_slice=[1,2]` var actual sliceStruct assert.Error(t, toml.Unmarshal([]byte(doc), &actual)) } func TestUnmarshalMixedTypeSlice(t *testing.T) { type TestStruct struct { ArrayField []interface{} } //doc := []byte(`ArrayField = [3.14,100,true,"hello world",{Field = "inner1"},[{Field = "inner2"},{Field = "inner3"}]] //`) doc := []byte(`ArrayField = [{Field = "inner1"},[{Field = "inner2"},{Field = "inner3"}]] `) actual := TestStruct{} expected := TestStruct{ ArrayField: []interface{}{ //3.14, //int64(100), //true, //"hello world", map[string]interface{}{ "Field": "inner1", }, []interface{}{ map[string]interface{}{"Field": "inner2"}, map[string]interface{}{"Field": "inner3"}, }, }, } err := toml.Unmarshal(doc, &actual) require.NoError(t, err) assert.Equal(t, expected, actual) } func TestUnmarshalArray(t *testing.T) { var err error var actual arrayStruct err = toml.Unmarshal(sliceTomlDemo, &actual) require.NoError(t, err) expected := arrayStruct{ Slice: [4]string{"Howdy", "Hey There"}, SlicePtr: &[4]string{"Howdy", "Hey There"}, IntSlice: [4]int{1, 2}, IntSlicePtr: &[4]int{1, 2}, StructSlice: [4]basicMarshalTestSubStruct{{"1"}, {"2"}}, StructSlicePtr: &[4]basicMarshalTestSubStruct{{"1"}, {"2"}}, } assert.Equal(t, expected, actual) } func TestUnmarshalArrayFail3(t *testing.T) { doc := `[[struct_slice]] String2="1" [[struct_slice]] String2="2"` var actual arrayTooSmallStruct err := toml.Unmarshal([]byte(doc), &actual) assert.Error(t, err) } func decoder(doc string) *toml.Decoder { return toml.NewDecoder(bytes.NewReader([]byte(doc))) } func strictDecoder(doc string) *toml.Decoder { d := decoder(doc) d.DisallowUnknownFields() return d } func TestDecoderStrict(t *testing.T) { input := ` [decoded] key = "" [undecoded] key = "" [undecoded.inner] key = "" [[undecoded.array]] key = "" [[undecoded.array]] key = "" ` var doc struct { Decoded struct { Key string } } err := strictDecoder(input).Decode(&doc) require.Error(t, err) require.IsType(t, &toml.StrictMissingError{}, err) se := err.(*toml.StrictMissingError) keys := []toml.Key{} for _, e := range se.Errors { keys = append(keys, e.Key()) } expectedKeys := []toml.Key{ {"undecoded"}, {"undecoded", "inner"}, {"undecoded", "array"}, {"undecoded", "array"}, } require.Equal(t, expectedKeys, keys) err = decoder(input).Decode(&doc) require.NoError(t, err) var m map[string]interface{} err = decoder(input).Decode(&m) } func TestDecoderStrictValid(t *testing.T) { input := ` [decoded] key = "" ` var doc struct { Decoded struct { Key string } } err := strictDecoder(input).Decode(&doc) require.NoError(t, err) } type docUnmarshalTOML struct { Decoded struct { Key string } } func (d *docUnmarshalTOML) UnmarshalTOML(i interface{}) error { if iMap, ok := i.(map[string]interface{}); !ok { return fmt.Errorf("type assertion error: wants %T, have %T", map[string]interface{}{}, i) } else if key, ok := iMap["key"]; !ok { return fmt.Errorf("key '%s' not in map", "key") } else if keyString, ok := key.(string); !ok { return fmt.Errorf("type assertion error: wants %T, have %T", "", key) } else { d.Decoded.Key = keyString } return nil } func TestDecoderStrictCustomUnmarshal(t *testing.T) { t.Skip() //input := `key = "ok"` //var doc docUnmarshalTOML //err := NewDecoder(bytes.NewReader([]byte(input))).Strict(true).Decode(&doc) //if err != nil { // t.Fatal("unexpected error:", err) //} //if doc.Decoded.Key != "ok" { // t.Errorf("Bad unmarshal: expected ok, got %v", doc.Decoded.Key) //} } type parent struct { Doc docUnmarshalTOML DocPointer *docUnmarshalTOML } func TestCustomUnmarshal(t *testing.T) { t.Skip("not sure if UnmarshalTOML is a good idea") input := ` [Doc] key = "ok1" [DocPointer] key = "ok2" ` var d parent err := toml.Unmarshal([]byte(input), &d) require.NoError(t, err) assert.Equal(t, "ok1", d.Doc.Decoded.Key) assert.Equal(t, "ok2", d.DocPointer.Decoded.Key) } func TestCustomUnmarshalError(t *testing.T) { t.Skip("not sure if UnmarshalTOML is a good idea") input := ` [Doc] key = 1 [DocPointer] key = "ok2" ` expected := "(2, 1): unmarshal toml: type assertion error: wants string, have int64" var d parent err := toml.Unmarshal([]byte(input), &d) if err == nil { t.Error("expected error, got none") } else if err.Error() != expected { t.Errorf("expect err: %s, got: %s", expected, err.Error()) } } type intWrapper struct { Value int } func (w *intWrapper) UnmarshalText(text []byte) error { var err error if w.Value, err = strconv.Atoi(string(text)); err == nil { return nil } if b, err := strconv.ParseBool(string(text)); err == nil { if b { w.Value = 1 } return nil } if f, err := strconv.ParseFloat(string(text), 32); err == nil { w.Value = int(f) return nil } return fmt.Errorf("unsupported: %s", text) } func TestTextUnmarshal(t *testing.T) { var doc struct { UnixTime intWrapper Version *intWrapper Bool intWrapper Int intWrapper Float intWrapper } input := ` UnixTime = "12" Version = "42" Bool = true Int = 21 Float = 2.0 ` err := toml.Unmarshal([]byte(input), &doc) require.NoError(t, err) assert.Equal(t, 12, doc.UnixTime.Value) assert.Equal(t, 42, doc.Version.Value) assert.Equal(t, 1, doc.Bool.Value) assert.Equal(t, 21, doc.Int.Value) assert.Equal(t, 2, doc.Float.Value) } func TestTextUnmarshalError(t *testing.T) { var doc struct { Failer intWrapper } input := `Failer = "hello"` if err := toml.Unmarshal([]byte(input), &doc); err == nil { t.Fatalf("expected err, got none") } } // issue406 func TestPreserveNotEmptyField(t *testing.T) { doc := []byte(`Field1 = "ccc"`) type Inner struct { InnerField1 string InnerField2 int } type TestStruct struct { Field1 string Field2 int Field3 Inner } actual := TestStruct{ "aaa", 100, Inner{ "bbb", 200, }, } expected := TestStruct{ "ccc", 100, Inner{ "bbb", 200, }, } err := toml.Unmarshal(doc, &actual) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(actual, expected) { t.Errorf("Bad unmarshal: expected %+v, got %+v", expected, actual) } } // github issue 432 func TestUnmarshalEmptyInterface(t *testing.T) { doc := []byte(`User = "pelletier"`) var v interface{} err := toml.Unmarshal(doc, &v) if err != nil { t.Fatal(err) } require.IsType(t, map[string]interface{}{}, v) x := v.(map[string]interface{}) assert.Equal(t, "pelletier", x["User"]) } func TestUnmarshalEmptyInterfaceDeep(t *testing.T) { t.Skipf("TODO") doc := []byte(` User = "pelletier" Age = 99 [foo] bar = 42 `) var v interface{} err := toml.Unmarshal(doc, &v) if err != nil { t.Fatal(err) } x, ok := v.(map[string]interface{}) if !ok { t.Fatal(err) } expected := map[string]interface{}{ "User": "pelletier", "Age": 99, "foo": map[string]interface{}{ "bar": 42, }, } reflect.DeepEqual(x, expected) } type Config struct { Key string `toml:"key"` Obj Custom `toml:"obj"` } type Custom struct { v string } func (c *Custom) UnmarshalTOML(v interface{}) error { c.v = "called" return nil } func TestGithubIssue431(t *testing.T) { doc := `key = "value"` var c Config if err := toml.Unmarshal([]byte(doc), &c); err != nil { t.Fatalf("unexpected error: %s", err) } if c.Key != "value" { t.Errorf("expected c.Key='value', not '%s'", c.Key) } if c.Obj.v == "called" { t.Errorf("UnmarshalTOML should not have been called") } } type durationString struct { time.Duration } func (d *durationString) UnmarshalTOML(v interface{}) error { d.Duration = 10 * time.Second return nil } type config437Error struct{} func (e *config437Error) UnmarshalTOML(v interface{}) error { return errors.New("expected") } type config437 struct { HTTP struct { PingTimeout durationString `toml:"PingTimeout"` ErrorField config437Error } `toml:"HTTP"` } func TestGithubIssue437(t *testing.T) { t.Skipf("unmarshalTOML not implemented") src := ` [HTTP] PingTimeout = "32m" ` cfg := &config437{} cfg.HTTP.PingTimeout = durationString{time.Second} err := toml.Unmarshal([]byte(src), cfg) if err != nil { t.Fatalf("unexpected errors %s", err) } expected := durationString{10 * time.Second} if cfg.HTTP.PingTimeout != expected { t.Fatalf("expected '%s', got '%s'", expected, cfg.HTTP.PingTimeout) } } func TestLeafUnmarshalerError(t *testing.T) { src := ` [HTTP] ErrorField = "foo" ` cfg := &config437{} err := toml.Unmarshal([]byte(src), cfg) if err == nil { t.Fatalf("error was expected") } } go-toml-2.1.1/internal/testsuite/000077500000000000000000000000001453566013500167465ustar00rootroot00000000000000go-toml-2.1.1/internal/testsuite/add.go000066400000000000000000000035521453566013500200320ustar00rootroot00000000000000package testsuite import ( "fmt" "math" "time" "github.com/pelletier/go-toml/v2" ) // addTag adds JSON tags to a data structure as expected by toml-test. func addTag(key string, tomlData interface{}) interface{} { // Switch on the data type. switch orig := tomlData.(type) { default: //return map[string]interface{}{} panic(fmt.Sprintf("Unknown type: %T", tomlData)) // A table: we don't need to add any tags, just recurse for every table // entry. case map[string]interface{}: typed := make(map[string]interface{}, len(orig)) for k, v := range orig { typed[k] = addTag(k, v) } return typed // An array: we don't need to add any tags, just recurse for every table // entry. case []map[string]interface{}: typed := make([]map[string]interface{}, len(orig)) for i, v := range orig { typed[i] = addTag("", v).(map[string]interface{}) } return typed case []interface{}: typed := make([]interface{}, len(orig)) for i, v := range orig { typed[i] = addTag("", v) } return typed // Datetime: tag as datetime. case toml.LocalTime: return tag("time-local", orig.String()) case toml.LocalDate: return tag("date-local", orig.String()) case toml.LocalDateTime: return tag("datetime-local", orig.String()) case time.Time: return tag("datetime", orig.Format("2006-01-02T15:04:05.999999999Z07:00")) // Tag primitive values: bool, string, int, and float64. case bool: return tag("bool", fmt.Sprintf("%v", orig)) case string: return tag("string", orig) case int64: return tag("integer", fmt.Sprintf("%d", orig)) case float64: // Special case for nan since NaN == NaN is false. if math.IsNaN(orig) { return tag("float", "nan") } return tag("float", fmt.Sprintf("%v", orig)) } } func tag(typeName string, data interface{}) map[string]interface{} { return map[string]interface{}{ "type": typeName, "value": data, } } go-toml-2.1.1/internal/testsuite/json.go000066400000000000000000000143051453566013500202510ustar00rootroot00000000000000package testsuite import ( "fmt" "strconv" "strings" "testing" "time" ) func CmpJSON(t *testing.T, key string, want, have interface{}) { switch w := want.(type) { case map[string]interface{}: cmpJSONMaps(t, key, w, have) case []interface{}: cmpJSONArrays(t, key, w, have) default: t.Errorf( "Key '%s' in expected output should be a map or a list of maps, but it's a %T", key, want) } } func cmpJSONMaps(t *testing.T, key string, want map[string]interface{}, have interface{}) { haveMap, ok := have.(map[string]interface{}) if !ok { mismatch(t, key, "table", want, haveMap) return } // Check to make sure both or neither are values. if isValue(want) && !isValue(haveMap) { t.Fatalf("Key '%s' is supposed to be a value, but the parser reports it as a table", key) } if !isValue(want) && isValue(haveMap) { t.Fatalf("Key '%s' is supposed to be a table, but the parser reports it as a value", key) } if isValue(want) && isValue(haveMap) { cmpJSONValues(t, key, want, haveMap) return } // Check that the keys of each map are equivalent. for k := range want { if _, ok := haveMap[k]; !ok { bunk := kjoin(key, k) t.Fatalf("Could not find key '%s' in parser output.", bunk) } } for k := range haveMap { if _, ok := want[k]; !ok { bunk := kjoin(key, k) t.Fatalf("Could not find key '%s' in expected output.", bunk) } } // Okay, now make sure that each value is equivalent. for k := range want { CmpJSON(t, kjoin(key, k), want[k], haveMap[k]) } } func cmpJSONArrays(t *testing.T, key string, want, have interface{}) { wantSlice, ok := want.([]interface{}) if !ok { panic(fmt.Sprintf("'value' should be a JSON array when 'type=array', but it is a %T", want)) } haveSlice, ok := have.([]interface{}) if !ok { t.Fatalf("Malformed output from your encoder: 'value' is not a JSON array: %T", have) } if len(wantSlice) != len(haveSlice) { t.Fatalf("Array lengths differ for key '%s':\n"+ " Expected: %d\n"+ " Your encoder: %d", key, len(wantSlice), len(haveSlice)) } for i := 0; i < len(wantSlice); i++ { CmpJSON(t, key, wantSlice[i], haveSlice[i]) } } func cmpJSONValues(t *testing.T, key string, want, have map[string]interface{}) { wantType, ok := want["type"].(string) if !ok { panic(fmt.Sprintf("'type' should be a string, but it is a %T", want["type"])) } haveType, ok := have["type"].(string) if !ok { t.Fatalf("Malformed output from your encoder: 'type' is not a string: %T", have["type"]) } if wantType != haveType { valMismatch(t, key, wantType, haveType, want, have) } // If this is an array, then we've got to do some work to check equality. if wantType == "array" { cmpJSONArrays(t, key, want, have) return } // Atomic values are always strings wantVal, ok := want["value"].(string) if !ok { panic(fmt.Sprintf("'value' %v should be a string, but it is a %[1]T", want["value"])) } haveVal, ok := have["value"].(string) if !ok { panic(fmt.Sprintf("Malformed output from your encoder: %T is not a string", have["value"])) } // Excepting floats and datetimes, other values can be compared as strings. switch wantType { case "float": cmpFloats(t, key, wantVal, haveVal) case "datetime", "datetime-local", "date-local", "time-local": cmpAsDatetimes(t, key, wantType, wantVal, haveVal) default: cmpAsStrings(t, key, wantVal, haveVal) } } func cmpAsStrings(t *testing.T, key string, want, have string) { if want != have { t.Fatalf("Values for key '%s' don't match:\n"+ " Expected: %s\n"+ " Your encoder: %s", key, want, have) } } func cmpFloats(t *testing.T, key string, want, have string) { // Special case for NaN, since NaN != NaN. if strings.HasSuffix(want, "nan") || strings.HasSuffix(have, "nan") { if want != have { t.Fatalf("Values for key '%s' don't match:\n"+ " Expected: %v\n"+ " Your encoder: %v", key, want, have) } return } wantF, err := strconv.ParseFloat(want, 64) if err != nil { panic(fmt.Sprintf("Could not read '%s' as a float value for key '%s'", want, key)) } haveF, err := strconv.ParseFloat(have, 64) if err != nil { panic(fmt.Sprintf("Malformed output from your encoder: key '%s' is not a float: '%s'", key, have)) } if wantF != haveF { t.Fatalf("Values for key '%s' don't match:\n"+ " Expected: %v\n"+ " Your encoder: %v", key, wantF, haveF) } } var datetimeRepl = strings.NewReplacer( " ", "T", "t", "T", "z", "Z") var layouts = map[string]string{ "datetime": time.RFC3339Nano, "datetime-local": "2006-01-02T15:04:05.999999999", "date-local": "2006-01-02", "time-local": "15:04:05", } func cmpAsDatetimes(t *testing.T, key string, kind, want, have string) { layout, ok := layouts[kind] if !ok { panic("should never happen") } wantT, err := time.Parse(layout, datetimeRepl.Replace(want)) if err != nil { panic(fmt.Sprintf("Could not read '%s' as a datetime value for key '%s'", want, key)) } haveT, err := time.Parse(layout, datetimeRepl.Replace(want)) if err != nil { t.Fatalf("Malformed output from your encoder: key '%s' is not a datetime: '%s'", key, have) return } if !wantT.Equal(haveT) { t.Fatalf("Values for key '%s' don't match:\n"+ " Expected: %v\n"+ " Your encoder: %v", key, wantT, haveT) } } func cmpAsDatetimesLocal(t *testing.T, key string, want, have string) { if datetimeRepl.Replace(want) != datetimeRepl.Replace(have) { t.Fatalf("Values for key '%s' don't match:\n"+ " Expected: %v\n"+ " Your encoder: %v", key, want, have) } } func kjoin(old, key string) string { if len(old) == 0 { return key } return old + "." + key } func isValue(m map[string]interface{}) bool { if len(m) != 2 { return false } if _, ok := m["type"]; !ok { return false } if _, ok := m["value"]; !ok { return false } return true } func mismatch(t *testing.T, key string, wantType string, want, have interface{}) { t.Fatalf("Key '%s' is not an %s but %[4]T:\n"+ " Expected: %#[3]v\n"+ " Your encoder: %#[4]v", key, wantType, want, have) } func valMismatch(t *testing.T, key string, wantType, haveType string, want, have interface{}) { t.Fatalf("Key '%s' is not an %s but %s:\n"+ " Expected: %#[3]v\n"+ " Your encoder: %#[4]v", key, wantType, want, have) } go-toml-2.1.1/internal/testsuite/parser.go000066400000000000000000000023241453566013500205720ustar00rootroot00000000000000package testsuite import ( "bytes" "encoding/json" "fmt" "github.com/pelletier/go-toml/v2" ) type parser struct{} func (p parser) Decode(input string) (output string, outputIsError bool, retErr error) { defer func() { if r := recover(); r != nil { switch rr := r.(type) { case error: retErr = rr default: retErr = fmt.Errorf("%s", rr) } } }() var v interface{} if err := toml.Unmarshal([]byte(input), &v); err != nil { return err.Error(), true, nil } j, err := json.MarshalIndent(addTag("", v), "", " ") if err != nil { return "", false, retErr } return string(j), false, retErr } func (p parser) Encode(input string) (output string, outputIsError bool, retErr error) { defer func() { if r := recover(); r != nil { switch rr := r.(type) { case error: retErr = rr default: retErr = fmt.Errorf("%s", rr) } } }() var tmp interface{} err := json.Unmarshal([]byte(input), &tmp) if err != nil { return "", false, err } rm, err := rmTag(tmp) if err != nil { return err.Error(), true, retErr } buf := new(bytes.Buffer) err = toml.NewEncoder(buf).Encode(rm) if err != nil { return err.Error(), true, retErr } return buf.String(), false, retErr } go-toml-2.1.1/internal/testsuite/rm.go000066400000000000000000000057101453566013500177160ustar00rootroot00000000000000package testsuite import ( "fmt" "strconv" "time" "github.com/pelletier/go-toml/v2" ) // Remove JSON tags to a data structure as returned by toml-test. func rmTag(typedJson interface{}) (interface{}, error) { // Check if key is in the table m. in := func(key string, m map[string]interface{}) bool { _, ok := m[key] return ok } // Switch on the data type. switch v := typedJson.(type) { // Object: this can either be a TOML table or a primitive with tags. case map[string]interface{}: // This value represents a primitive: remove the tags and return just // the primitive value. if len(v) == 2 && in("type", v) && in("value", v) { ut, err := untag(v) if err != nil { return ut, fmt.Errorf("tag.Remove: %w", err) } return ut, nil } // Table: remove tags on all children. m := make(map[string]interface{}, len(v)) for k, v2 := range v { var err error m[k], err = rmTag(v2) if err != nil { return nil, err } } return m, nil // Array: remove tags from all itenm. case []interface{}: a := make([]interface{}, len(v)) for i := range v { var err error a[i], err = rmTag(v[i]) if err != nil { return nil, err } } return a, nil } // The top level must be an object or array. return nil, fmt.Errorf("unrecognized JSON format '%T'", typedJson) } // Return a primitive: read the "type" and convert the "value" to that. func untag(typed map[string]interface{}) (interface{}, error) { t := typed["type"].(string) v := typed["value"].(string) switch t { case "string": return v, nil case "integer": n, err := strconv.ParseInt(v, 10, 64) if err != nil { return nil, fmt.Errorf("untag: %w", err) } return n, nil case "float": f, err := strconv.ParseFloat(v, 64) if err != nil { return nil, fmt.Errorf("untag: %w", err) } return f, nil //toml.LocalDate{Year:2020, Month:12, Day:12} case "datetime": return time.Parse("2006-01-02T15:04:05.999999999Z07:00", v) case "datetime-local": var t toml.LocalDateTime err := t.UnmarshalText([]byte(v)) if err != nil { return nil, fmt.Errorf("untag: %w", err) } return t, nil case "date-local": var t toml.LocalDate err := t.UnmarshalText([]byte(v)) if err != nil { return nil, fmt.Errorf("untag: %w", err) } return t, nil case "time-local": var t toml.LocalTime err := t.UnmarshalText([]byte(v)) if err != nil { return nil, fmt.Errorf("untag: %w", err) } return t, nil case "bool": switch v { case "true": return true, nil case "false": return false, nil } return nil, fmt.Errorf("untag: could not parse %q as a boolean", v) } return nil, fmt.Errorf("untag: unrecognized tag type %q", t) } func parseTime(v, format string, local bool) (t time.Time, err error) { if local { t, err = time.ParseInLocation(format, v, time.Local) } else { t, err = time.Parse(format, v) } if err != nil { return time.Time{}, fmt.Errorf("Could not parse %q as a datetime: %w", v, err) } return t, nil } go-toml-2.1.1/internal/testsuite/testsuite.go000066400000000000000000000034111453566013500213250ustar00rootroot00000000000000// Package testsuite provides helper functions for interoperating with the // language-agnostic TOML test suite at github.com/BurntSushi/toml-test. package testsuite import ( "encoding/json" "fmt" "os" "github.com/pelletier/go-toml/v2" ) // Marshal is a helpfer function for calling toml.Marshal // // Only needed to avoid package import loops. func Marshal(v interface{}) ([]byte, error) { return toml.Marshal(v) } // Unmarshal is a helper function for calling toml.Unmarshal. // // Only needed to avoid package import loops. func Unmarshal(data []byte, v interface{}) error { return toml.Unmarshal(data, v) } // ValueToTaggedJSON takes a data structure and returns the tagged JSON // representation. func ValueToTaggedJSON(doc interface{}) ([]byte, error) { return json.MarshalIndent(addTag("", doc), "", " ") } // DecodeStdin is a helper function for the toml-test binary interface. TOML input // is read from STDIN and a resulting tagged JSON representation is written to // STDOUT. func DecodeStdin() error { var decoded map[string]interface{} if err := toml.NewDecoder(os.Stdin).Decode(&decoded); err != nil { return fmt.Errorf("Error decoding TOML: %s", err) } j := json.NewEncoder(os.Stdout) j.SetIndent("", " ") if err := j.Encode(addTag("", decoded)); err != nil { return fmt.Errorf("Error encoding JSON: %s", err) } return nil } // EncodeStdin is a helper function for the toml-test binary interface. Tagged // JSON is read from STDIN and a resulting TOML representation is written to // STDOUT. func EncodeStdin() error { var j interface{} err := json.NewDecoder(os.Stdin).Decode(&j) if err != nil { return err } rm, err := rmTag(j) if err != nil { return fmt.Errorf("removing tags: %w", err) } return toml.NewEncoder(os.Stdout).Encode(rm) } go-toml-2.1.1/internal/tracker/000077500000000000000000000000001453566013500163505ustar00rootroot00000000000000go-toml-2.1.1/internal/tracker/key.go000066400000000000000000000017521453566013500174740ustar00rootroot00000000000000package tracker import "github.com/pelletier/go-toml/v2/unstable" // KeyTracker is a tracker that keeps track of the current Key as the AST is // walked. type KeyTracker struct { k []string } // UpdateTable sets the state of the tracker with the AST table node. func (t *KeyTracker) UpdateTable(node *unstable.Node) { t.reset() t.Push(node) } // UpdateArrayTable sets the state of the tracker with the AST array table node. func (t *KeyTracker) UpdateArrayTable(node *unstable.Node) { t.reset() t.Push(node) } // Push the given key on the stack. func (t *KeyTracker) Push(node *unstable.Node) { it := node.Key() for it.Next() { t.k = append(t.k, string(it.Node().Data)) } } // Pop key from stack. func (t *KeyTracker) Pop(node *unstable.Node) { it := node.Key() for it.Next() { t.k = t.k[:len(t.k)-1] } } // Key returns the current key func (t *KeyTracker) Key() []string { k := make([]string, len(t.k)) copy(k, t.k) return k } func (t *KeyTracker) reset() { t.k = t.k[:0] } go-toml-2.1.1/internal/tracker/seen.go000066400000000000000000000174121453566013500176360ustar00rootroot00000000000000package tracker import ( "bytes" "fmt" "sync" "github.com/pelletier/go-toml/v2/unstable" ) type keyKind uint8 const ( invalidKind keyKind = iota valueKind tableKind arrayTableKind ) func (k keyKind) String() string { switch k { case invalidKind: return "invalid" case valueKind: return "value" case tableKind: return "table" case arrayTableKind: return "array table" } panic("missing keyKind string mapping") } // SeenTracker tracks which keys have been seen with which TOML type to flag // duplicates and mismatches according to the spec. // // Each node in the visited tree is represented by an entry. Each entry has an // identifier, which is provided by a counter. Entries are stored in the array // entries. As new nodes are discovered (referenced for the first time in the // TOML document), entries are created and appended to the array. An entry // points to its parent using its id. // // To find whether a given key (sequence of []byte) has already been visited, // the entries are linearly searched, looking for one with the right name and // parent id. // // Given that all keys appear in the document after their parent, it is // guaranteed that all descendants of a node are stored after the node, this // speeds up the search process. // // When encountering [[array tables]], the descendants of that node are removed // to allow that branch of the tree to be "rediscovered". To maintain the // invariant above, the deletion process needs to keep the order of entries. // This results in more copies in that case. type SeenTracker struct { entries []entry currentIdx int } var pool sync.Pool func (s *SeenTracker) reset() { // Always contains a root element at index 0. s.currentIdx = 0 if len(s.entries) == 0 { s.entries = make([]entry, 1, 2) } else { s.entries = s.entries[:1] } s.entries[0].child = -1 s.entries[0].next = -1 } type entry struct { // Use -1 to indicate no child or no sibling. child int next int name []byte kind keyKind explicit bool kv bool } // Find the index of the child of parentIdx with key k. Returns -1 if // it does not exist. func (s *SeenTracker) find(parentIdx int, k []byte) int { for i := s.entries[parentIdx].child; i >= 0; i = s.entries[i].next { if bytes.Equal(s.entries[i].name, k) { return i } } return -1 } // Remove all descendants of node at position idx. func (s *SeenTracker) clear(idx int) { if idx >= len(s.entries) { return } for i := s.entries[idx].child; i >= 0; { next := s.entries[i].next n := s.entries[0].next s.entries[0].next = i s.entries[i].next = n s.entries[i].name = nil s.clear(i) i = next } s.entries[idx].child = -1 } func (s *SeenTracker) create(parentIdx int, name []byte, kind keyKind, explicit bool, kv bool) int { e := entry{ child: -1, next: s.entries[parentIdx].child, name: name, kind: kind, explicit: explicit, kv: kv, } var idx int if s.entries[0].next >= 0 { idx = s.entries[0].next s.entries[0].next = s.entries[idx].next s.entries[idx] = e } else { idx = len(s.entries) s.entries = append(s.entries, e) } s.entries[parentIdx].child = idx return idx } func (s *SeenTracker) setExplicitFlag(parentIdx int) { for i := s.entries[parentIdx].child; i >= 0; i = s.entries[i].next { if s.entries[i].kv { s.entries[i].explicit = true s.entries[i].kv = false } s.setExplicitFlag(i) } } // CheckExpression takes a top-level node and checks that it does not contain // keys that have been seen in previous calls, and validates that types are // consistent. func (s *SeenTracker) CheckExpression(node *unstable.Node) error { if s.entries == nil { s.reset() } switch node.Kind { case unstable.KeyValue: return s.checkKeyValue(node) case unstable.Table: return s.checkTable(node) case unstable.ArrayTable: return s.checkArrayTable(node) default: panic(fmt.Errorf("this should not be a top level node type: %s", node.Kind)) } } func (s *SeenTracker) checkTable(node *unstable.Node) error { if s.currentIdx >= 0 { s.setExplicitFlag(s.currentIdx) } it := node.Key() parentIdx := 0 // This code is duplicated in checkArrayTable. This is because factoring // it in a function requires to copy the iterator, or allocate it to the // heap, which is not cheap. for it.Next() { if it.IsLast() { break } k := it.Node().Data idx := s.find(parentIdx, k) if idx < 0 { idx = s.create(parentIdx, k, tableKind, false, false) } else { entry := s.entries[idx] if entry.kind == valueKind { return fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind) } } parentIdx = idx } k := it.Node().Data idx := s.find(parentIdx, k) if idx >= 0 { kind := s.entries[idx].kind if kind != tableKind { return fmt.Errorf("toml: key %s should be a table, not a %s", string(k), kind) } if s.entries[idx].explicit { return fmt.Errorf("toml: table %s already exists", string(k)) } s.entries[idx].explicit = true } else { idx = s.create(parentIdx, k, tableKind, true, false) } s.currentIdx = idx return nil } func (s *SeenTracker) checkArrayTable(node *unstable.Node) error { if s.currentIdx >= 0 { s.setExplicitFlag(s.currentIdx) } it := node.Key() parentIdx := 0 for it.Next() { if it.IsLast() { break } k := it.Node().Data idx := s.find(parentIdx, k) if idx < 0 { idx = s.create(parentIdx, k, tableKind, false, false) } else { entry := s.entries[idx] if entry.kind == valueKind { return fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind) } } parentIdx = idx } k := it.Node().Data idx := s.find(parentIdx, k) if idx >= 0 { kind := s.entries[idx].kind if kind != arrayTableKind { return fmt.Errorf("toml: key %s already exists as a %s, but should be an array table", kind, string(k)) } s.clear(idx) } else { idx = s.create(parentIdx, k, arrayTableKind, true, false) } s.currentIdx = idx return nil } func (s *SeenTracker) checkKeyValue(node *unstable.Node) error { parentIdx := s.currentIdx it := node.Key() for it.Next() { k := it.Node().Data idx := s.find(parentIdx, k) if idx < 0 { idx = s.create(parentIdx, k, tableKind, false, true) } else { entry := s.entries[idx] if it.IsLast() { return fmt.Errorf("toml: key %s is already defined", string(k)) } else if entry.kind != tableKind { return fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind) } else if entry.explicit { return fmt.Errorf("toml: cannot redefine table %s that has already been explicitly defined", string(k)) } } parentIdx = idx } s.entries[parentIdx].kind = valueKind value := node.Value() switch value.Kind { case unstable.InlineTable: return s.checkInlineTable(value) case unstable.Array: return s.checkArray(value) } return nil } func (s *SeenTracker) checkArray(node *unstable.Node) error { it := node.Children() for it.Next() { n := it.Node() switch n.Kind { case unstable.InlineTable: err := s.checkInlineTable(n) if err != nil { return err } case unstable.Array: err := s.checkArray(n) if err != nil { return err } } } return nil } func (s *SeenTracker) checkInlineTable(node *unstable.Node) error { if pool.New == nil { pool.New = func() interface{} { return &SeenTracker{} } } s = pool.Get().(*SeenTracker) s.reset() it := node.Children() for it.Next() { n := it.Node() err := s.checkKeyValue(n) if err != nil { return err } } // As inline tables are self-contained, the tracker does not // need to retain the details of what they contain. The // keyValue element that creates the inline table is kept to // mark the presence of the inline table and prevent // redefinition of its keys: check* functions cannot walk into // a value. pool.Put(s) return nil } go-toml-2.1.1/internal/tracker/seen_test.go000066400000000000000000000006361453566013500206750ustar00rootroot00000000000000package tracker import ( "testing" "unsafe" "github.com/stretchr/testify/require" ) func TestEntrySize(t *testing.T) { // Validate no regression on the size of entry{}. This is a critical bit for // performance of unmarshaling documents. Should only be increased with care // and a very good reason. maxExpectedEntrySize := 48 require.LessOrEqual(t, int(unsafe.Sizeof(entry{})), maxExpectedEntrySize) } go-toml-2.1.1/internal/tracker/tracker.go000066400000000000000000000000201453566013500203220ustar00rootroot00000000000000package tracker go-toml-2.1.1/localtime.go000066400000000000000000000063541453566013500154110ustar00rootroot00000000000000package toml import ( "fmt" "strings" "time" "github.com/pelletier/go-toml/v2/unstable" ) // LocalDate represents a calendar day in no specific timezone. type LocalDate struct { Year int Month int Day int } // AsTime converts d into a specific time instance at midnight in zone. func (d LocalDate) AsTime(zone *time.Location) time.Time { return time.Date(d.Year, time.Month(d.Month), d.Day, 0, 0, 0, 0, zone) } // String returns RFC 3339 representation of d. func (d LocalDate) String() string { return fmt.Sprintf("%04d-%02d-%02d", d.Year, d.Month, d.Day) } // MarshalText returns RFC 3339 representation of d. func (d LocalDate) MarshalText() ([]byte, error) { return []byte(d.String()), nil } // UnmarshalText parses b using RFC 3339 to fill d. func (d *LocalDate) UnmarshalText(b []byte) error { res, err := parseLocalDate(b) if err != nil { return err } *d = res return nil } // LocalTime represents a time of day of no specific day in no specific // timezone. type LocalTime struct { Hour int // Hour of the day: [0; 24[ Minute int // Minute of the hour: [0; 60[ Second int // Second of the minute: [0; 60[ Nanosecond int // Nanoseconds within the second: [0, 1000000000[ Precision int // Number of digits to display for Nanosecond. } // String returns RFC 3339 representation of d. // If d.Nanosecond and d.Precision are zero, the time won't have a nanosecond // component. If d.Nanosecond > 0 but d.Precision = 0, then the minimum number // of digits for nanoseconds is provided. func (d LocalTime) String() string { s := fmt.Sprintf("%02d:%02d:%02d", d.Hour, d.Minute, d.Second) if d.Precision > 0 { s += fmt.Sprintf(".%09d", d.Nanosecond)[:d.Precision+1] } else if d.Nanosecond > 0 { // Nanoseconds are specified, but precision is not provided. Use the // minimum. s += strings.Trim(fmt.Sprintf(".%09d", d.Nanosecond), "0") } return s } // MarshalText returns RFC 3339 representation of d. func (d LocalTime) MarshalText() ([]byte, error) { return []byte(d.String()), nil } // UnmarshalText parses b using RFC 3339 to fill d. func (d *LocalTime) UnmarshalText(b []byte) error { res, left, err := parseLocalTime(b) if err == nil && len(left) != 0 { err = unstable.NewParserError(left, "extra characters") } if err != nil { return err } *d = res return nil } // LocalDateTime represents a time of a specific day in no specific timezone. type LocalDateTime struct { LocalDate LocalTime } // AsTime converts d into a specific time instance in zone. func (d LocalDateTime) AsTime(zone *time.Location) time.Time { return time.Date(d.Year, time.Month(d.Month), d.Day, d.Hour, d.Minute, d.Second, d.Nanosecond, zone) } // String returns RFC 3339 representation of d. func (d LocalDateTime) String() string { return d.LocalDate.String() + "T" + d.LocalTime.String() } // MarshalText returns RFC 3339 representation of d. func (d LocalDateTime) MarshalText() ([]byte, error) { return []byte(d.String()), nil } // UnmarshalText parses b using RFC 3339 to fill d. func (d *LocalDateTime) UnmarshalText(data []byte) error { res, left, err := parseLocalDateTime(data) if err == nil && len(left) != 0 { err = unstable.NewParserError(left, "extra characters") } if err != nil { return err } *d = res return nil } go-toml-2.1.1/localtime_test.go000066400000000000000000000061611453566013500164440ustar00rootroot00000000000000package toml_test import ( "testing" "time" "github.com/pelletier/go-toml/v2" "github.com/stretchr/testify/require" ) func TestLocalDate_AsTime(t *testing.T) { d := toml.LocalDate{2021, 6, 8} cast := d.AsTime(time.UTC) require.Equal(t, time.Date(2021, time.June, 8, 0, 0, 0, 0, time.UTC), cast) } func TestLocalDate_String(t *testing.T) { d := toml.LocalDate{2021, 6, 8} require.Equal(t, "2021-06-08", d.String()) } func TestLocalDate_MarshalText(t *testing.T) { d := toml.LocalDate{2021, 6, 8} b, err := d.MarshalText() require.NoError(t, err) require.Equal(t, []byte("2021-06-08"), b) } func TestLocalDate_UnmarshalMarshalText(t *testing.T) { d := toml.LocalDate{} err := d.UnmarshalText([]byte("2021-06-08")) require.NoError(t, err) require.Equal(t, toml.LocalDate{2021, 6, 8}, d) err = d.UnmarshalText([]byte("what")) require.Error(t, err) } func TestLocalTime_String(t *testing.T) { d := toml.LocalTime{20, 12, 1, 2, 9} require.Equal(t, "20:12:01.000000002", d.String()) d = toml.LocalTime{20, 12, 1, 0, 0} require.Equal(t, "20:12:01", d.String()) d = toml.LocalTime{20, 12, 1, 0, 9} require.Equal(t, "20:12:01.000000000", d.String()) d = toml.LocalTime{20, 12, 1, 100, 0} require.Equal(t, "20:12:01.0000001", d.String()) } func TestLocalTime_MarshalText(t *testing.T) { d := toml.LocalTime{20, 12, 1, 2, 9} b, err := d.MarshalText() require.NoError(t, err) require.Equal(t, []byte("20:12:01.000000002"), b) } func TestLocalTime_UnmarshalMarshalText(t *testing.T) { d := toml.LocalTime{} err := d.UnmarshalText([]byte("20:12:01.000000002")) require.NoError(t, err) require.Equal(t, toml.LocalTime{20, 12, 1, 2, 9}, d) err = d.UnmarshalText([]byte("what")) require.Error(t, err) err = d.UnmarshalText([]byte("20:12:01.000000002 bad")) require.Error(t, err) } func TestLocalTime_RoundTrip(t *testing.T) { var d struct{ A toml.LocalTime } err := toml.Unmarshal([]byte("a=20:12:01.500"), &d) require.NoError(t, err) require.Equal(t, "20:12:01.500", d.A.String()) } func TestLocalDateTime_AsTime(t *testing.T) { d := toml.LocalDateTime{ toml.LocalDate{2021, 6, 8}, toml.LocalTime{20, 12, 1, 2, 9}, } cast := d.AsTime(time.UTC) require.Equal(t, time.Date(2021, time.June, 8, 20, 12, 1, 2, time.UTC), cast) } func TestLocalDateTime_String(t *testing.T) { d := toml.LocalDateTime{ toml.LocalDate{2021, 6, 8}, toml.LocalTime{20, 12, 1, 2, 9}, } require.Equal(t, "2021-06-08T20:12:01.000000002", d.String()) } func TestLocalDateTime_MarshalText(t *testing.T) { d := toml.LocalDateTime{ toml.LocalDate{2021, 6, 8}, toml.LocalTime{20, 12, 1, 2, 9}, } b, err := d.MarshalText() require.NoError(t, err) require.Equal(t, []byte("2021-06-08T20:12:01.000000002"), b) } func TestLocalDateTime_UnmarshalMarshalText(t *testing.T) { d := toml.LocalDateTime{} err := d.UnmarshalText([]byte("2021-06-08 20:12:01.000000002")) require.NoError(t, err) require.Equal(t, toml.LocalDateTime{ toml.LocalDate{2021, 6, 8}, toml.LocalTime{20, 12, 1, 2, 9}, }, d) err = d.UnmarshalText([]byte("what")) require.Error(t, err) err = d.UnmarshalText([]byte("2021-06-08 20:12:01.000000002 bad")) require.Error(t, err) } go-toml-2.1.1/marshaler.go000066400000000000000000000552751453566013500154240ustar00rootroot00000000000000package toml import ( "bytes" "encoding" "fmt" "io" "math" "reflect" "sort" "strconv" "strings" "time" "unicode" "github.com/pelletier/go-toml/v2/internal/characters" ) // Marshal serializes a Go value as a TOML document. // // It is a shortcut for Encoder.Encode() with the default options. func Marshal(v interface{}) ([]byte, error) { var buf bytes.Buffer enc := NewEncoder(&buf) err := enc.Encode(v) if err != nil { return nil, err } return buf.Bytes(), nil } // Encoder writes a TOML document to an output stream. type Encoder struct { // output w io.Writer // global settings tablesInline bool arraysMultiline bool indentSymbol string indentTables bool } // NewEncoder returns a new Encoder that writes to w. func NewEncoder(w io.Writer) *Encoder { return &Encoder{ w: w, indentSymbol: " ", } } // SetTablesInline forces the encoder to emit all tables inline. // // This behavior can be controlled on an individual struct field basis with the // inline tag: // // MyField `toml:",inline"` func (enc *Encoder) SetTablesInline(inline bool) *Encoder { enc.tablesInline = inline return enc } // SetArraysMultiline forces the encoder to emit all arrays with one element per // line. // // This behavior can be controlled on an individual struct field basis with the multiline tag: // // MyField `multiline:"true"` func (enc *Encoder) SetArraysMultiline(multiline bool) *Encoder { enc.arraysMultiline = multiline return enc } // SetIndentSymbol defines the string that should be used for indentation. The // provided string is repeated for each indentation level. Defaults to two // spaces. func (enc *Encoder) SetIndentSymbol(s string) *Encoder { enc.indentSymbol = s return enc } // SetIndentTables forces the encoder to intent tables and array tables. func (enc *Encoder) SetIndentTables(indent bool) *Encoder { enc.indentTables = indent return enc } // Encode writes a TOML representation of v to the stream. // // If v cannot be represented to TOML it returns an error. // // # Encoding rules // // A top level slice containing only maps or structs is encoded as [[table // array]]. // // All slices not matching rule 1 are encoded as [array]. As a result, any map // or struct they contain is encoded as an {inline table}. // // Nil interfaces and nil pointers are not supported. // // Keys in key-values always have one part. // // Intermediate tables are always printed. // // By default, strings are encoded as literal string, unless they contain either // a newline character or a single quote. In that case they are emitted as // quoted strings. // // Unsigned integers larger than math.MaxInt64 cannot be encoded. Doing so // results in an error. This rule exists because the TOML specification only // requires parsers to support at least the 64 bits integer range. Allowing // larger numbers would create non-standard TOML documents, which may not be // readable (at best) by other implementations. To encode such numbers, a // solution is a custom type that implements encoding.TextMarshaler. // // When encoding structs, fields are encoded in order of definition, with their // exact name. // // Tables and array tables are separated by empty lines. However, consecutive // subtables definitions are not. For example: // // [top1] // // [top2] // [top2.child1] // // [[array]] // // [[array]] // [array.child2] // // # Struct tags // // The encoding of each public struct field can be customized by the format // string in the "toml" key of the struct field's tag. This follows // encoding/json's convention. The format string starts with the name of the // field, optionally followed by a comma-separated list of options. The name may // be empty in order to provide options without overriding the default name. // // The "multiline" option emits strings as quoted multi-line TOML strings. It // has no effect on fields that would not be encoded as strings. // // The "inline" option turns fields that would be emitted as tables into inline // tables instead. It has no effect on other fields. // // The "omitempty" option prevents empty values or groups from being emitted. // // The "commented" option prefixes the value and all its children with a comment // symbol. // // In addition to the "toml" tag struct tag, a "comment" tag can be used to emit // a TOML comment before the value being annotated. Comments are ignored inside // inline tables. For array tables, the comment is only present before the first // element of the array. func (enc *Encoder) Encode(v interface{}) error { var ( b []byte ctx encoderCtx ) ctx.inline = enc.tablesInline if v == nil { return fmt.Errorf("toml: cannot encode a nil interface") } b, err := enc.encode(b, ctx, reflect.ValueOf(v)) if err != nil { return err } _, err = enc.w.Write(b) if err != nil { return fmt.Errorf("toml: cannot write: %w", err) } return nil } type valueOptions struct { multiline bool omitempty bool commented bool comment string } type encoderCtx struct { // Current top-level key. parentKey []string // Key that should be used for a KV. key string // Extra flag to account for the empty string hasKey bool // Set to true to indicate that the encoder is inside a KV, so that all // tables need to be inlined. insideKv bool // Set to true to skip the first table header in an array table. skipTableHeader bool // Should the next table be encoded as inline inline bool // Indentation level indent int // Prefix the current value with a comment. commented bool // Options coming from struct tags options valueOptions } func (ctx *encoderCtx) shiftKey() { if ctx.hasKey { ctx.parentKey = append(ctx.parentKey, ctx.key) ctx.clearKey() } } func (ctx *encoderCtx) setKey(k string) { ctx.key = k ctx.hasKey = true } func (ctx *encoderCtx) clearKey() { ctx.key = "" ctx.hasKey = false } func (ctx *encoderCtx) isRoot() bool { return len(ctx.parentKey) == 0 && !ctx.hasKey } func (enc *Encoder) encode(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) { i := v.Interface() switch x := i.(type) { case time.Time: if x.Nanosecond() > 0 { return x.AppendFormat(b, time.RFC3339Nano), nil } return x.AppendFormat(b, time.RFC3339), nil case LocalTime: return append(b, x.String()...), nil case LocalDate: return append(b, x.String()...), nil case LocalDateTime: return append(b, x.String()...), nil } hasTextMarshaler := v.Type().Implements(textMarshalerType) if hasTextMarshaler || (v.CanAddr() && reflect.PtrTo(v.Type()).Implements(textMarshalerType)) { if !hasTextMarshaler { v = v.Addr() } if ctx.isRoot() { return nil, fmt.Errorf("toml: type %s implementing the TextMarshaler interface cannot be a root element", v.Type()) } text, err := v.Interface().(encoding.TextMarshaler).MarshalText() if err != nil { return nil, err } b = enc.encodeString(b, string(text), ctx.options) return b, nil } switch v.Kind() { // containers case reflect.Map: return enc.encodeMap(b, ctx, v) case reflect.Struct: return enc.encodeStruct(b, ctx, v) case reflect.Slice, reflect.Array: return enc.encodeSlice(b, ctx, v) case reflect.Interface: if v.IsNil() { return nil, fmt.Errorf("toml: encoding a nil interface is not supported") } return enc.encode(b, ctx, v.Elem()) case reflect.Ptr: if v.IsNil() { return enc.encode(b, ctx, reflect.Zero(v.Type().Elem())) } return enc.encode(b, ctx, v.Elem()) // values case reflect.String: b = enc.encodeString(b, v.String(), ctx.options) case reflect.Float32: f := v.Float() if math.IsNaN(f) { b = append(b, "nan"...) } else if f > math.MaxFloat32 { b = append(b, "inf"...) } else if f < -math.MaxFloat32 { b = append(b, "-inf"...) } else if math.Trunc(f) == f { b = strconv.AppendFloat(b, f, 'f', 1, 32) } else { b = strconv.AppendFloat(b, f, 'f', -1, 32) } case reflect.Float64: f := v.Float() if math.IsNaN(f) { b = append(b, "nan"...) } else if f > math.MaxFloat64 { b = append(b, "inf"...) } else if f < -math.MaxFloat64 { b = append(b, "-inf"...) } else if math.Trunc(f) == f { b = strconv.AppendFloat(b, f, 'f', 1, 64) } else { b = strconv.AppendFloat(b, f, 'f', -1, 64) } case reflect.Bool: if v.Bool() { b = append(b, "true"...) } else { b = append(b, "false"...) } case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint: x := v.Uint() if x > uint64(math.MaxInt64) { return nil, fmt.Errorf("toml: not encoding uint (%d) greater than max int64 (%d)", x, int64(math.MaxInt64)) } b = strconv.AppendUint(b, x, 10) case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int: b = strconv.AppendInt(b, v.Int(), 10) default: return nil, fmt.Errorf("toml: cannot encode value of type %s", v.Kind()) } return b, nil } func isNil(v reflect.Value) bool { switch v.Kind() { case reflect.Ptr, reflect.Interface, reflect.Map: return v.IsNil() default: return false } } func shouldOmitEmpty(options valueOptions, v reflect.Value) bool { return options.omitempty && isEmptyValue(v) } func (enc *Encoder) encodeKv(b []byte, ctx encoderCtx, options valueOptions, v reflect.Value) ([]byte, error) { var err error if !ctx.inline { b = enc.encodeComment(ctx.indent, options.comment, b) b = enc.commented(ctx.commented, b) b = enc.indent(ctx.indent, b) } b = enc.encodeKey(b, ctx.key) b = append(b, " = "...) // create a copy of the context because the value of a KV shouldn't // modify the global context. subctx := ctx subctx.insideKv = true subctx.shiftKey() subctx.options = options b, err = enc.encode(b, subctx, v) if err != nil { return nil, err } return b, nil } func (enc *Encoder) commented(commented bool, b []byte) []byte { if commented { return append(b, "# "...) } return b } func isEmptyValue(v reflect.Value) bool { switch v.Kind() { case reflect.Struct: return isEmptyStruct(v) case reflect.Array, reflect.Map, reflect.Slice, reflect.String: return v.Len() == 0 case reflect.Bool: return !v.Bool() case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return v.Int() == 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return v.Uint() == 0 case reflect.Float32, reflect.Float64: return v.Float() == 0 case reflect.Interface, reflect.Ptr: return v.IsNil() } return false } func isEmptyStruct(v reflect.Value) bool { // TODO: merge with walkStruct and cache. typ := v.Type() for i := 0; i < typ.NumField(); i++ { fieldType := typ.Field(i) // only consider exported fields if fieldType.PkgPath != "" { continue } tag := fieldType.Tag.Get("toml") // special field name to skip field if tag == "-" { continue } f := v.Field(i) if !isEmptyValue(f) { return false } } return true } const literalQuote = '\'' func (enc *Encoder) encodeString(b []byte, v string, options valueOptions) []byte { if needsQuoting(v) { return enc.encodeQuotedString(options.multiline, b, v) } return enc.encodeLiteralString(b, v) } func needsQuoting(v string) bool { // TODO: vectorize for _, b := range []byte(v) { if b == '\'' || b == '\r' || b == '\n' || characters.InvalidAscii(b) { return true } } return false } // caller should have checked that the string does not contain new lines or ' . func (enc *Encoder) encodeLiteralString(b []byte, v string) []byte { b = append(b, literalQuote) b = append(b, v...) b = append(b, literalQuote) return b } func (enc *Encoder) encodeQuotedString(multiline bool, b []byte, v string) []byte { stringQuote := `"` if multiline { stringQuote = `"""` } b = append(b, stringQuote...) if multiline { b = append(b, '\n') } const ( hextable = "0123456789ABCDEF" // U+0000 to U+0008, U+000A to U+001F, U+007F nul = 0x0 bs = 0x8 lf = 0xa us = 0x1f del = 0x7f ) for _, r := range []byte(v) { switch r { case '\\': b = append(b, `\\`...) case '"': b = append(b, `\"`...) case '\b': b = append(b, `\b`...) case '\f': b = append(b, `\f`...) case '\n': if multiline { b = append(b, r) } else { b = append(b, `\n`...) } case '\r': b = append(b, `\r`...) case '\t': b = append(b, `\t`...) default: switch { case r >= nul && r <= bs, r >= lf && r <= us, r == del: b = append(b, `\u00`...) b = append(b, hextable[r>>4]) b = append(b, hextable[r&0x0f]) default: b = append(b, r) } } } b = append(b, stringQuote...) return b } // caller should have checked that the string is in A-Z / a-z / 0-9 / - / _ . func (enc *Encoder) encodeUnquotedKey(b []byte, v string) []byte { return append(b, v...) } func (enc *Encoder) encodeTableHeader(ctx encoderCtx, b []byte) ([]byte, error) { if len(ctx.parentKey) == 0 { return b, nil } b = enc.encodeComment(ctx.indent, ctx.options.comment, b) b = enc.commented(ctx.commented, b) b = enc.indent(ctx.indent, b) b = append(b, '[') b = enc.encodeKey(b, ctx.parentKey[0]) for _, k := range ctx.parentKey[1:] { b = append(b, '.') b = enc.encodeKey(b, k) } b = append(b, "]\n"...) return b, nil } //nolint:cyclop func (enc *Encoder) encodeKey(b []byte, k string) []byte { needsQuotation := false cannotUseLiteral := false if len(k) == 0 { return append(b, "''"...) } for _, c := range k { if (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' { continue } if c == literalQuote { cannotUseLiteral = true } needsQuotation = true } if needsQuotation && needsQuoting(k) { cannotUseLiteral = true } switch { case cannotUseLiteral: return enc.encodeQuotedString(false, b, k) case needsQuotation: return enc.encodeLiteralString(b, k) default: return enc.encodeUnquotedKey(b, k) } } func (enc *Encoder) keyToString(k reflect.Value) (string, error) { keyType := k.Type() switch { case keyType.Kind() == reflect.String: return k.String(), nil case keyType.Implements(textMarshalerType): keyB, err := k.Interface().(encoding.TextMarshaler).MarshalText() if err != nil { return "", fmt.Errorf("toml: error marshalling key %v from text: %w", k, err) } return string(keyB), nil } return "", fmt.Errorf("toml: type %s is not supported as a map key", keyType.Kind()) } func (enc *Encoder) encodeMap(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) { var ( t table emptyValueOptions valueOptions ) iter := v.MapRange() for iter.Next() { v := iter.Value() if isNil(v) { continue } k, err := enc.keyToString(iter.Key()) if err != nil { return nil, err } if willConvertToTableOrArrayTable(ctx, v) { t.pushTable(k, v, emptyValueOptions) } else { t.pushKV(k, v, emptyValueOptions) } } sortEntriesByKey(t.kvs) sortEntriesByKey(t.tables) return enc.encodeTable(b, ctx, t) } func sortEntriesByKey(e []entry) { sort.Slice(e, func(i, j int) bool { return e[i].Key < e[j].Key }) } type entry struct { Key string Value reflect.Value Options valueOptions } type table struct { kvs []entry tables []entry } func (t *table) pushKV(k string, v reflect.Value, options valueOptions) { for _, e := range t.kvs { if e.Key == k { return } } t.kvs = append(t.kvs, entry{Key: k, Value: v, Options: options}) } func (t *table) pushTable(k string, v reflect.Value, options valueOptions) { for _, e := range t.tables { if e.Key == k { return } } t.tables = append(t.tables, entry{Key: k, Value: v, Options: options}) } func walkStruct(ctx encoderCtx, t *table, v reflect.Value) { // TODO: cache this typ := v.Type() for i := 0; i < typ.NumField(); i++ { fieldType := typ.Field(i) // only consider exported fields if fieldType.PkgPath != "" { continue } tag := fieldType.Tag.Get("toml") // special field name to skip field if tag == "-" { continue } k, opts := parseTag(tag) if !isValidName(k) { k = "" } f := v.Field(i) if k == "" { if fieldType.Anonymous { if fieldType.Type.Kind() == reflect.Struct { walkStruct(ctx, t, f) } continue } else { k = fieldType.Name } } if isNil(f) { continue } options := valueOptions{ multiline: opts.multiline, omitempty: opts.omitempty, commented: opts.commented, comment: fieldType.Tag.Get("comment"), } if opts.inline || !willConvertToTableOrArrayTable(ctx, f) { t.pushKV(k, f, options) } else { t.pushTable(k, f, options) } } } func (enc *Encoder) encodeStruct(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) { var t table walkStruct(ctx, &t, v) return enc.encodeTable(b, ctx, t) } func (enc *Encoder) encodeComment(indent int, comment string, b []byte) []byte { for len(comment) > 0 { var line string idx := strings.IndexByte(comment, '\n') if idx >= 0 { line = comment[:idx] comment = comment[idx+1:] } else { line = comment comment = "" } b = enc.indent(indent, b) b = append(b, "# "...) b = append(b, line...) b = append(b, '\n') } return b } func isValidName(s string) bool { if s == "" { return false } for _, c := range s { switch { case strings.ContainsRune("!#$%&()*+-./:;<=>?@[]^_{|}~ ", c): // Backslash and quote chars are reserved, but // otherwise any punctuation chars are allowed // in a tag name. case !unicode.IsLetter(c) && !unicode.IsDigit(c): return false } } return true } type tagOptions struct { multiline bool inline bool omitempty bool commented bool } func parseTag(tag string) (string, tagOptions) { opts := tagOptions{} idx := strings.Index(tag, ",") if idx == -1 { return tag, opts } raw := tag[idx+1:] tag = string(tag[:idx]) for raw != "" { var o string i := strings.Index(raw, ",") if i >= 0 { o, raw = raw[:i], raw[i+1:] } else { o, raw = raw, "" } switch o { case "multiline": opts.multiline = true case "inline": opts.inline = true case "omitempty": opts.omitempty = true case "commented": opts.commented = true } } return tag, opts } func (enc *Encoder) encodeTable(b []byte, ctx encoderCtx, t table) ([]byte, error) { var err error ctx.shiftKey() if ctx.insideKv || (ctx.inline && !ctx.isRoot()) { return enc.encodeTableInline(b, ctx, t) } if !ctx.skipTableHeader { b, err = enc.encodeTableHeader(ctx, b) if err != nil { return nil, err } if enc.indentTables && len(ctx.parentKey) > 0 { ctx.indent++ } } ctx.skipTableHeader = false hasNonEmptyKV := false for _, kv := range t.kvs { if shouldOmitEmpty(kv.Options, kv.Value) { continue } hasNonEmptyKV = true ctx.setKey(kv.Key) ctx2 := ctx ctx2.commented = kv.Options.commented || ctx2.commented b, err = enc.encodeKv(b, ctx2, kv.Options, kv.Value) if err != nil { return nil, err } b = append(b, '\n') } first := true for _, table := range t.tables { if shouldOmitEmpty(table.Options, table.Value) { continue } if first { first = false if hasNonEmptyKV { b = append(b, '\n') } } else { b = append(b, "\n"...) } ctx.setKey(table.Key) ctx.options = table.Options ctx2 := ctx ctx2.commented = ctx2.commented || ctx.options.commented b, err = enc.encode(b, ctx2, table.Value) if err != nil { return nil, err } } return b, nil } func (enc *Encoder) encodeTableInline(b []byte, ctx encoderCtx, t table) ([]byte, error) { var err error b = append(b, '{') first := true for _, kv := range t.kvs { if shouldOmitEmpty(kv.Options, kv.Value) { continue } if first { first = false } else { b = append(b, `, `...) } ctx.setKey(kv.Key) b, err = enc.encodeKv(b, ctx, kv.Options, kv.Value) if err != nil { return nil, err } } if len(t.tables) > 0 { panic("inline table cannot contain nested tables, only key-values") } b = append(b, "}"...) return b, nil } func willConvertToTable(ctx encoderCtx, v reflect.Value) bool { if !v.IsValid() { return false } if v.Type() == timeType || v.Type().Implements(textMarshalerType) || (v.Kind() != reflect.Ptr && v.CanAddr() && reflect.PtrTo(v.Type()).Implements(textMarshalerType)) { return false } t := v.Type() switch t.Kind() { case reflect.Map, reflect.Struct: return !ctx.inline case reflect.Interface: return willConvertToTable(ctx, v.Elem()) case reflect.Ptr: if v.IsNil() { return false } return willConvertToTable(ctx, v.Elem()) default: return false } } func willConvertToTableOrArrayTable(ctx encoderCtx, v reflect.Value) bool { if ctx.insideKv { return false } t := v.Type() if t.Kind() == reflect.Interface { return willConvertToTableOrArrayTable(ctx, v.Elem()) } if t.Kind() == reflect.Slice || t.Kind() == reflect.Array { if v.Len() == 0 { // An empty slice should be a kv = []. return false } for i := 0; i < v.Len(); i++ { t := willConvertToTable(ctx, v.Index(i)) if !t { return false } } return true } return willConvertToTable(ctx, v) } func (enc *Encoder) encodeSlice(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) { if v.Len() == 0 { b = append(b, "[]"...) return b, nil } if willConvertToTableOrArrayTable(ctx, v) { return enc.encodeSliceAsArrayTable(b, ctx, v) } return enc.encodeSliceAsArray(b, ctx, v) } // caller should have checked that v is a slice that only contains values that // encode into tables. func (enc *Encoder) encodeSliceAsArrayTable(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) { ctx.shiftKey() scratch := make([]byte, 0, 64) scratch = enc.commented(ctx.commented, scratch) scratch = append(scratch, "[["...) for i, k := range ctx.parentKey { if i > 0 { scratch = append(scratch, '.') } scratch = enc.encodeKey(scratch, k) } scratch = append(scratch, "]]\n"...) ctx.skipTableHeader = true b = enc.encodeComment(ctx.indent, ctx.options.comment, b) if enc.indentTables { ctx.indent++ } for i := 0; i < v.Len(); i++ { if i != 0 { b = append(b, "\n"...) } b = append(b, scratch...) var err error b, err = enc.encode(b, ctx, v.Index(i)) if err != nil { return nil, err } } return b, nil } func (enc *Encoder) encodeSliceAsArray(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, error) { multiline := ctx.options.multiline || enc.arraysMultiline separator := ", " b = append(b, '[') subCtx := ctx subCtx.options = valueOptions{} if multiline { separator = ",\n" b = append(b, '\n') subCtx.indent++ } var err error first := true for i := 0; i < v.Len(); i++ { if first { first = false } else { b = append(b, separator...) } if multiline { b = enc.indent(subCtx.indent, b) } b, err = enc.encode(b, subCtx, v.Index(i)) if err != nil { return nil, err } } if multiline { b = append(b, '\n') b = enc.indent(ctx.indent, b) } b = append(b, ']') return b, nil } func (enc *Encoder) indent(level int, b []byte) []byte { for i := 0; i < level; i++ { b = append(b, enc.indentSymbol...) } return b } go-toml-2.1.1/marshaler_test.go000066400000000000000000001015331453566013500164500ustar00rootroot00000000000000package toml_test import ( "bytes" "encoding/json" "fmt" "math" "math/big" "strings" "testing" "time" "github.com/pelletier/go-toml/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) type marshalTextKey struct { A string B string } func (k marshalTextKey) MarshalText() ([]byte, error) { return []byte(k.A + "-" + k.B), nil } type marshalBadTextKey struct{} func (k marshalBadTextKey) MarshalText() ([]byte, error) { return nil, fmt.Errorf("error") } func TestMarshal(t *testing.T) { someInt := 42 type structInline struct { A interface{} `toml:",inline"` } type comments struct { One int Two int `comment:"Before kv"` Three []int `comment:"Before array"` } examples := []struct { desc string v interface{} expected string err bool }{ { desc: "simple map and string", v: map[string]string{ "hello": "world", }, expected: "hello = 'world'\n", }, { desc: "map with new line in key", v: map[string]string{ "hel\nlo": "world", }, expected: "\"hel\\nlo\" = 'world'\n", }, { desc: `map with " in key`, v: map[string]string{ `hel"lo`: "world", }, expected: "'hel\"lo' = 'world'\n", }, { desc: "map in map and string", v: map[string]map[string]string{ "table": { "hello": "world", }, }, expected: `[table] hello = 'world' `, }, { desc: "map in map in map and string", v: map[string]map[string]map[string]string{ "this": { "is": { "a": "test", }, }, }, expected: `[this] [this.is] a = 'test' `, }, { desc: "map in map in map and string with values", v: map[string]interface{}{ "this": map[string]interface{}{ "is": map[string]string{ "a": "test", }, "also": "that", }, }, expected: `[this] also = 'that' [this.is] a = 'test' `, }, { desc: `map with text key`, v: map[marshalTextKey]string{ {A: "a", B: "1"}: "value 1", {A: "a", B: "2"}: "value 2", {A: "b", B: "1"}: "value 3", }, expected: `a-1 = 'value 1' a-2 = 'value 2' b-1 = 'value 3' `, }, { desc: `table with text key`, v: map[marshalTextKey]map[string]string{ {A: "a", B: "1"}: {"value": "foo"}, }, expected: `[a-1] value = 'foo' `, }, { desc: `map with ptr text key`, v: map[*marshalTextKey]string{ {A: "a", B: "1"}: "value 1", {A: "a", B: "2"}: "value 2", {A: "b", B: "1"}: "value 3", }, expected: `a-1 = 'value 1' a-2 = 'value 2' b-1 = 'value 3' `, }, { desc: `map with bad text key`, v: map[marshalBadTextKey]string{ {}: "value 1", }, err: true, }, { desc: `map with bad ptr text key`, v: map[*marshalBadTextKey]string{ {}: "value 1", }, err: true, }, { desc: "simple string array", v: map[string][]string{ "array": {"one", "two", "three"}, }, expected: `array = ['one', 'two', 'three'] `, }, { desc: "empty string array", v: map[string][]string{}, expected: ``, }, { desc: "map", v: map[string][]string{}, expected: ``, }, { desc: "nested string arrays", v: map[string][][]string{ "array": {{"one", "two"}, {"three"}}, }, expected: `array = [['one', 'two'], ['three']] `, }, { desc: "mixed strings and nested string arrays", v: map[string][]interface{}{ "array": {"a string", []string{"one", "two"}, "last"}, }, expected: `array = ['a string', ['one', 'two'], 'last'] `, }, { desc: "array of maps", v: map[string][]map[string]string{ "top": { {"map1.1": "v1.1"}, {"map2.1": "v2.1"}, }, }, expected: `[[top]] 'map1.1' = 'v1.1' [[top]] 'map2.1' = 'v2.1' `, }, { desc: "fixed size string array", v: map[string][3]string{ "array": {"one", "two", "three"}, }, expected: `array = ['one', 'two', 'three'] `, }, { desc: "fixed size nested string arrays", v: map[string][2][2]string{ "array": {{"one", "two"}, {"three"}}, }, expected: `array = [['one', 'two'], ['three', '']] `, }, { desc: "mixed strings and fixed size nested string arrays", v: map[string][]interface{}{ "array": {"a string", [2]string{"one", "two"}, "last"}, }, expected: `array = ['a string', ['one', 'two'], 'last'] `, }, { desc: "fixed size array of maps", v: map[string][2]map[string]string{ "ftop": { {"map1.1": "v1.1"}, {"map2.1": "v2.1"}, }, }, expected: `[[ftop]] 'map1.1' = 'v1.1' [[ftop]] 'map2.1' = 'v2.1' `, }, { desc: "map with two keys", v: map[string]string{ "key1": "value1", "key2": "value2", }, expected: `key1 = 'value1' key2 = 'value2' `, }, { desc: "simple struct", v: struct { A string }{ A: "foo", }, expected: `A = 'foo' `, }, { desc: "one level of structs within structs", v: struct { A interface{} }{ A: struct { K1 string K2 string }{ K1: "v1", K2: "v2", }, }, expected: `[A] K1 = 'v1' K2 = 'v2' `, }, { desc: "structs in array with interfaces", v: map[string]interface{}{ "root": map[string]interface{}{ "nested": []interface{}{ map[string]interface{}{"name": "Bob"}, map[string]interface{}{"name": "Alice"}, }, }, }, expected: `[root] [[root.nested]] name = 'Bob' [[root.nested]] name = 'Alice' `, }, { desc: "string escapes", v: map[string]interface{}{ "a": "'\b\f\r\t\"\\", }, expected: `a = "'\b\f\r\t\"\\" `, }, { desc: "string utf8 low", v: map[string]interface{}{ "a": "'Ę", }, expected: `a = "'Ę" `, }, { desc: "string utf8 low 2", v: map[string]interface{}{ "a": "'\u10A85", }, expected: "a = \"'\u10A85\"\n", }, { desc: "string utf8 low 2", v: map[string]interface{}{ "a": "'\u10A85", }, expected: "a = \"'\u10A85\"\n", }, { desc: "emoji", v: map[string]interface{}{ "a": "'😀", }, expected: "a = \"'😀\"\n", }, { desc: "control char", v: map[string]interface{}{ "a": "'\u001A", }, expected: `a = "'\u001A" `, }, { desc: "multi-line string", v: map[string]interface{}{ "a": "hello\nworld", }, expected: `a = "hello\nworld" `, }, { desc: "multi-line forced", v: struct { A string `toml:",multiline"` }{ A: "hello\nworld", }, expected: `A = """ hello world""" `, }, { desc: "inline field", v: struct { A map[string]string `toml:",inline"` B map[string]string }{ A: map[string]string{ "isinline": "yes", }, B: map[string]string{ "isinline": "no", }, }, expected: `A = {isinline = 'yes'} [B] isinline = 'no' `, }, { desc: "mutiline array int", v: struct { A []int `toml:",multiline"` B []int }{ A: []int{1, 2, 3, 4}, B: []int{1, 2, 3, 4}, }, expected: `A = [ 1, 2, 3, 4 ] B = [1, 2, 3, 4] `, }, { desc: "mutiline array in array", v: struct { A [][]int `toml:",multiline"` }{ A: [][]int{{1, 2}, {3, 4}}, }, expected: `A = [ [1, 2], [3, 4] ] `, }, { desc: "nil interface not supported at root", v: nil, err: true, }, { desc: "nil interface not supported in slice", v: map[string]interface{}{ "a": []interface{}{"a", nil, 2}, }, err: true, }, { desc: "nil pointer in slice uses zero value", v: struct { A []*int }{ A: []*int{nil}, }, expected: `A = [0] `, }, { desc: "nil pointer in slice uses zero value", v: struct { A []*int }{ A: []*int{nil}, }, expected: `A = [0] `, }, { desc: "pointer in slice", v: struct { A []*int }{ A: []*int{&someInt}, }, expected: `A = [42] `, }, { desc: "inline table in inline table", v: structInline{ A: structInline{ A: structInline{ A: "hello", }, }, }, expected: `A = {A = {A = 'hello'}} `, }, { desc: "empty slice in map", v: map[string][]string{ "a": {}, }, expected: `a = [] `, }, { desc: "map in slice", v: map[string][]map[string]string{ "a": {{"hello": "world"}}, }, expected: `[[a]] hello = 'world' `, }, { desc: "newline in map in slice", v: map[string][]map[string]string{ "a\n": {{"hello": "world"}}, }, expected: `[["a\n"]] hello = 'world' `, }, { desc: "newline in map in slice", v: map[string][]map[string]*customTextMarshaler{ "a": {{"hello": &customTextMarshaler{1}}}, }, err: true, }, { desc: "empty slice of empty struct", v: struct { A []struct{} }{ A: []struct{}{}, }, expected: `A = [] `, }, { desc: "nil field is ignored", v: struct { A interface{} }{ A: nil, }, expected: ``, }, { desc: "private fields are ignored", v: struct { Public string private string }{ Public: "shown", private: "hidden", }, expected: `Public = 'shown' `, }, { desc: "fields tagged - are ignored", v: struct { Public string `toml:"-"` private string }{ Public: "hidden", }, expected: ``, }, { desc: "nil value in map is ignored", v: map[string]interface{}{ "A": nil, }, expected: ``, }, { desc: "new line in table key", v: map[string]interface{}{ "hello\nworld": 42, }, expected: `"hello\nworld" = 42 `, }, { desc: "new line in parent of nested table key", v: map[string]interface{}{ "hello\nworld": map[string]interface{}{ "inner": 42, }, }, expected: `["hello\nworld"] inner = 42 `, }, { desc: "new line in nested table key", v: map[string]interface{}{ "parent": map[string]interface{}{ "in\ner": map[string]interface{}{ "foo": 42, }, }, }, expected: `[parent] [parent."in\ner"] foo = 42 `, }, { desc: "invalid map key", v: map[int]interface{}{1: "a"}, err: true, }, { desc: "invalid map key but empty", v: map[int]interface{}{}, expected: "", }, { desc: "unhandled type", v: struct { A chan int }{ A: make(chan int), }, err: true, }, { desc: "time", v: struct { T time.Time }{ T: time.Time{}, }, expected: `T = 0001-01-01T00:00:00Z `, }, { desc: "time nano", v: struct { T time.Time }{ T: time.Date(1979, time.May, 27, 0, 32, 0, 999999000, time.UTC), }, expected: `T = 1979-05-27T00:32:00.999999Z `, }, { desc: "bool", v: struct { A bool B bool }{ A: false, B: true, }, expected: `A = false B = true `, }, { desc: "numbers", v: struct { A float32 B uint64 C uint32 D uint16 E uint8 F uint G int64 H int32 I int16 J int8 K int L float64 }{ A: 1.1, B: 42, C: 42, D: 42, E: 42, F: 42, G: 42, H: 42, I: 42, J: 42, K: 42, L: 2.2, }, expected: `A = 1.1 B = 42 C = 42 D = 42 E = 42 F = 42 G = 42 H = 42 I = 42 J = 42 K = 42 L = 2.2 `, }, { desc: "comments", v: struct { Table comments `comment:"Before table"` }{ Table: comments{ One: 1, Two: 2, Three: []int{1, 2, 3}, }, }, expected: `# Before table [Table] One = 1 # Before kv Two = 2 # Before array Three = [1, 2, 3] `, }, } for _, e := range examples { e := e t.Run(e.desc, func(t *testing.T) { b, err := toml.Marshal(e.v) if e.err { require.Error(t, err) return } require.NoError(t, err) assert.Equal(t, e.expected, string(b)) // make sure the output is always valid TOML defaultMap := map[string]interface{}{} err = toml.Unmarshal(b, &defaultMap) require.NoError(t, err) testWithAllFlags(t, func(t *testing.T, flags int) { t.Helper() var buf bytes.Buffer enc := toml.NewEncoder(&buf) setFlags(enc, flags) err := enc.Encode(e.v) require.NoError(t, err) inlineMap := map[string]interface{}{} err = toml.Unmarshal(buf.Bytes(), &inlineMap) require.NoError(t, err) require.Equal(t, defaultMap, inlineMap) }) }) } } type flagsSetters []struct { name string f func(enc *toml.Encoder, flag bool) *toml.Encoder } var allFlags = flagsSetters{ {"arrays-multiline", (*toml.Encoder).SetArraysMultiline}, {"tables-inline", (*toml.Encoder).SetTablesInline}, {"indent-tables", (*toml.Encoder).SetIndentTables}, } func setFlags(enc *toml.Encoder, flags int) { for i := 0; i < len(allFlags); i++ { enabled := flags&1 > 0 allFlags[i].f(enc, enabled) } } func testWithAllFlags(t *testing.T, testfn func(t *testing.T, flags int)) { t.Helper() testWithFlags(t, 0, allFlags, testfn) } func testWithFlags(t *testing.T, flags int, setters flagsSetters, testfn func(t *testing.T, flags int)) { t.Helper() if len(setters) == 0 { testfn(t, flags) return } s := setters[0] for _, enabled := range []bool{false, true} { name := fmt.Sprintf("%s=%t", s.name, enabled) newFlags := flags << 1 if enabled { newFlags++ } t.Run(name, func(t *testing.T) { testWithFlags(t, newFlags, setters[1:], testfn) }) } } func TestMarshalFloats(t *testing.T) { v := map[string]float32{ "nan": float32(math.NaN()), "+inf": float32(math.Inf(1)), "-inf": float32(math.Inf(-1)), } expected := `'+inf' = inf -inf = -inf nan = nan ` actual, err := toml.Marshal(v) require.NoError(t, err) require.Equal(t, expected, string(actual)) v64 := map[string]float64{ "nan": math.NaN(), "+inf": math.Inf(1), "-inf": math.Inf(-1), } actual, err = toml.Marshal(v64) require.NoError(t, err) require.Equal(t, expected, string(actual)) } //nolint:funlen func TestMarshalIndentTables(t *testing.T) { examples := []struct { desc string v interface{} expected string }{ { desc: "one kv", v: map[string]interface{}{ "foo": "bar", }, expected: `foo = 'bar' `, }, { desc: "one level table", v: map[string]map[string]string{ "foo": { "one": "value1", "two": "value2", }, }, expected: `[foo] one = 'value1' two = 'value2' `, }, { desc: "two levels table", v: map[string]interface{}{ "root": "value0", "level1": map[string]interface{}{ "one": "value1", "level2": map[string]interface{}{ "two": "value2", }, }, }, expected: `root = 'value0' [level1] one = 'value1' [level1.level2] two = 'value2' `, }, } for _, e := range examples { e := e t.Run(e.desc, func(t *testing.T) { var buf strings.Builder enc := toml.NewEncoder(&buf) enc.SetIndentTables(true) err := enc.Encode(e.v) require.NoError(t, err) assert.Equal(t, e.expected, buf.String()) }) } } type customTextMarshaler struct { value int64 } func (c *customTextMarshaler) MarshalText() ([]byte, error) { if c.value == 1 { return nil, fmt.Errorf("cannot represent 1 because this is a silly test") } return []byte(fmt.Sprintf("::%d", c.value)), nil } func TestMarshalTextMarshaler_NoRoot(t *testing.T) { c := customTextMarshaler{} _, err := toml.Marshal(&c) require.Error(t, err) } func TestMarshalTextMarshaler_Error(t *testing.T) { m := map[string]interface{}{"a": &customTextMarshaler{value: 1}} _, err := toml.Marshal(m) require.Error(t, err) } func TestMarshalTextMarshaler_ErrorInline(t *testing.T) { type s struct { A map[string]interface{} `inline:"true"` } d := s{ A: map[string]interface{}{"a": &customTextMarshaler{value: 1}}, } _, err := toml.Marshal(d) require.Error(t, err) } func TestMarshalTextMarshaler(t *testing.T) { m := map[string]interface{}{"a": &customTextMarshaler{value: 2}} r, err := toml.Marshal(m) require.NoError(t, err) assert.Equal(t, "a = '::2'\n", string(r)) } type brokenWriter struct{} func (b *brokenWriter) Write([]byte) (int, error) { return 0, fmt.Errorf("dead") } func TestEncodeToBrokenWriter(t *testing.T) { w := brokenWriter{} enc := toml.NewEncoder(&w) err := enc.Encode(map[string]string{"hello": "world"}) require.Error(t, err) } func TestEncoderSetIndentSymbol(t *testing.T) { var w strings.Builder enc := toml.NewEncoder(&w) enc.SetIndentTables(true) enc.SetIndentSymbol(">>>") err := enc.Encode(map[string]map[string]string{"parent": {"hello": "world"}}) require.NoError(t, err) expected := `[parent] >>>hello = 'world' ` assert.Equal(t, expected, w.String()) } func TestEncoderOmitempty(t *testing.T) { type doc struct { String string `toml:",omitempty,multiline"` Bool bool `toml:",omitempty,multiline"` Int int `toml:",omitempty,multiline"` Int8 int8 `toml:",omitempty,multiline"` Int16 int16 `toml:",omitempty,multiline"` Int32 int32 `toml:",omitempty,multiline"` Int64 int64 `toml:",omitempty,multiline"` Uint uint `toml:",omitempty,multiline"` Uint8 uint8 `toml:",omitempty,multiline"` Uint16 uint16 `toml:",omitempty,multiline"` Uint32 uint32 `toml:",omitempty,multiline"` Uint64 uint64 `toml:",omitempty,multiline"` Float32 float32 `toml:",omitempty,multiline"` Float64 float64 `toml:",omitempty,multiline"` MapNil map[string]string `toml:",omitempty,multiline"` Slice []string `toml:",omitempty,multiline"` Ptr *string `toml:",omitempty,multiline"` Iface interface{} `toml:",omitempty,multiline"` Struct struct{} `toml:",omitempty,multiline"` } d := doc{} b, err := toml.Marshal(d) require.NoError(t, err) expected := `` assert.Equal(t, expected, string(b)) } func TestEncoderTagFieldName(t *testing.T) { type doc struct { String string `toml:"hello"` OkSym string `toml:"#"` Bad string `toml:"\"` } d := doc{String: "world"} b, err := toml.Marshal(d) require.NoError(t, err) expected := `hello = 'world' '#' = '' Bad = '' ` assert.Equal(t, expected, string(b)) } func TestIssue436(t *testing.T) { data := []byte(`{"a": [ { "b": { "c": "d" } } ]}`) var v interface{} err := json.Unmarshal(data, &v) require.NoError(t, err) var buf bytes.Buffer err = toml.NewEncoder(&buf).Encode(v) require.NoError(t, err) expected := `[[a]] [a.b] c = 'd' ` assert.Equal(t, expected, buf.String()) } func TestIssue424(t *testing.T) { type Message1 struct { Text string } type Message2 struct { Text string `multiline:"true"` } msg1 := Message1{"Hello\\World"} msg2 := Message2{"Hello\\World"} toml1, err := toml.Marshal(msg1) require.NoError(t, err) toml2, err := toml.Marshal(msg2) require.NoError(t, err) msg1parsed := Message1{} err = toml.Unmarshal(toml1, &msg1parsed) require.NoError(t, err) require.Equal(t, msg1, msg1parsed) msg2parsed := Message2{} err = toml.Unmarshal(toml2, &msg2parsed) require.NoError(t, err) require.Equal(t, msg2, msg2parsed) } func TestIssue567(t *testing.T) { var m map[string]interface{} err := toml.Unmarshal([]byte("A = 12:08:05"), &m) require.NoError(t, err) require.IsType(t, m["A"], toml.LocalTime{}) } func TestIssue590(t *testing.T) { type CustomType int var cfg struct { Option CustomType `toml:"option"` } err := toml.Unmarshal([]byte("option = 42"), &cfg) require.NoError(t, err) } func TestIssue571(t *testing.T) { type Foo struct { Float32 float32 Float64 float64 } const closeEnough = 1e-9 foo := Foo{ Float32: 42, Float64: 43, } b, err := toml.Marshal(foo) require.NoError(t, err) var foo2 Foo err = toml.Unmarshal(b, &foo2) require.NoError(t, err) assert.InDelta(t, 42, foo2.Float32, closeEnough) assert.InDelta(t, 43, foo2.Float64, closeEnough) } func TestIssue678(t *testing.T) { type Config struct { BigInt big.Int } cfg := &Config{ BigInt: *big.NewInt(123), } out, err := toml.Marshal(cfg) require.NoError(t, err) assert.Equal(t, "BigInt = '123'\n", string(out)) cfg2 := &Config{} err = toml.Unmarshal(out, cfg2) require.NoError(t, err) require.Equal(t, cfg, cfg2) } func TestIssue752(t *testing.T) { type Fooer interface { Foo() string } type Container struct { Fooer } c := Container{} out, err := toml.Marshal(c) require.NoError(t, err) require.Equal(t, "", string(out)) } func TestIssue768(t *testing.T) { type cfg struct { Name string `comment:"This is a multiline comment.\nThis is line 2."` } out, err := toml.Marshal(&cfg{}) require.NoError(t, err) expected := `# This is a multiline comment. # This is line 2. Name = '' ` require.Equal(t, expected, string(out)) } func TestIssue786(t *testing.T) { type Dependencies struct { Dependencies []string `toml:"dependencies,multiline,omitempty"` BuildDependencies []string `toml:"buildDependencies,multiline,omitempty"` OptionalDependencies []string `toml:"optionalDependencies,multiline,omitempty"` } type Test struct { Dependencies Dependencies `toml:"dependencies,omitempty"` } x := Test{} b, err := toml.Marshal(x) require.NoError(t, err) require.Equal(t, "", string(b)) type General struct { From string `toml:"from,omitempty" json:"from,omitempty" comment:"from in graphite-web format, the local TZ is used"` Randomize bool `toml:"randomize" json:"randomize" comment:"randomize starting time with [0,step)"` } type Custom struct { Name string `toml:"name" json:"name,omitempty" comment:"names for generator, braces are expanded like in shell"` Type string `toml:"type,omitempty" json:"type,omitempty" comment:"type of generator"` General } type Config struct { General Custom []Custom `toml:"custom,omitempty" json:"custom,omitempty" comment:"generators with custom parameters can be specified separately"` } buf := new(bytes.Buffer) config := &Config{General: General{From: "-2d", Randomize: true}} config.Custom = []Custom{{Name: "omit", General: General{Randomize: false}}} config.Custom = append(config.Custom, Custom{Name: "present", General: General{From: "-2d", Randomize: true}}) encoder := toml.NewEncoder(buf) encoder.Encode(config) expected := `# from in graphite-web format, the local TZ is used from = '-2d' # randomize starting time with [0,step) randomize = true # generators with custom parameters can be specified separately [[custom]] # names for generator, braces are expanded like in shell name = 'omit' # randomize starting time with [0,step) randomize = false [[custom]] # names for generator, braces are expanded like in shell name = 'present' # from in graphite-web format, the local TZ is used from = '-2d' # randomize starting time with [0,step) randomize = true ` require.Equal(t, expected, buf.String()) } func TestMarhsalIssue888(t *testing.T) { type Thing struct { FieldA string `comment:"my field A"` FieldB string `comment:"my field B"` } type Cfg struct { Custom []Thing `comment:"custom config"` } buf := new(bytes.Buffer) config := Cfg{ Custom: []Thing{ {FieldA: "field a 1", FieldB: "field b 1"}, {FieldA: "field a 2", FieldB: "field b 2"}, }, } encoder := toml.NewEncoder(buf).SetIndentTables(true) encoder.Encode(config) expected := `# custom config [[Custom]] # my field A FieldA = 'field a 1' # my field B FieldB = 'field b 1' [[Custom]] # my field A FieldA = 'field a 2' # my field B FieldB = 'field b 2' ` require.Equal(t, expected, buf.String()) } func TestMarshalNestedAnonymousStructs(t *testing.T) { type Embedded struct { Value string `toml:"value" json:"value"` Top struct { Value string `toml:"value" json:"value"` } `toml:"top" json:"top"` } type Named struct { Value string `toml:"value" json:"value"` } var doc struct { Embedded Named `toml:"named" json:"named"` Anonymous struct { Value string `toml:"value" json:"value"` } `toml:"anonymous" json:"anonymous"` } expected := `value = '' [top] value = '' [named] value = '' [anonymous] value = '' ` result, err := toml.Marshal(doc) require.NoError(t, err) require.Equal(t, expected, string(result)) } func TestMarshalNestedAnonymousStructs_DuplicateField(t *testing.T) { type Embedded struct { Value string `toml:"value" json:"value"` Top struct { Value string `toml:"value" json:"value"` } `toml:"top" json:"top"` } var doc struct { Value string `toml:"value" json:"value"` Embedded } doc.Embedded.Value = "shadowed" doc.Value = "shadows" expected := `value = 'shadows' [top] value = '' ` result, err := toml.Marshal(doc) require.NoError(t, err) require.NoError(t, err) require.Equal(t, expected, string(result)) } func TestLocalTime(t *testing.T) { v := map[string]toml.LocalTime{ "a": { Hour: 1, Minute: 2, Second: 3, Nanosecond: 4, }, } expected := `a = 01:02:03.000000004 ` out, err := toml.Marshal(v) require.NoError(t, err) require.Equal(t, expected, string(out)) } func TestMarshalUint64Overflow(t *testing.T) { // The TOML spec only requires implementation to provide support for the // int64 range. To avoid generating TOML documents that would not be // supported by standard-compliant parsers, uint64 > max int64 cannot be // marshaled. x := map[string]interface{}{ "foo": uint64(math.MaxInt64) + 1, } _, err := toml.Marshal(x) require.Error(t, err) } func TestIndentWithInlineTable(t *testing.T) { x := map[string][]map[string]string{ "one": { {"0": "0"}, {"1": "1"}, }, } expected := `one = [ {0 = '0'}, {1 = '1'} ] ` var buf bytes.Buffer enc := toml.NewEncoder(&buf) enc.SetIndentTables(true) enc.SetTablesInline(true) enc.SetArraysMultiline(true) require.NoError(t, enc.Encode(x)) assert.Equal(t, expected, buf.String()) } type C3 struct { Value int `toml:",commented"` Values []int `toml:",commented"` } type C2 struct { Int int64 String string ArrayInts []int Structs []C3 } type C1 struct { Int int64 `toml:",commented"` String string `toml:",commented"` ArrayInts []int `toml:",commented"` Structs []C3 `toml:",commented"` } type Commented struct { Int int64 `toml:",commented"` String string `toml:",commented"` C1 C1 C2 C2 `toml:",commented"` // same as C1, but commented at top level } func TestMarshalCommented(t *testing.T) { c := Commented{ Int: 42, String: "root", C1: C1{ Int: 11, String: "C1", ArrayInts: []int{1, 2, 3}, Structs: []C3{ {Value: 100}, {Values: []int{4, 5, 6}}, }, }, C2: C2{ Int: 22, String: "C2", ArrayInts: []int{1, 2, 3}, Structs: []C3{ {Value: 100}, {Values: []int{4, 5, 6}}, }, }, } out, err := toml.Marshal(c) require.NoError(t, err) expected := `# Int = 42 # String = 'root' [C1] # Int = 11 # String = 'C1' # ArrayInts = [1, 2, 3] # [[C1.Structs]] # Value = 100 # Values = [] # [[C1.Structs]] # Value = 0 # Values = [4, 5, 6] # [C2] # Int = 22 # String = 'C2' # ArrayInts = [1, 2, 3] # [[C2.Structs]] # Value = 100 # Values = [] # [[C2.Structs]] # Value = 0 # Values = [4, 5, 6] ` require.Equal(t, expected, string(out)) } func ExampleMarshal() { type MyConfig struct { Version int Name string Tags []string } cfg := MyConfig{ Version: 2, Name: "go-toml", Tags: []string{"go", "toml"}, } b, err := toml.Marshal(cfg) if err != nil { panic(err) } fmt.Println(string(b)) // Output: // Version = 2 // Name = 'go-toml' // Tags = ['go', 'toml'] } // Example that uses the 'commented' field tag option to generate an example // configuration file that has commented out sections (example from // go-graphite/graphite-clickhouse). func ExampleMarshal_commented() { type Common struct { Listen string `toml:"listen" comment:"general listener"` PprofListen string `toml:"pprof-listen" comment:"listener to serve /debug/pprof requests. '-pprof' argument overrides it"` MaxMetricsPerTarget int `toml:"max-metrics-per-target" comment:"limit numbers of queried metrics per target in /render requests, 0 or negative = unlimited"` MemoryReturnInterval time.Duration `toml:"memory-return-interval" comment:"daemon will return the freed memory to the OS when it>0"` } type Costs struct { Cost *int `toml:"cost" comment:"default cost (for wildcarded equalence or matched with regex, or if no value cost set)"` ValuesCost map[string]int `toml:"values-cost" comment:"cost with some value (for equalence without wildcards) (additional tuning, usually not needed)"` } type ClickHouse struct { URL string `toml:"url" comment:"default url, see https://clickhouse.tech/docs/en/interfaces/http. Can be overwritten with query-params"` RenderMaxQueries int `toml:"render-max-queries" comment:"Max queries to render queiries"` RenderConcurrentQueries int `toml:"render-concurrent-queries" comment:"Concurrent queries to render queiries"` TaggedCosts map[string]*Costs `toml:"tagged-costs,commented"` TreeTable string `toml:"tree-table,commented"` ReverseTreeTable string `toml:"reverse-tree-table,commented"` DateTreeTable string `toml:"date-tree-table,commented"` DateTreeTableVersion int `toml:"date-tree-table-version,commented"` TreeTimeout time.Duration `toml:"tree-timeout,commented"` TagTable string `toml:"tag-table,commented"` ExtraPrefix string `toml:"extra-prefix" comment:"add extra prefix (directory in graphite) for all metrics, w/o trailing dot"` ConnectTimeout time.Duration `toml:"connect-timeout" comment:"TCP connection timeout"` DataTableLegacy string `toml:"data-table,commented"` RollupConfLegacy string `toml:"rollup-conf,commented"` MaxDataPoints int `toml:"max-data-points" comment:"max points per metric when internal-aggregation=true"` InternalAggregation bool `toml:"internal-aggregation" comment:"ClickHouse-side aggregation, see doc/aggregation.md"` } type Tags struct { Rules string `toml:"rules"` Date string `toml:"date"` ExtraWhere string `toml:"extra-where"` InputFile string `toml:"input-file"` OutputFile string `toml:"output-file"` } type Config struct { Common Common `toml:"common"` ClickHouse ClickHouse `toml:"clickhouse"` Tags Tags `toml:"tags,commented"` } cfg := &Config{ Common: Common{ Listen: ":9090", PprofListen: "", MaxMetricsPerTarget: 15000, // This is arbitrary value to protect CH from overload MemoryReturnInterval: 0, }, ClickHouse: ClickHouse{ URL: "http://localhost:8123?cancel_http_readonly_queries_on_client_close=1", ExtraPrefix: "", ConnectTimeout: time.Second, DataTableLegacy: "", RollupConfLegacy: "auto", MaxDataPoints: 1048576, InternalAggregation: true, }, Tags: Tags{}, } out, err := toml.Marshal(cfg) if err != nil { panic(err) } err = toml.Unmarshal(out, &cfg) if err != nil { panic(err) } fmt.Println(string(out)) // Output: // [common] // # general listener // listen = ':9090' // # listener to serve /debug/pprof requests. '-pprof' argument overrides it // pprof-listen = '' // # limit numbers of queried metrics per target in /render requests, 0 or negative = unlimited // max-metrics-per-target = 15000 // # daemon will return the freed memory to the OS when it>0 // memory-return-interval = 0 // // [clickhouse] // # default url, see https://clickhouse.tech/docs/en/interfaces/http. Can be overwritten with query-params // url = 'http://localhost:8123?cancel_http_readonly_queries_on_client_close=1' // # Max queries to render queiries // render-max-queries = 0 // # Concurrent queries to render queiries // render-concurrent-queries = 0 // # tree-table = '' // # reverse-tree-table = '' // # date-tree-table = '' // # date-tree-table-version = 0 // # tree-timeout = 0 // # tag-table = '' // # add extra prefix (directory in graphite) for all metrics, w/o trailing dot // extra-prefix = '' // # TCP connection timeout // connect-timeout = 1000000000 // # data-table = '' // # rollup-conf = 'auto' // # max points per metric when internal-aggregation=true // max-data-points = 1048576 // # ClickHouse-side aggregation, see doc/aggregation.md // internal-aggregation = true // // # [tags] // # rules = '' // # date = '' // # extra-where = '' // # input-file = '' // # output-file = '' } func TestReadmeComments(t *testing.T) { type TLS struct { Cipher string `toml:"cipher"` Version string `toml:"version"` } type Config struct { Host string `toml:"host" comment:"Host IP to connect to."` Port int `toml:"port" comment:"Port of the remote server."` Tls TLS `toml:"TLS,commented" comment:"Encryption parameters (optional)"` } example := Config{ Host: "127.0.0.1", Port: 4242, Tls: TLS{ Cipher: "AEAD-AES128-GCM-SHA256", Version: "TLS 1.3", }, } out, err := toml.Marshal(example) require.NoError(t, err) expected := `# Host IP to connect to. host = '127.0.0.1' # Port of the remote server. port = 4242 # Encryption parameters (optional) # [TLS] # cipher = 'AEAD-AES128-GCM-SHA256' # version = 'TLS 1.3' ` require.Equal(t, expected, string(out)) } go-toml-2.1.1/ossfuzz/000077500000000000000000000000001453566013500146245ustar00rootroot00000000000000go-toml-2.1.1/ossfuzz/fuzz.go000066400000000000000000000013561453566013500161560ustar00rootroot00000000000000//go:build go1.18 || go1.19 || go1.20 || go1.21 // +build go1.18 go1.19 go1.20 go1.21 package ossfuzz import ( "fmt" "reflect" "strings" "github.com/pelletier/go-toml/v2" ) func FuzzToml(data []byte) int { if len(data) >= 2048 { return 0 } if strings.Contains(string(data), "nan") { return 0 } var v interface{} err := toml.Unmarshal(data, &v) if err != nil { return 0 } encoded, err := toml.Marshal(v) if err != nil { panic(fmt.Sprintf("failed to marshal unmarshaled document: %s", err)) } var v2 interface{} err = toml.Unmarshal(encoded, &v2) if err != nil { panic(fmt.Sprintf("failed round trip: %s", err)) } if !reflect.DeepEqual(v, v2) { panic(fmt.Sprintf("not equal: %#+v %#+v", v, v2)) } return 1 } go-toml-2.1.1/strict.go000066400000000000000000000034151453566013500147430ustar00rootroot00000000000000package toml import ( "github.com/pelletier/go-toml/v2/internal/danger" "github.com/pelletier/go-toml/v2/internal/tracker" "github.com/pelletier/go-toml/v2/unstable" ) type strict struct { Enabled bool // Tracks the current key being processed. key tracker.KeyTracker missing []unstable.ParserError } func (s *strict) EnterTable(node *unstable.Node) { if !s.Enabled { return } s.key.UpdateTable(node) } func (s *strict) EnterArrayTable(node *unstable.Node) { if !s.Enabled { return } s.key.UpdateArrayTable(node) } func (s *strict) EnterKeyValue(node *unstable.Node) { if !s.Enabled { return } s.key.Push(node) } func (s *strict) ExitKeyValue(node *unstable.Node) { if !s.Enabled { return } s.key.Pop(node) } func (s *strict) MissingTable(node *unstable.Node) { if !s.Enabled { return } s.missing = append(s.missing, unstable.ParserError{ Highlight: keyLocation(node), Message: "missing table", Key: s.key.Key(), }) } func (s *strict) MissingField(node *unstable.Node) { if !s.Enabled { return } s.missing = append(s.missing, unstable.ParserError{ Highlight: keyLocation(node), Message: "missing field", Key: s.key.Key(), }) } func (s *strict) Error(doc []byte) error { if !s.Enabled || len(s.missing) == 0 { return nil } err := &StrictMissingError{ Errors: make([]DecodeError, 0, len(s.missing)), } for _, derr := range s.missing { derr := derr err.Errors = append(err.Errors, *wrapDecodeError(doc, &derr)) } return err } func keyLocation(node *unstable.Node) []byte { k := node.Key() hasOne := k.Next() if !hasOne { panic("should not be called with empty key") } start := k.Node().Data end := k.Node().Data for k.Next() { end = k.Node().Data } return danger.BytesRange(start, end) } go-toml-2.1.1/testdata/000077500000000000000000000000001453566013500147125ustar00rootroot00000000000000go-toml-2.1.1/testdata/fuzz/000077500000000000000000000000001453566013500157105ustar00rootroot00000000000000go-toml-2.1.1/testdata/fuzz/FuzzUnmarshal/000077500000000000000000000000001453566013500205215ustar00rootroot000000000000000c08b924aa26af23d16b77029b4f5567a96af93402e0472fef7b837b37580d7c000066400000000000000000000000601453566013500310310ustar00rootroot00000000000000go-toml-2.1.1/testdata/fuzz/FuzzUnmarshalgo test fuzz v1 []byte("0=0000-01-01 00:00:00") 249a017d8ffa0d88d41c594ada5399f764833f64050180cb39f106d12666853f000066400000000000000000000000471453566013500306300ustar00rootroot00000000000000go-toml-2.1.1/testdata/fuzz/FuzzUnmarshalgo test fuzz v1 []byte("\"\\n\"=\"\"") 391ce67b866d58464d067c5981e99951fd499896453a2fe2779a6bfe7df11bf5000066400000000000000000000000371453566013500310520ustar00rootroot00000000000000go-toml-2.1.1/testdata/fuzz/FuzzUnmarshalgo test fuzz v1 []byte("''=0") 49ae83283c5cfbed874bcacbfb342de107b69c2a3d3c35c2d99f67345fe89946000066400000000000000000000000471453566013500315210ustar00rootroot00000000000000go-toml-2.1.1/testdata/fuzz/FuzzUnmarshalgo test fuzz v1 []byte("0=0000-01-01") 69ea82e85bb4ad966ae86bb05eea3af69ed1a826c846f1bf2282f329b4d8fb36000066400000000000000000000000641453566013500315740ustar00rootroot00000000000000go-toml-2.1.1/testdata/fuzz/FuzzUnmarshalgo test fuzz v1 []byte("0=\"\"\"\\U00000000\"\"\"") 746910fac6deb42e7e62dce60b333c81c39d844a122c9075894c023400e8fdbf000066400000000000000000000000431453566013500311600ustar00rootroot00000000000000go-toml-2.1.1/testdata/fuzz/FuzzUnmarshalgo test fuzz v1 []byte("0=[[{}]]") 844d1171a3adc95e51627f0e9378bbaa193fe2d0284f4ba8ed19a5226a8cbc74000066400000000000000000000000471453566013500313230ustar00rootroot00000000000000go-toml-2.1.1/testdata/fuzz/FuzzUnmarshalgo test fuzz v1 []byte("\"\\b\"=\"\"") 900c54b5071113ae2a5365b3156377ddd775b9d38add61629544903cb9c94b00000066400000000000000000000000401453566013500305700ustar00rootroot00000000000000go-toml-2.1.1/testdata/fuzz/FuzzUnmarshalgo test fuzz v1 []byte("0=inf") c87e8eb4e8fa40fd3c89d8ed16a049ce81592ded710965ee29a4e506eb5427b3000066400000000000000000000000661453566013500314450ustar00rootroot00000000000000go-toml-2.1.1/testdata/fuzz/FuzzUnmarshalgo test fuzz v1 []byte("0=0000-01-01 00:00:00+00:00") d0e5d6fc3b865cfae5480c01f301b87c932722ae0c1d692deea413349ea028be000066400000000000000000000000411453566013500313600ustar00rootroot00000000000000go-toml-2.1.1/testdata/fuzz/FuzzUnmarshalgo test fuzz v1 []byte("0=[{}]") d2cecea5b8ee5f148419671cef0644178cdba1ee91d3f295be38da576e89ef40000066400000000000000000000000401453566013500315700ustar00rootroot00000000000000go-toml-2.1.1/testdata/fuzz/FuzzUnmarshalgo test fuzz v1 []byte("0=nan") go-toml-2.1.1/toml.abnf000066400000000000000000000155651453566013500147200ustar00rootroot00000000000000;; This document describes TOML's syntax, using the ABNF format (defined in ;; RFC 5234 -- https://www.ietf.org/rfc/rfc5234.txt). ;; ;; All valid TOML documents will match this description, however certain ;; invalid documents would need to be rejected as per the semantics described ;; in the supporting text description. ;; It is possible to try this grammar interactively, using instaparse. ;; http://instaparse.mojombo.com/ ;; ;; To do so, in the lower right, click on Options and change `:input-format` to ;; ':abnf'. Then paste this entire ABNF document into the grammar entry box ;; (above the options). Then you can type or paste a sample TOML document into ;; the beige box on the left. Tada! ;; Overall Structure toml = expression *( newline expression ) expression = ws [ comment ] expression =/ ws keyval ws [ comment ] expression =/ ws table ws [ comment ] ;; Whitespace ws = *wschar wschar = %x20 ; Space wschar =/ %x09 ; Horizontal tab ;; Newline newline = %x0A ; LF newline =/ %x0D.0A ; CRLF ;; Comment comment-start-symbol = %x23 ; # non-ascii = %x80-D7FF / %xE000-10FFFF non-eol = %x09 / %x20-7F / non-ascii comment = comment-start-symbol *non-eol ;; Key-Value pairs keyval = key keyval-sep val key = simple-key / dotted-key simple-key = quoted-key / unquoted-key unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _ quoted-key = basic-string / literal-string dotted-key = simple-key 1*( dot-sep simple-key ) dot-sep = ws %x2E ws ; . Period keyval-sep = ws %x3D ws ; = val = string / boolean / array / inline-table / date-time / float / integer ;; String string = ml-basic-string / basic-string / ml-literal-string / literal-string ;; Basic String basic-string = quotation-mark *basic-char quotation-mark quotation-mark = %x22 ; " basic-char = basic-unescaped / escaped basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii escaped = escape escape-seq-char escape = %x5C ; \ escape-seq-char = %x22 ; " quotation mark U+0022 escape-seq-char =/ %x5C ; \ reverse solidus U+005C escape-seq-char =/ %x62 ; b backspace U+0008 escape-seq-char =/ %x66 ; f form feed U+000C escape-seq-char =/ %x6E ; n line feed U+000A escape-seq-char =/ %x72 ; r carriage return U+000D escape-seq-char =/ %x74 ; t tab U+0009 escape-seq-char =/ %x75 4HEXDIG ; uXXXX U+XXXX escape-seq-char =/ %x55 8HEXDIG ; UXXXXXXXX U+XXXXXXXX ;; Multiline Basic String ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body ml-basic-string-delim ml-basic-string-delim = 3quotation-mark ml-basic-body = *mlb-content *( mlb-quotes 1*mlb-content ) [ mlb-quotes ] mlb-content = mlb-char / newline / mlb-escaped-nl mlb-char = mlb-unescaped / escaped mlb-quotes = 1*2quotation-mark mlb-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii mlb-escaped-nl = escape ws newline *( wschar / newline ) ;; Literal String literal-string = apostrophe *literal-char apostrophe apostrophe = %x27 ; ' apostrophe literal-char = %x09 / %x20-26 / %x28-7E / non-ascii ;; Multiline Literal String ml-literal-string = ml-literal-string-delim [ newline ] ml-literal-body ml-literal-string-delim ml-literal-string-delim = 3apostrophe ml-literal-body = *mll-content *( mll-quotes 1*mll-content ) [ mll-quotes ] mll-content = mll-char / newline mll-char = %x09 / %x20-26 / %x28-7E / non-ascii mll-quotes = 1*2apostrophe ;; Integer integer = dec-int / hex-int / oct-int / bin-int minus = %x2D ; - plus = %x2B ; + underscore = %x5F ; _ digit1-9 = %x31-39 ; 1-9 digit0-7 = %x30-37 ; 0-7 digit0-1 = %x30-31 ; 0-1 hex-prefix = %x30.78 ; 0x oct-prefix = %x30.6F ; 0o bin-prefix = %x30.62 ; 0b dec-int = [ minus / plus ] unsigned-dec-int unsigned-dec-int = DIGIT / digit1-9 1*( DIGIT / underscore DIGIT ) hex-int = hex-prefix HEXDIG *( HEXDIG / underscore HEXDIG ) oct-int = oct-prefix digit0-7 *( digit0-7 / underscore digit0-7 ) bin-int = bin-prefix digit0-1 *( digit0-1 / underscore digit0-1 ) ;; Float float = float-int-part ( exp / frac [ exp ] ) float =/ special-float float-int-part = dec-int frac = decimal-point zero-prefixable-int decimal-point = %x2E ; . zero-prefixable-int = DIGIT *( DIGIT / underscore DIGIT ) exp = "e" float-exp-part float-exp-part = [ minus / plus ] zero-prefixable-int special-float = [ minus / plus ] ( inf / nan ) inf = %x69.6e.66 ; inf nan = %x6e.61.6e ; nan ;; Boolean boolean = true / false true = %x74.72.75.65 ; true false = %x66.61.6C.73.65 ; false ;; Date and Time (as defined in RFC 3339) date-time = offset-date-time / local-date-time / local-date / local-time date-fullyear = 4DIGIT date-month = 2DIGIT ; 01-12 date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year time-delim = "T" / %x20 ; T, t, or space time-hour = 2DIGIT ; 00-23 time-minute = 2DIGIT ; 00-59 time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second rules time-secfrac = "." 1*DIGIT time-numoffset = ( "+" / "-" ) time-hour ":" time-minute time-offset = "Z" / time-numoffset partial-time = time-hour ":" time-minute ":" time-second [ time-secfrac ] full-date = date-fullyear "-" date-month "-" date-mday full-time = partial-time time-offset ;; Offset Date-Time offset-date-time = full-date time-delim full-time ;; Local Date-Time local-date-time = full-date time-delim partial-time ;; Local Date local-date = full-date ;; Local Time local-time = partial-time ;; Array array = array-open [ array-values ] ws-comment-newline array-close array-open = %x5B ; [ array-close = %x5D ; ] array-values = ws-comment-newline val ws-comment-newline array-sep array-values array-values =/ ws-comment-newline val ws-comment-newline [ array-sep ] array-sep = %x2C ; , Comma ws-comment-newline = *( wschar / [ comment ] newline ) ;; Table table = std-table / array-table ;; Standard Table std-table = std-table-open key std-table-close std-table-open = %x5B ws ; [ Left square bracket std-table-close = ws %x5D ; ] Right square bracket ;; Inline Table inline-table = inline-table-open [ inline-table-keyvals ] inline-table-close inline-table-open = %x7B ws ; { inline-table-close = ws %x7D ; } inline-table-sep = ws %x2C ws ; , Comma inline-table-keyvals = keyval [ inline-table-sep inline-table-keyvals ] ;; Array Table array-table = array-table-open key array-table-close array-table-open = %x5B.5B ws ; [[ Double left square bracket array-table-close = ws %x5D.5D ; ]] Double right square bracket ;; Built-in ABNF terms, reproduced here for clarity ALPHA = %x41-5A / %x61-7A ; A-Z / a-z DIGIT = %x30-39 ; 0-9 HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F" go-toml-2.1.1/toml_testgen_support_test.go000066400000000000000000000026471453566013500210000ustar00rootroot00000000000000//go:generate go run github.com/toml-lang/toml-test/cmd/toml-test@master -copy ./tests //go:generate go run ./cmd/tomltestgen/main.go -o toml_testgen_test.go // This is a support file for toml_testgen_test.go package toml_test import ( "encoding/json" "testing" "github.com/pelletier/go-toml/v2" "github.com/pelletier/go-toml/v2/internal/testsuite" "github.com/stretchr/testify/require" ) func testgenInvalid(t *testing.T, input string) { t.Helper() t.Logf("Input TOML:\n%s", input) doc := map[string]interface{}{} err := testsuite.Unmarshal([]byte(input), &doc) if err == nil { out, err := json.Marshal(doc) if err != nil { panic("could not marshal map to json") } t.Log("JSON output from unmarshal:", string(out)) t.Fatalf("test did not fail") } } func testgenValid(t *testing.T, input string, jsonRef string) { t.Helper() t.Logf("Input TOML:\n%s", input) // TODO: change this to interface{} var doc map[string]interface{} err := testsuite.Unmarshal([]byte(input), &doc) if err != nil { if de, ok := err.(*toml.DecodeError); ok { t.Logf("%s\n%s", err, de) } t.Fatalf("failed parsing toml: %s", err) } j, err := testsuite.ValueToTaggedJSON(doc) require.NoError(t, err) var ref interface{} err = json.Unmarshal([]byte(jsonRef), &ref) require.NoError(t, err) var actual interface{} err = json.Unmarshal([]byte(j), &actual) require.NoError(t, err) testsuite.CmpJSON(t, "", ref, actual) } go-toml-2.1.1/toml_testgen_test.go000066400000000000000000004726771453566013500172220ustar00rootroot00000000000000// Generated by tomltestgen for toml-test ref master on 2023-10-26T19:48:52+02:00 package toml_test import ( "testing" ) func TestTOMLTest_Invalid_Tests_Invalid_Array_DoubleComma1(t *testing.T) { input := "double-comma-1 = [1,,2]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_DoubleComma2(t *testing.T) { input := "double-comma-2 = [1,2,,]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_ExtendDefinedAot(t *testing.T) { input := "[[tab.arr]]\n[tab]\narr.val1=1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_ExtendingTable(t *testing.T) { input := "a = [{ b = 1 }]\n\n# Cannot extend tables within static arrays\n# https://github.com/toml-lang/toml/issues/908\n[a.c]\nfoo = 1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_MissingSeparator1(t *testing.T) { input := "arrr = [true false]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_MissingSeparator2(t *testing.T) { input := "wrong = [ 1 2 3 ]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_NoClose1(t *testing.T) { input := "no-close-1 = [ 1, 2, 3\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_NoClose2(t *testing.T) { input := "no-close-2 = [1,\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_NoClose3(t *testing.T) { input := "no-close-3 = [42 #]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_NoClose4(t *testing.T) { input := "no-close-4 = [{ key = 42\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_NoClose5(t *testing.T) { input := "no-close-5 = [{ key = 42}\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_NoClose6(t *testing.T) { input := "no-close-6 = [{ key = 42 #}]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_NoClose7(t *testing.T) { input := "no-close-7 = [{ key = 42} #]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_NoClose8(t *testing.T) { input := "no-close-8 = [\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_NoCloseTable1(t *testing.T) { input := "x = [{ key = 42\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_NoCloseTable2(t *testing.T) { input := "x = [{ key = 42 #\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_NoComma1(t *testing.T) { input := "no-comma-1 = [true false]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_NoComma2(t *testing.T) { input := "no-comma-2 = [ 1 2 3 ]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_NoComma3(t *testing.T) { input := "no-comma-3 = [ 1 #,]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_OnlyComma1(t *testing.T) { input := "only-comma-1 = [,]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_OnlyComma2(t *testing.T) { input := "only-comma-2 = [,,]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_Tables1(t *testing.T) { input := "# INVALID TOML DOC\nfruit = []\n\n[[fruit]] # Not allowed\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_Tables2(t *testing.T) { input := "# INVALID TOML DOC\n[[fruit]]\n name = \"apple\"\n\n [[fruit.variety]]\n name = \"red delicious\"\n\n # This table conflicts with the previous table\n [fruit.variety]\n name = \"granny smith\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_TextAfterArrayEntries(t *testing.T) { input := "array = [\n \"Is there life after an array separator?\", No\n \"Entry\"\n]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_TextBeforeArraySeparator(t *testing.T) { input := "array = [\n \"Is there life before an array separator?\" No,\n \"Entry\"\n]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Array_TextInArray(t *testing.T) { input := "array = [\n \"Entry 1\",\n I don't belong,\n \"Entry 2\",\n]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Bool_AlmostFalseWithExtra(t *testing.T) { input := "almost-false-with-extra = falsify\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Bool_AlmostFalse(t *testing.T) { input := "almost-false = fals\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Bool_AlmostTrueWithExtra(t *testing.T) { input := "almost-true-with-extra = truthy\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Bool_AlmostTrue(t *testing.T) { input := "almost-true = tru\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Bool_CapitalizedFalse(t *testing.T) { input := "capitalized-false = False\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Bool_CapitalizedTrue(t *testing.T) { input := "capitalized-true = True\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Bool_JustF(t *testing.T) { input := "just-f = f\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Bool_JustT(t *testing.T) { input := "just-t = t\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Bool_MixedCaseFalse(t *testing.T) { input := "mixed-case-false = falsE\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Bool_MixedCaseTrue(t *testing.T) { input := "mixed-case-true = trUe\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Bool_MixedCase(t *testing.T) { input := "mixed-case = valid = False\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Bool_StartingSameFalse(t *testing.T) { input := "starting-same-false = falsey\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Bool_StartingSameTrue(t *testing.T) { input := "starting-same-true = truer\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Bool_WrongCaseFalse(t *testing.T) { input := "wrong-case-false = FALSE\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Bool_WrongCaseTrue(t *testing.T) { input := "wrong-case-true = TRUE\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_BareCr(t *testing.T) { input := "# The following line contains a single carriage return control character\r\n\r" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_BareFormfeed(t *testing.T) { input := "bare-formfeed = \f\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_BareNull(t *testing.T) { input := "bare-null = \"some value\" \x00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_BareVerticalTab(t *testing.T) { input := "bare-vertical-tab = \v\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_CommentCr(t *testing.T) { input := "comment-cr = \"Carriage return in comment\" # \ra=1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_CommentDel(t *testing.T) { input := "comment-del = \"0x7f\" # \x7f\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_CommentFf(t *testing.T) { input := "comment-ff = \"0x7f\" # \f\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_CommentLf(t *testing.T) { input := "comment-lf = \"ctrl-P\" # \x10\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_CommentNull(t *testing.T) { input := "comment-null = \"null\" # \x00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_CommentUs(t *testing.T) { input := "comment-us = \"ctrl-_\" # \x1f\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_MultiCr(t *testing.T) { input := "multi-cr = \"\"\"null\r\"\"\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_MultiDel(t *testing.T) { input := "multi-del = \"\"\"null\x7f\"\"\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_MultiLf(t *testing.T) { input := "multi-lf = \"\"\"null\x10\"\"\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_MultiNull(t *testing.T) { input := "multi-null = \"\"\"null\x00\"\"\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_MultiUs(t *testing.T) { input := "multi-us = \"\"\"null\x1f\"\"\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_RawmultiCd(t *testing.T) { input := "rawmulti-cd = '''null\r'''\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_RawmultiDel(t *testing.T) { input := "rawmulti-del = '''null\x7f'''\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_RawmultiLf(t *testing.T) { input := "rawmulti-lf = '''null\x10'''\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_RawmultiNull(t *testing.T) { input := "rawmulti-null = '''null\x00'''\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_RawmultiUs(t *testing.T) { input := "rawmulti-us = '''null\x1f'''\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_RawstringCr(t *testing.T) { input := "rawstring-cr = 'null\r'\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_RawstringDel(t *testing.T) { input := "rawstring-del = 'null\x7f'\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_RawstringLf(t *testing.T) { input := "rawstring-lf = 'null\x10'\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_RawstringNull(t *testing.T) { input := "rawstring-null = 'null\x00'\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_RawstringUs(t *testing.T) { input := "rawstring-us = 'null\x1f'\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_StringBs(t *testing.T) { input := "string-bs = \"backspace\b\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_StringCr(t *testing.T) { input := "string-cr = \"null\r\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_StringDel(t *testing.T) { input := "string-del = \"null\x7f\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_StringLf(t *testing.T) { input := "string-lf = \"null\x10\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_StringNull(t *testing.T) { input := "string-null = \"null\x00\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Control_StringUs(t *testing.T) { input := "string-us = \"null\x1f\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Datetime_Feb29(t *testing.T) { input := "\"not a leap year\" = 2100-02-29T15:15:15Z\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Datetime_Feb30(t *testing.T) { input := "\"only 28 or 29 days in february\" = 1988-02-30T15:15:15Z\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Datetime_HourOver(t *testing.T) { input := "# time-hour = 2DIGIT ; 00-23\nd = 2006-01-01T24:00:00-00:00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Datetime_MdayOver(t *testing.T) { input := "# date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on\n# ; month/year\nd = 2006-01-32T00:00:00-00:00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Datetime_MdayUnder(t *testing.T) { input := "# date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on\n# ; month/year\nd = 2006-01-00T00:00:00-00:00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Datetime_MinuteOver(t *testing.T) { input := "# time-minute = 2DIGIT ; 00-59\nd = 2006-01-01T00:60:00-00:00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Datetime_MonthOver(t *testing.T) { input := "# date-month = 2DIGIT ; 01-12\nd = 2006-13-01T00:00:00-00:00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Datetime_MonthUnder(t *testing.T) { input := "# date-month = 2DIGIT ; 01-12\nd = 2007-00-01T00:00:00-00:00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Datetime_NoLeadsMonth(t *testing.T) { input := "# Month \"7\" instead of \"07\"; the leading zero is required.\nno-leads = 1987-7-05T17:45:00Z\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Datetime_NoLeadsWithMilli(t *testing.T) { input := "# Day \"5\" instead of \"05\"; the leading zero is required.\nwith-milli = 1987-07-5T17:45:00.12Z\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Datetime_NoLeads(t *testing.T) { input := "# Month \"7\" instead of \"07\"; the leading zero is required.\nno-leads = 1987-7-05T17:45:00Z\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Datetime_NoSecs(t *testing.T) { input := "# No seconds in time.\nno-secs = 1987-07-05T17:45Z\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Datetime_NoT(t *testing.T) { input := "# No \"t\" or \"T\" between the date and time.\nno-t = 1987-07-0517:45:00Z\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Datetime_SecondOver(t *testing.T) { input := "# time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second\n# ; rules\nd = 2006-01-01T00:00:61-00:00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Datetime_TimeNoLeads(t *testing.T) { input := "# Leading 0 is always required.\nd = 2023-10-01T1:32:00Z\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Encoding_BadCodepoint(t *testing.T) { input := "# Invalid codepoint U+D800 : \xed\xa0\x80\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Encoding_BadUtf8AtEnd(t *testing.T) { input := "# There is a 0xda at after the quotes, and no EOL at the end of the file.\n#\n# This is a bit of an edge case: This indicates there should be two bytes\n# (0b1101_1010) but there is no byte to follow because it's the end of the file.\nx = \"\"\"\"\"\"\xda" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Encoding_BadUtf8InComment(t *testing.T) { input := "# \xc3\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Encoding_BadUtf8InMultilineLiteral(t *testing.T) { input := "# The following line contains an invalid UTF-8 sequence.\nbad = '''\xc3'''\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Encoding_BadUtf8InMultiline(t *testing.T) { input := "# The following line contains an invalid UTF-8 sequence.\nbad = \"\"\"\xc3\"\"\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Encoding_BadUtf8InStringLiteral(t *testing.T) { input := "# The following line contains an invalid UTF-8 sequence.\nbad = '\xc3'\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Encoding_BadUtf8InString(t *testing.T) { input := "# The following line contains an invalid UTF-8 sequence.\nbad = \"\xc3\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Encoding_BomNotAtStart1(t *testing.T) { input := "bom-not-at-start \xff\xfd\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Encoding_BomNotAtStart2(t *testing.T) { input := "bom-not-at-start= \xff\xfd\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Encoding_Utf16Bom(t *testing.T) { input := "\xfe\xff\x00#\x00 \x00U\x00T\x00F\x00-\x001\x006\x00 \x00w\x00i\x00t\x00h\x00 \x00B\x00O\x00M\x00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Encoding_Utf16(t *testing.T) { input := "\x00#\x00 \x00U\x00T\x00F\x00-\x001\x006\x00 \x00w\x00i\x00t\x00h\x00o\x00u\x00t\x00 \x00B\x00O\x00M\x00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_DoublePoint1(t *testing.T) { input := "double-point-1 = 0..1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_DoublePoint2(t *testing.T) { input := "double-point-2 = 0.1.2\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_ExpDoubleE1(t *testing.T) { input := "exp-double-e-1 = 1ee2\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_ExpDoubleE2(t *testing.T) { input := "exp-double-e-2 = 1e2e3\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_ExpDoubleUs(t *testing.T) { input := "exp-double-us = 1e__23\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_ExpLeadingUs(t *testing.T) { input := "exp-leading-us = 1e_23\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_ExpPoint1(t *testing.T) { input := "exp-point-1 = 1e2.3\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_ExpPoint2(t *testing.T) { input := "exp-point-2 = 1.e2\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_ExpTrailingUs(t *testing.T) { input := "exp-trailing-us = 1e23_\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_InfCapital(t *testing.T) { input := "v = Inf\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_InfIncomplete1(t *testing.T) { input := "inf-incomplete-1 = in\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_InfIncomplete2(t *testing.T) { input := "inf-incomplete-2 = +in\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_InfIncomplete3(t *testing.T) { input := "inf-incomplete-3 = -in\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_Inf_underscore(t *testing.T) { input := "inf_underscore = in_f\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_LeadingPointNeg(t *testing.T) { input := "leading-point-neg = -.12345\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_LeadingPointPlus(t *testing.T) { input := "leading-point-plus = +.12345\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_LeadingPoint(t *testing.T) { input := "leading-point = .12345\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_LeadingUs(t *testing.T) { input := "leading-us = _1.2\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_LeadingZeroNeg(t *testing.T) { input := "leading-zero-neg = -03.14\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_LeadingZeroPlus(t *testing.T) { input := "leading-zero-plus = +03.14\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_LeadingZero(t *testing.T) { input := "leading-zero = 03.14\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_NanCapital(t *testing.T) { input := "v = NaN\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_NanIncomplete1(t *testing.T) { input := "nan-incomplete-1 = na\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_NanIncomplete2(t *testing.T) { input := "nan-incomplete-2 = +na\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_NanIncomplete3(t *testing.T) { input := "nan-incomplete-3 = -na\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_Nan_underscore(t *testing.T) { input := "nan_underscore = na_n\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_TrailingPointMin(t *testing.T) { input := "trailing-point-min = -1.\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_TrailingPointPlus(t *testing.T) { input := "trailing-point-plus = +1.\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_TrailingPoint(t *testing.T) { input := "trailing-point = 1.\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_TrailingUsExp1(t *testing.T) { input := "trailing-us-exp-1 = 1_e2\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_TrailingUsExp2(t *testing.T) { input := "trailing-us-exp-2 = 1.2_e2\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_TrailingUs(t *testing.T) { input := "trailing-us = 1.2_\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_UsAfterPoint(t *testing.T) { input := "us-after-point = 1._2\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Float_UsBeforePoint(t *testing.T) { input := "us-before-point = 1_.2\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_BadKeySyntax(t *testing.T) { input := "tbl = { a = 1, [b] }\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_DoubleComma(t *testing.T) { input := "t = {x=3,,y=4}\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_DuplicateKey1(t *testing.T) { input := "# Duplicate keys within an inline table are invalid\na={b=1, b=2}\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_DuplicateKey2(t *testing.T) { input := "table1 = { table2.dupe = 1, table2.dupe = 2 }\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_DuplicateKey3(t *testing.T) { input := "tbl = { fruit = { apple.color = \"red\" }, fruit.apple.texture = { smooth = true } }\n\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_DuplicateKey4(t *testing.T) { input := "tbl = { a.b = \"a_b\", a.b.c = \"a_b_c\" }\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_Empty1(t *testing.T) { input := "t = {,}\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_Empty2(t *testing.T) { input := "t = {,\n}\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_Empty3(t *testing.T) { input := "t = {\n,\n}\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_Linebreak1(t *testing.T) { input := "# No newlines are allowed between the curly braces unless they are valid within\n# a value.\nsimple = { a = 1 \n}\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_Linebreak2(t *testing.T) { input := "t = {a=1,\nb=2}\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_Linebreak3(t *testing.T) { input := "t = {a=1\n,b=2}\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_Linebreak4(t *testing.T) { input := "json_like = {\n first = \"Tom\",\n last = \"Preston-Werner\"\n}\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_NoClose1(t *testing.T) { input := "a={\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_NoClose2(t *testing.T) { input := "a={b=1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_NoComma1(t *testing.T) { input := "t = {x = 3 y = 4}\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_NoComma2(t *testing.T) { input := "arrr = { comma-missing = true valid-toml = false }\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_Overwrite1(t *testing.T) { input := "a.b=0\n# Since table \"a\" is already defined, it can't be replaced by an inline table.\na={}\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_Overwrite2(t *testing.T) { input := "a={}\n# Inline tables are immutable and can't be extended\n[a.b]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_Overwrite3(t *testing.T) { input := "a = { b = 1 }\na.b = 2\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_Overwrite4(t *testing.T) { input := "inline-t = { nest = {} }\n\n[[inline-t.nest]]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_Overwrite5(t *testing.T) { input := "inline-t = { nest = {} }\n\n[inline-t.nest]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_Overwrite6(t *testing.T) { input := "a = { b = 1, b.c = 2 }\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_Overwrite7(t *testing.T) { input := "tab = { inner.table = [{}], inner.table.val = \"bad\" }" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_Overwrite8(t *testing.T) { input := "tab = { inner = { dog = \"best\" }, inner.cat = \"worst\" }" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_Overwrite9(t *testing.T) { input := "[tab.nested]\ninline-t = { nest = {} }\n\n[tab]\nnested.inline-t.nest = 2\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_InlineTable_TrailingComma(t *testing.T) { input := "# A terminating comma (also called trailing comma) is not permitted after the\n# last key/value pair in an inline table\nabc = { abc = 123, }\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_CapitalBin(t *testing.T) { input := "capital-bin = 0B0\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_CapitalHex(t *testing.T) { input := "capital-hex = 0X1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_CapitalOct(t *testing.T) { input := "capital-oct = 0O0\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_DoubleSignNex(t *testing.T) { input := "double-sign-nex = --99\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_DoubleSignPlus(t *testing.T) { input := "double-sign-plus = ++99\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_DoubleUs(t *testing.T) { input := "double-us = 1__23\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_IncompleteBin(t *testing.T) { input := "incomplete-bin = 0b\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_IncompleteHex(t *testing.T) { input := "incomplete-hex = 0x\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_IncompleteOct(t *testing.T) { input := "incomplete-oct = 0o\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_InvalidBin(t *testing.T) { input := "invalid-bin = 0b0012\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_InvalidHex1(t *testing.T) { input := "invalid-hex-1 = 0xaafz\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_InvalidHex2(t *testing.T) { input := "invalid-hex-2 = 0xgabba00f1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_InvalidHex(t *testing.T) { input := "invalid-hex = 0xaafz\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_InvalidOct(t *testing.T) { input := "invalid-oct = 0o778\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_LeadingUsBin(t *testing.T) { input := "leading-us-bin = _0b1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_LeadingUsHex(t *testing.T) { input := "leading-us-hex = _0x1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_LeadingUsOct(t *testing.T) { input := "leading-us-oct = _0o1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_LeadingUs(t *testing.T) { input := "leading-us = _123\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_LeadingZero1(t *testing.T) { input := "leading-zero-1 = 01\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_LeadingZero2(t *testing.T) { input := "leading-zero-2 = 00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_LeadingZero3(t *testing.T) { input := "leading-zero-3 = 0_0\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_LeadingZeroSign1(t *testing.T) { input := "leading-zero-sign-1 = -01\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_LeadingZeroSign2(t *testing.T) { input := "leading-zero-sign-2 = +01\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_LeadingZeroSign3(t *testing.T) { input := "leading-zero-sign-3 = +0_1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_NegativeBin(t *testing.T) { input := "negative-bin = -0b11010110\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_NegativeHex(t *testing.T) { input := "negative-hex = -0xff\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_NegativeOct(t *testing.T) { input := "negative-oct = -0o755\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_PositiveBin(t *testing.T) { input := "positive-bin = +0b11010110\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_PositiveHex(t *testing.T) { input := "positive-hex = +0xff\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_PositiveOct(t *testing.T) { input := "positive-oct = +0o755\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_TextAfterInteger(t *testing.T) { input := "answer = 42 the ultimate answer?\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_TrailingUsBin(t *testing.T) { input := "trailing-us-bin = 0b1_\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_TrailingUsHex(t *testing.T) { input := "trailing-us-hex = 0x1_\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_TrailingUsOct(t *testing.T) { input := "trailing-us-oct = 0o1_\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_TrailingUs(t *testing.T) { input := "trailing-us = 123_\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_UsAfterBin(t *testing.T) { input := "us-after-bin = 0b_1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_UsAfterHex(t *testing.T) { input := "us-after-hex = 0x_1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Integer_UsAfterOct(t *testing.T) { input := "us-after-oct = 0o_1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_AfterArray(t *testing.T) { input := "[[agencies]] owner = \"S Cjelli\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_AfterTable(t *testing.T) { input := "[error] this = \"should not be here\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_AfterValue(t *testing.T) { input := "first = \"Tom\" last = \"Preston-Werner\" # INVALID\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_BareInvalidCharacter(t *testing.T) { input := "bare!key = 123\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_DottedRedefineTable1(t *testing.T) { input := "a = false\na.b = true\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_DottedRedefineTable2(t *testing.T) { input := "# Defined a.b as int\na.b = 1\n# Tries to access it as table: error\na.b.c = 2\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_DuplicateKeys(t *testing.T) { input := "dupe = false\ndupe = true\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_Duplicate(t *testing.T) { input := "# DO NOT DO THIS\nname = \"Tom\"\nname = \"Pradyun\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_Empty(t *testing.T) { input := " = 1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_EndInEscape(t *testing.T) { input := "\"backslash is the last char\\\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_Escape(t *testing.T) { input := "\\u00c0 = \"latin capital letter A with grave\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_Hash(t *testing.T) { input := "a# = 1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_Multiline(t *testing.T) { input := "\"\"\"long\nkey\"\"\" = 1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_Newline(t *testing.T) { input := "barekey\n = 123\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_NoEol(t *testing.T) { input := "a = 1 b = 2\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_OpenBracket(t *testing.T) { input := "[abc = 1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_PartialQuoted(t *testing.T) { input := "partial\"quoted\" = 5\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_QuotedUnclosed1(t *testing.T) { input := "\"key = x\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_QuotedUnclosed2(t *testing.T) { input := "\"key\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_SingleOpenBracket(t *testing.T) { input := "[\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_Space(t *testing.T) { input := "a b = 1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_SpecialCharacter(t *testing.T) { input := "μ = \"greek small letter mu\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_StartBracket(t *testing.T) { input := "[a]\n[xyz = 5\n[b]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_StartDot(t *testing.T) { input := ".key = 1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_TwoEquals(t *testing.T) { input := "key= = 1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_TwoEquals2(t *testing.T) { input := "a==1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_TwoEquals3(t *testing.T) { input := "a=b=1\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_WithoutValue1(t *testing.T) { input := "key\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_WithoutValue2(t *testing.T) { input := "key = \n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_WithoutValue3(t *testing.T) { input := "\"key\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_WithoutValue4(t *testing.T) { input := "\"key\" = \n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_WithoutValue5(t *testing.T) { input := "fs.fw\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_WithoutValue6(t *testing.T) { input := "fs.fw =\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Key_WithoutValue7(t *testing.T) { input := "fs.\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDate_Feb29(t *testing.T) { input := "\"not a leap year\" = 2100-02-29\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDate_Feb30(t *testing.T) { input := "\"only 28 or 29 days in february\" = 1988-02-30\n\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDate_MdayOver(t *testing.T) { input := "# date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on\n# ; month/year\nd = 2006-01-32\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDate_MdayUnder(t *testing.T) { input := "# date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on\n# ; month/year\nd = 2006-01-00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDate_MonthOver(t *testing.T) { input := "# date-month = 2DIGIT ; 01-12\nd = 2006-13-01\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDate_MonthUnder(t *testing.T) { input := "# date-month = 2DIGIT ; 01-12\nd = 2007-00-01\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDate_NoLeadsWithMilli(t *testing.T) { input := "# Day \"5\" instead of \"05\"; the leading zero is required.\nwith-milli = 1987-07-5\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDate_NoLeads(t *testing.T) { input := "# Month \"7\" instead of \"07\"; the leading zero is required.\nno-leads = 1987-7-05\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDate_TrailingT(t *testing.T) { input := "# Date cannot end with trailing T\nd = 2006-01-30T\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDatetime_Feb29(t *testing.T) { input := "\"not a leap year\" = 2100-02-29T15:15:15\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDatetime_Feb30(t *testing.T) { input := "\"only 28 or 29 days in february\" = 1988-02-30T15:15:15\n\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDatetime_HourOver(t *testing.T) { input := "# time-hour = 2DIGIT ; 00-23\nd = 2006-01-01T24:00:00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDatetime_MdayOver(t *testing.T) { input := "# date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on\n# ; month/year\nd = 2006-01-32T00:00:00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDatetime_MdayUnder(t *testing.T) { input := "# date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on\n# ; month/year\nd = 2006-01-00T00:00:00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDatetime_MinuteOver(t *testing.T) { input := "# time-minute = 2DIGIT ; 00-59\nd = 2006-01-01T00:60:00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDatetime_MonthOver(t *testing.T) { input := "# date-month = 2DIGIT ; 01-12\nd = 2006-13-01T00:00:00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDatetime_MonthUnder(t *testing.T) { input := "# date-month = 2DIGIT ; 01-12\nd = 2007-00-01T00:00:00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDatetime_NoLeadsWithMilli(t *testing.T) { input := "# Day \"5\" instead of \"05\"; the leading zero is required.\nwith-milli = 1987-07-5T17:45:00.12\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDatetime_NoLeads(t *testing.T) { input := "# Month \"7\" instead of \"07\"; the leading zero is required.\nno-leads = 1987-7-05T17:45:00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDatetime_NoSecs(t *testing.T) { input := "# No seconds in time.\nno-secs = 1987-07-05T17:45\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDatetime_NoT(t *testing.T) { input := "# No \"t\" or \"T\" between the date and time.\nno-t = 1987-07-0517:45:00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDatetime_SecondOver(t *testing.T) { input := "# time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second\n# ; rules\nd = 2006-01-01T00:00:61\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalDatetime_TimeNoLeads(t *testing.T) { input := "# Leading 0 is always required.\nd = 2023-10-01T1:32:00Z\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalTime_HourOver(t *testing.T) { input := "# time-hour = 2DIGIT ; 00-23\nd = 24:00:00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalTime_MinuteOver(t *testing.T) { input := "# time-minute = 2DIGIT ; 00-59\nd = 00:60:00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalTime_NoSecs(t *testing.T) { input := "# No seconds in time.\nno-secs = 17:45\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalTime_SecondOver(t *testing.T) { input := "# time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second\n# ; rules\nd = 00:00:61\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalTime_TimeNoLeads2(t *testing.T) { input := "# Leading 0 is always required.\nd = 01:32:0\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_LocalTime_TimeNoLeads(t *testing.T) { input := "# Leading 0 is always required.\nd = 1:32:00\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Spec_InlineTable20(t *testing.T) { input := "[product]\ntype = { name = \"Nail\" }\ntype.edible = false # INVALID\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Spec_InlineTable30(t *testing.T) { input := "[product]\ntype.name = \"Nail\"\ntype = { edible = false } # INVALID\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Spec_KeyValuePair1(t *testing.T) { input := "key = # INVALID\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Spec_Keys2(t *testing.T) { input := "= \"no key name\" # INVALID\n\"\" = \"blank\" # VALID but discouraged\n'' = 'blank' # VALID but discouraged\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Spec_String40(t *testing.T) { input := "str4 = \"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"\nstr5 = \"\"\"Here are three quotation marks: \"\"\".\"\"\" # INVALID\nstr5 = \"\"\"Here are three quotation marks: \"\"\\\".\"\"\"\nstr6 = \"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"\n\n# \"This,\" she said, \"is just a pointless statement.\"\nstr7 = \"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Spec_String70(t *testing.T) { input := "quot15 = '''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"'''\n\napos15 = '''Here are fifteen apostrophes: '''''''''''''''''' # INVALID\napos15 = \"Here are fifteen apostrophes: '''''''''''''''\"\n\n# 'That,' she said, 'is still pointless.'\nstr = ''''That,' she said, 'is still pointless.''''\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Spec_Table90(t *testing.T) { input := "[fruit]\napple.color = \"red\"\napple.taste.sweet = true\n\n[fruit.apple] # INVALID\n# [fruit.apple.taste] # INVALID\n\n[fruit.apple.texture] # you can add sub-tables\nsmooth = true\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Spec_Table91(t *testing.T) { input := "[fruit]\napple.color = \"red\"\napple.taste.sweet = true\n\n# [fruit.apple] # INVALID\n[fruit.apple.taste] # INVALID\n\n[fruit.apple.texture] # you can add sub-tables\nsmooth = true\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BadByteEscape(t *testing.T) { input := "naughty = \"\\xAg\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BadConcat(t *testing.T) { input := "no_concat = \"first\" \"second\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BadEscape1(t *testing.T) { input := "invalid-escape = \"This string has a bad \\a escape character.\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BadEscape2(t *testing.T) { input := "invalid-escape = \"This string has a bad \\ escape character.\"\n\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BadEscape3(t *testing.T) { input := "backslash = \"\\\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BadHexEsc1(t *testing.T) { input := "bad-hex-esc-1 = \"\\x0g\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BadHexEsc2(t *testing.T) { input := "bad-hex-esc-2 = \"\\xG0\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BadHexEsc3(t *testing.T) { input := "bad-hex-esc-3 = \"\\x\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BadHexEsc4(t *testing.T) { input := "bad-hex-esc-4 = \"\\x 50\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BadHexEsc5(t *testing.T) { input := "bad-hex-esc-5 = \"\\x 50\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BadMultiline(t *testing.T) { input := "multi = \"first line\nsecond line\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BadSlashEscape(t *testing.T) { input := "invalid-escape = \"This string has a bad \\/ escape character.\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BadUniEsc1(t *testing.T) { input := "bad-uni-esc-1 = \"val\\ue\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BadUniEsc2(t *testing.T) { input := "bad-uni-esc-2 = \"val\\Ux\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BadUniEsc3(t *testing.T) { input := "bad-uni-esc-3 = \"val\\U0000000\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BadUniEsc4(t *testing.T) { input := "bad-uni-esc-4 = \"val\\U0000\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BadUniEsc5(t *testing.T) { input := "bad-uni-esc-5 = \"val\\Ugggggggg\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BadUniEsc6(t *testing.T) { input := "bad-uni-esc-6 = \"This string contains a non scalar unicode codepoint \\uD801\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BadUniEsc7(t *testing.T) { input := "bad-uni-esc-7 = \"\\uabag\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BasicByteEscapes(t *testing.T) { input := "answer = \"\\x33\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BasicMultilineOutOfRangeUnicodeEscape1(t *testing.T) { input := "a = \"\"\"\\UFFFFFFFF\"\"\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BasicMultilineOutOfRangeUnicodeEscape2(t *testing.T) { input := "a = \"\"\"\\U00D80000\"\"\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BasicMultilineQuotes(t *testing.T) { input := "str5 = \"\"\"Here are three quotation marks: \"\"\".\"\"\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BasicMultilineUnknownEscape(t *testing.T) { input := "a = \"\"\"\\@\"\"\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BasicOutOfRangeUnicodeEscape1(t *testing.T) { input := "a = \"\\UFFFFFFFF\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BasicOutOfRangeUnicodeEscape2(t *testing.T) { input := "a = \"\\U00D80000\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_BasicUnknownEscape(t *testing.T) { input := "a = \"\\@\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_LiteralMultilineQuotes1(t *testing.T) { input := "a = '''6 apostrophes: ''''''\n\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_LiteralMultilineQuotes2(t *testing.T) { input := "a = '''15 apostrophes: ''''''''''''''''''\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_MissingQuotes(t *testing.T) { input := "name = value\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_MultilineBadEscape1(t *testing.T) { input := "k = \"\"\"t\\a\"\"\"\n\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_MultilineBadEscape2(t *testing.T) { input := "# \\ is not a valid escape.\nk = \"\"\"t\\ t\"\"\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_MultilineBadEscape3(t *testing.T) { input := "# \\ is not a valid escape.\nk = \"\"\"t\\ \"\"\"\n\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_MultilineBadEscape4(t *testing.T) { input := "backslash = \"\"\"\\\"\"\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_MultilineEscapeSpace1(t *testing.T) { input := "a = \"\"\"\n foo \\ \\n\n bar\"\"\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_MultilineEscapeSpace2(t *testing.T) { input := "bee = \"\"\"\nhee \\\n\ngee \\ \"\"\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_MultilineLitNoClose1(t *testing.T) { input := "invalid = '''\n this will fail\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_MultilineLitNoClose2(t *testing.T) { input := "x='''\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_MultilineLitNoClose3(t *testing.T) { input := "not-closed= '''\ndiibaa\nblibae ete\neteta\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_MultilineLitNoClose4(t *testing.T) { input := "bee = '''\nhee\ngee ''\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_MultilineNoClose1(t *testing.T) { input := "invalid = \"\"\"\n this will fail\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_MultilineNoClose2(t *testing.T) { input := "x=\"\"\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_MultilineNoClose3(t *testing.T) { input := "not-closed= \"\"\"\ndiibaa\nblibae ete\neteta\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_MultilineNoClose4(t *testing.T) { input := "bee = \"\"\"\nhee\ngee \"\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_MultilineNoClose5(t *testing.T) { input := "bee = \"\"\"\nhee\ngee\\\t \n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_MultilineQuotes1(t *testing.T) { input := "a = \"\"\"6 quotes: \"\"\"\"\"\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_NoClose1(t *testing.T) { input := "no-ending-quote = \"One time, at band camp\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_NoClose2(t *testing.T) { input := "\"a-string\".must-be = \"closed\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_NoClose3(t *testing.T) { input := "no-ending-quote = 'One time, at band camp\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_NoClose4(t *testing.T) { input := "'a-string'.must-be = 'closed\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_TextAfterString(t *testing.T) { input := "string = \"Is there life after strings?\" No.\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_String_WrongClose(t *testing.T) { input := "bad-ending-quote = \"double and single'\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_AppendToArrayWithDottedKeys(t *testing.T) { input := "[[a.b]]\n\n[a]\nb.y = 2\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_AppendWithDottedKeys1(t *testing.T) { input := "# First a.b.c defines a table: a.b.c = {z=9}\n#\n# Then we define a.b.c.t = \"str\" to add a str to the above table, making it:\n#\n# a.b.c = {z=9, t=\"...\"}\n#\n# While this makes sense, logically, it was decided this is not valid TOML as\n# it's too confusing/convoluted.\n# \n# See: https://github.com/toml-lang/toml/issues/846\n# https://github.com/toml-lang/toml/pull/859\n\n[a.b.c]\n z = 9\n\n[a]\n b.c.t = \"Using dotted keys to add to [a.b.c] after explicitly defining it above is not allowed\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_AppendWithDottedKeys2(t *testing.T) { input := "# This is the same issue as in injection-1.toml, except that nests one level\n# deeper. See that file for a more complete description.\n\n[a.b.c.d]\n z = 9\n\n[a]\n b.c.d.k.t = \"Using dotted keys to add to [a.b.c.d] after explicitly defining it above is not allowed\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_ArrayEmpty(t *testing.T) { input := "[[]]\nname = \"Born to Run\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_ArrayImplicit(t *testing.T) { input := "# This test is a bit tricky. It should fail because the first use of\n# `[[albums.songs]]` without first declaring `albums` implies that `albums`\n# must be a table. The alternative would be quite weird. Namely, it wouldn't\n# comply with the TOML spec: \"Each double-bracketed sub-table will belong to \n# the most *recently* defined table element *above* it.\"\n#\n# This is in contrast to the *valid* test, table-array-implicit where\n# `[[albums.songs]]` works by itself, so long as `[[albums]]` isn't declared\n# later. (Although, `[albums]` could be.)\n[[albums.songs]]\nname = \"Glory Days\"\n\n[[albums]]\nname = \"Born in the USA\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_ArrayNoClose1(t *testing.T) { input := "[[albums]\nname = \"Born to Run\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_ArrayNoClose2(t *testing.T) { input := "[[closing-bracket.missing]\nblaa=2\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_DuplicateKeyDottedArray(t *testing.T) { input := "[fruit]\napple.color = \"red\"\n\n[[fruit.apple]]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_DuplicateKeyDottedTable(t *testing.T) { input := "[fruit]\napple.color = \"red\"\n\n[fruit.apple] # INVALID\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_DuplicateKeyDottedTable2(t *testing.T) { input := "[fruit]\napple.taste.sweet = true\n\n[fruit.apple.taste] # INVALID\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_DuplicateKeyTable(t *testing.T) { input := "[fruit]\ntype = \"apple\"\n\n[fruit.type]\napple = \"yes\"\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_DuplicateTableArray(t *testing.T) { input := "[tbl]\n[[tbl]]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_DuplicateTableArray2(t *testing.T) { input := "[[tbl]]\n[tbl]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_Duplicate(t *testing.T) { input := "[a]\nb = 1\n\n[a]\nc = 2\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_EmptyImplicitTable(t *testing.T) { input := "[naughty..naughty]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_Empty(t *testing.T) { input := "[]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_EqualsSign(t *testing.T) { input := "[name=bad]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_Llbrace(t *testing.T) { input := "[ [table]]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_NestedBracketsClose(t *testing.T) { input := "[a]b]\nzyx = 42\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_NestedBracketsOpen(t *testing.T) { input := "[a[b]\nzyx = 42\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_NoClose1(t *testing.T) { input := "[where will it end\nname = value\n\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_NoClose2(t *testing.T) { input := "[closing-bracket.missingö\nblaa=2\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_NoClose3(t *testing.T) { input := "[\"where will it end]\nname = value\n\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_NoClose4(t *testing.T) { input := "[\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_NoClose5(t *testing.T) { input := "[fwfw.wafw\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_OverwriteArrayInParent(t *testing.T) { input := "[[parent-table.arr]]\n[parent-table]\nnot-arr = 1\narr = 2\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_OverwriteBoolWithArray(t *testing.T) { input := "a=true\n[[a]]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_OverwriteWithDeepTable(t *testing.T) { input := "a=1\n[a.b.c.d]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_Redefine1(t *testing.T) { input := "# Define b as int, and try to use it as a table: error\n[a]\nb = 1\n\n[a.b]\nc = 2\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_Redefine2(t *testing.T) { input := "[t1]\nt2.t3.v = 0\n[t1.t2]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_Redefine3(t *testing.T) { input := "[t1]\nt2.t3.v = 0\n[t1.t2.t3]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_Rrbrace(t *testing.T) { input := "[[table] ]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_TextAfterTable(t *testing.T) { input := "[error] this shouldn't be here\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_Whitespace(t *testing.T) { input := "[invalid key]\n" testgenInvalid(t, input) } func TestTOMLTest_Invalid_Tests_Invalid_Table_WithPound(t *testing.T) { input := "[key#group]\nanswer = 42\n" testgenInvalid(t, input) } func TestTOMLTest_Valid_Array_ArraySubtables(t *testing.T) { input := "[[arr]]\n[arr.subtab]\nval=1\n\n[[arr]]\n[arr.subtab]\nval=2\n" jsonRef := "{\n \"arr\": [\n {\n \"subtab\": {\n \"val\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n }\n },\n {\n \"subtab\": {\n \"val\": {\n \"type\": \"integer\",\n \"value\": \"2\"\n }\n }\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Array_Array(t *testing.T) { input := "ints = [1, 2, 3, ]\nfloats = [1.1, 2.1, 3.1]\nstrings = [\"a\", \"b\", \"c\"]\ndates = [\n 1987-07-05T17:45:00Z,\n 1979-05-27T07:32:00Z,\n 2006-06-01T11:00:00Z,\n]\ncomments = [\n 1,\n 2, #this is ok\n]\n" jsonRef := "{\n \"comments\": [\n {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"2\"\n }\n ],\n \"dates\": [\n {\n \"type\": \"datetime\",\n \"value\": \"1987-07-05T17:45:00Z\"\n },\n {\n \"type\": \"datetime\",\n \"value\": \"1979-05-27T07:32:00Z\"\n },\n {\n \"type\": \"datetime\",\n \"value\": \"2006-06-01T11:00:00Z\"\n }\n ],\n \"floats\": [\n {\n \"type\": \"float\",\n \"value\": \"1.1\"\n },\n {\n \"type\": \"float\",\n \"value\": \"2.1\"\n },\n {\n \"type\": \"float\",\n \"value\": \"3.1\"\n }\n ],\n \"ints\": [\n {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"2\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"3\"\n }\n ],\n \"strings\": [\n {\n \"type\": \"string\",\n \"value\": \"a\"\n },\n {\n \"type\": \"string\",\n \"value\": \"b\"\n },\n {\n \"type\": \"string\",\n \"value\": \"c\"\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Array_Bool(t *testing.T) { input := "a = [true, false]\n" jsonRef := "{\n \"a\": [\n {\n \"type\": \"bool\",\n \"value\": \"true\"\n },\n {\n \"type\": \"bool\",\n \"value\": \"false\"\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Array_Empty(t *testing.T) { input := "thevoid = [[[[[]]]]]\n" jsonRef := "{\n \"thevoid\": [\n [\n [\n [\n []\n ]\n ]\n ]\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Array_Hetergeneous(t *testing.T) { input := "mixed = [[1, 2], [\"a\", \"b\"], [1.1, 2.1]]\n" jsonRef := "{\n \"mixed\": [\n [\n {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"2\"\n }\n ],\n [\n {\n \"type\": \"string\",\n \"value\": \"a\"\n },\n {\n \"type\": \"string\",\n \"value\": \"b\"\n }\n ],\n [\n {\n \"type\": \"float\",\n \"value\": \"1.1\"\n },\n {\n \"type\": \"float\",\n \"value\": \"2.1\"\n }\n ]\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Array_MixedIntArray(t *testing.T) { input := "arrays-and-ints = [1, [\"Arrays are not integers.\"]]\n" jsonRef := "{\n \"arrays-and-ints\": [\n {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n [\n {\n \"type\": \"string\",\n \"value\": \"Arrays are not integers.\"\n }\n ]\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Array_MixedIntFloat(t *testing.T) { input := "ints-and-floats = [1, 1.1]\n" jsonRef := "{\n \"ints-and-floats\": [\n {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n {\n \"type\": \"float\",\n \"value\": \"1.1\"\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Array_MixedIntString(t *testing.T) { input := "strings-and-ints = [\"hi\", 42]\n" jsonRef := "{\n \"strings-and-ints\": [\n {\n \"type\": \"string\",\n \"value\": \"hi\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"42\"\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Array_MixedStringTable(t *testing.T) { input := "contributors = [\n \"Foo Bar \",\n { name = \"Baz Qux\", email = \"bazqux@example.com\", url = \"https://example.com/bazqux\" }\n]\n\n# Start with a table as the first element. This tests a case that some libraries\n# might have where they will check if the first entry is a table/map/hash/assoc\n# array and then encode it as a table array. This was a reasonable thing to do\n# before TOML 1.0 since arrays could only contain one type, but now it's no\n# longer.\nmixed = [{k=\"a\"}, \"b\", 1]\n" jsonRef := "{\n \"contributors\": [\n {\n \"type\": \"string\",\n \"value\": \"Foo Bar \\u003cfoo@example.com\\u003e\"\n },\n {\n \"email\": {\n \"type\": \"string\",\n \"value\": \"bazqux@example.com\"\n },\n \"name\": {\n \"type\": \"string\",\n \"value\": \"Baz Qux\"\n },\n \"url\": {\n \"type\": \"string\",\n \"value\": \"https://example.com/bazqux\"\n }\n }\n ],\n \"mixed\": [\n {\n \"k\": {\n \"type\": \"string\",\n \"value\": \"a\"\n }\n },\n {\n \"type\": \"string\",\n \"value\": \"b\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Array_NestedDouble(t *testing.T) { input := "nest = [\n\t[\n\t\t[\"a\"],\n\t\t[1, 2, [3]]\n\t]\n]\n" jsonRef := "{\n \"nest\": [\n [\n [\n {\n \"type\": \"string\",\n \"value\": \"a\"\n }\n ],\n [\n {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"2\"\n },\n [\n {\n \"type\": \"integer\",\n \"value\": \"3\"\n }\n ]\n ]\n ]\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Array_NestedInlineTable(t *testing.T) { input := "a = [ { b = {} } ]\n" jsonRef := "{\n \"a\": [\n {\n \"b\": {}\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Array_Nested(t *testing.T) { input := "nest = [[\"a\"], [\"b\"]]\n" jsonRef := "{\n \"nest\": [\n [\n {\n \"type\": \"string\",\n \"value\": \"a\"\n }\n ],\n [\n {\n \"type\": \"string\",\n \"value\": \"b\"\n }\n ]\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Array_Nospaces(t *testing.T) { input := "ints = [1,2,3]\n" jsonRef := "{\n \"ints\": [\n {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"2\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"3\"\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Array_OpenParentTable(t *testing.T) { input := "[[parent-table.arr]]\n[[parent-table.arr]]\n[parent-table]\nnot-arr = 1\n" jsonRef := "{\n \"parent-table\": {\n \"arr\": [\n {},\n {}\n ],\n \"not-arr\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Array_StringQuoteComma2(t *testing.T) { input := "title = [ \" \\\", \",]\n" jsonRef := "{\n \"title\": [\n {\n \"type\": \"string\",\n \"value\": \" \\\", \"\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Array_StringQuoteComma(t *testing.T) { input := "title = [\n\"Client: \\\"XXXX\\\", Job: XXXX\",\n\"Code: XXXX\"\n]\n" jsonRef := "{\n \"title\": [\n {\n \"type\": \"string\",\n \"value\": \"Client: \\\"XXXX\\\", Job: XXXX\"\n },\n {\n \"type\": \"string\",\n \"value\": \"Code: XXXX\"\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Array_StringWithComma2(t *testing.T) { input := "title = [\n\"\"\"Client: XXXX,\nJob: XXXX\"\"\",\n\"Code: XXXX\"\n]\n" jsonRef := "{\n \"title\": [\n {\n \"type\": \"string\",\n \"value\": \"Client: XXXX,\\nJob: XXXX\"\n },\n {\n \"type\": \"string\",\n \"value\": \"Code: XXXX\"\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Array_StringWithComma(t *testing.T) { input := "title = [\n\"Client: XXXX, Job: XXXX\",\n\"Code: XXXX\"\n]\n" jsonRef := "{\n \"title\": [\n {\n \"type\": \"string\",\n \"value\": \"Client: XXXX, Job: XXXX\"\n },\n {\n \"type\": \"string\",\n \"value\": \"Code: XXXX\"\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Array_Strings(t *testing.T) { input := "string_array = [ \"all\", 'strings', \"\"\"are the same\"\"\", '''type''']\n" jsonRef := "{\n \"string_array\": [\n {\n \"type\": \"string\",\n \"value\": \"all\"\n },\n {\n \"type\": \"string\",\n \"value\": \"strings\"\n },\n {\n \"type\": \"string\",\n \"value\": \"are the same\"\n },\n {\n \"type\": \"string\",\n \"value\": \"type\"\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Array_TableArrayStringBackslash(t *testing.T) { input := "foo = [ { bar=\"\\\"{{baz}}\\\"\"} ]\n" jsonRef := "{\n \"foo\": [\n {\n \"bar\": {\n \"type\": \"string\",\n \"value\": \"\\\"{{baz}}\\\"\"\n }\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Array_TrailingComma(t *testing.T) { input := "arr-1 = [1,]\n\narr-2 = [2,3,]\n\narr-3 = [4,\n]\n\narr-4 = [\n\t5,\n\t6,\n]\n" jsonRef := "{\n \"arr-1\": [\n {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n ],\n \"arr-2\": [\n {\n \"type\": \"integer\",\n \"value\": \"2\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"3\"\n }\n ],\n \"arr-3\": [\n {\n \"type\": \"integer\",\n \"value\": \"4\"\n }\n ],\n \"arr-4\": [\n {\n \"type\": \"integer\",\n \"value\": \"5\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"6\"\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Bool_Bool(t *testing.T) { input := "t = true\nf = false\n" jsonRef := "{\n \"f\": {\n \"type\": \"bool\",\n \"value\": \"false\"\n },\n \"t\": {\n \"type\": \"bool\",\n \"value\": \"true\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Comment_AfterLiteralNoWs(t *testing.T) { input := "inf=inf#infinity\nnan=nan#not a number\ntrue=true#true\nfalse=false#false\n" jsonRef := "{\n \"false\": {\n \"type\": \"bool\",\n \"value\": \"false\"\n },\n \"inf\": {\n \"type\": \"float\",\n \"value\": \"inf\"\n },\n \"nan\": {\n \"type\": \"float\",\n \"value\": \"nan\"\n },\n \"true\": {\n \"type\": \"bool\",\n \"value\": \"true\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Comment_AtEof(t *testing.T) { input := "# This is a full-line comment\nkey = \"value\" # This is a comment at the end of a line\n" jsonRef := "{\n \"key\": {\n \"type\": \"string\",\n \"value\": \"value\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Comment_AtEof2(t *testing.T) { input := "# This is a full-line comment\nkey = \"value\" # This is a comment at the end of a line\n" jsonRef := "{\n \"key\": {\n \"type\": \"string\",\n \"value\": \"value\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Comment_Everywhere(t *testing.T) { input := "# Top comment.\n # Top comment.\n# Top comment.\n\n# [no-extraneous-groups-please]\n\n[group] # Comment\nanswer = 42 # Comment\n# no-extraneous-keys-please = 999\n# Inbetween comment.\nmore = [ # Comment\n # What about multiple # comments?\n # Can you handle it?\n #\n # Evil.\n# Evil.\n 42, 42, # Comments within arrays are fun.\n # What about multiple # comments?\n # Can you handle it?\n #\n # Evil.\n# Evil.\n# ] Did I fool you?\n] # Hopefully not.\n\n# Make sure the space between the datetime and \"#\" isn't lexed.\ndt = 1979-05-27T07:32:12-07:00 # c\nd = 1979-05-27 # Comment\n" jsonRef := "{\n \"group\": {\n \"answer\": {\n \"type\": \"integer\",\n \"value\": \"42\"\n },\n \"d\": {\n \"type\": \"date-local\",\n \"value\": \"1979-05-27\"\n },\n \"dt\": {\n \"type\": \"datetime\",\n \"value\": \"1979-05-27T07:32:12-07:00\"\n },\n \"more\": [\n {\n \"type\": \"integer\",\n \"value\": \"42\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"42\"\n }\n ]\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Comment_Noeol(t *testing.T) { input := "# single comment without any eol characters" jsonRef := "{}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Comment_Nonascii(t *testing.T) { input := "# ~ \u0080 ÿ \ud7ff \ue000 \uffff 𐀀 \U0010ffff\n" jsonRef := "{}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Comment_Tricky(t *testing.T) { input := "[section]#attached comment\n#[notsection]\none = \"11\"#cmt\ntwo = \"22#\"\nthree = '#'\n\nfour = \"\"\"# no comment\n# nor this\n#also not comment\"\"\"#is_comment\n\nfive = 5.5#66\nsix = 6#7\n8 = \"eight\"\n#nine = 99\nten = 10e2#1\neleven = 1.11e1#23\n\n[\"hash#tag\"]\n\"#!\" = \"hash bang\"\narr3 = [ \"#\", '#', \"\"\"###\"\"\" ]\narr4 = [ 1,# 9, 9,\n2#,9\n,#9\n3#]\n,4]\narr5 = [[[[#[\"#\"],\n[\"#\"]]]]#]\n]\ntbl1 = { \"#\" = '}#'}#}}\n\n\n" jsonRef := "{\n \"hash#tag\": {\n \"#!\": {\n \"type\": \"string\",\n \"value\": \"hash bang\"\n },\n \"arr3\": [\n {\n \"type\": \"string\",\n \"value\": \"#\"\n },\n {\n \"type\": \"string\",\n \"value\": \"#\"\n },\n {\n \"type\": \"string\",\n \"value\": \"###\"\n }\n ],\n \"arr4\": [\n {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"2\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"3\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"4\"\n }\n ],\n \"arr5\": [\n [\n [\n [\n [\n {\n \"type\": \"string\",\n \"value\": \"#\"\n }\n ]\n ]\n ]\n ]\n ],\n \"tbl1\": {\n \"#\": {\n \"type\": \"string\",\n \"value\": \"}#\"\n }\n }\n },\n \"section\": {\n \"8\": {\n \"type\": \"string\",\n \"value\": \"eight\"\n },\n \"eleven\": {\n \"type\": \"float\",\n \"value\": \"11.1\"\n },\n \"five\": {\n \"type\": \"float\",\n \"value\": \"5.5\"\n },\n \"four\": {\n \"type\": \"string\",\n \"value\": \"# no comment\\n# nor this\\n#also not comment\"\n },\n \"one\": {\n \"type\": \"string\",\n \"value\": \"11\"\n },\n \"six\": {\n \"type\": \"integer\",\n \"value\": \"6\"\n },\n \"ten\": {\n \"type\": \"float\",\n \"value\": \"1000.0\"\n },\n \"three\": {\n \"type\": \"string\",\n \"value\": \"#\"\n },\n \"two\": {\n \"type\": \"string\",\n \"value\": \"22#\"\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Datetime_Datetime(t *testing.T) { input := "space = 1987-07-05 17:45:00Z\nlower = 1987-07-05t17:45:00z\n" jsonRef := "{\n \"lower\": {\n \"type\": \"datetime\",\n \"value\": \"1987-07-05T17:45:00Z\"\n },\n \"space\": {\n \"type\": \"datetime\",\n \"value\": \"1987-07-05T17:45:00Z\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Datetime_Edge(t *testing.T) { input := "first-offset = 0001-01-01 00:00:00Z\nfirst-local = 0001-01-01 00:00:00\nfirst-date = 0001-01-01\n\nlast-offset = 9999-12-31 23:59:59Z\nlast-local = 9999-12-31 23:59:59\nlast-date = 9999-12-31\n" jsonRef := "{\n \"first-date\": {\n \"type\": \"date-local\",\n \"value\": \"0001-01-01\"\n },\n \"first-local\": {\n \"type\": \"datetime-local\",\n \"value\": \"0001-01-01T00:00:00\"\n },\n \"first-offset\": {\n \"type\": \"datetime\",\n \"value\": \"0001-01-01T00:00:00Z\"\n },\n \"last-date\": {\n \"type\": \"date-local\",\n \"value\": \"9999-12-31\"\n },\n \"last-local\": {\n \"type\": \"datetime-local\",\n \"value\": \"9999-12-31T23:59:59\"\n },\n \"last-offset\": {\n \"type\": \"datetime\",\n \"value\": \"9999-12-31T23:59:59Z\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Datetime_LeapYear(t *testing.T) { input := "2000-datetime = 2000-02-29 15:15:15Z\n2000-datetime-local = 2000-02-29 15:15:15\n2000-date = 2000-02-29\n\n2024-datetime = 2024-02-29 15:15:15Z\n2024-datetime-local = 2024-02-29 15:15:15\n2024-date = 2024-02-29\n" jsonRef := "{\n \"2000-date\": {\n \"type\": \"date-local\",\n \"value\": \"2000-02-29\"\n },\n \"2000-datetime\": {\n \"type\": \"datetime\",\n \"value\": \"2000-02-29T15:15:15Z\"\n },\n \"2000-datetime-local\": {\n \"type\": \"datetime-local\",\n \"value\": \"2000-02-29T15:15:15\"\n },\n \"2024-date\": {\n \"type\": \"date-local\",\n \"value\": \"2024-02-29\"\n },\n \"2024-datetime\": {\n \"type\": \"datetime\",\n \"value\": \"2024-02-29T15:15:15Z\"\n },\n \"2024-datetime-local\": {\n \"type\": \"datetime-local\",\n \"value\": \"2024-02-29T15:15:15\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Datetime_LocalDate(t *testing.T) { input := "bestdayever = 1987-07-05\n" jsonRef := "{\n \"bestdayever\": {\n \"type\": \"date-local\",\n \"value\": \"1987-07-05\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Datetime_LocalTime(t *testing.T) { input := "besttimeever = 17:45:00\nmilliseconds = 10:32:00.555\n" jsonRef := "{\n \"besttimeever\": {\n \"type\": \"time-local\",\n \"value\": \"17:45:00\"\n },\n \"milliseconds\": {\n \"type\": \"time-local\",\n \"value\": \"10:32:00.555\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Datetime_Local(t *testing.T) { input := "local = 1987-07-05T17:45:00\nmilli = 1977-12-21T10:32:00.555\nspace = 1987-07-05 17:45:00\n" jsonRef := "{\n \"local\": {\n \"type\": \"datetime-local\",\n \"value\": \"1987-07-05T17:45:00\"\n },\n \"milli\": {\n \"type\": \"datetime-local\",\n \"value\": \"1977-12-21T10:32:00.555\"\n },\n \"space\": {\n \"type\": \"datetime-local\",\n \"value\": \"1987-07-05T17:45:00\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Datetime_Milliseconds(t *testing.T) { input := "utc1 = 1987-07-05T17:45:56.123Z\nutc2 = 1987-07-05T17:45:56.6Z\nwita1 = 1987-07-05T17:45:56.123+08:00\nwita2 = 1987-07-05T17:45:56.6+08:00\n" jsonRef := "{\n \"utc1\": {\n \"type\": \"datetime\",\n \"value\": \"1987-07-05T17:45:56.123Z\"\n },\n \"utc2\": {\n \"type\": \"datetime\",\n \"value\": \"1987-07-05T17:45:56.600Z\"\n },\n \"wita1\": {\n \"type\": \"datetime\",\n \"value\": \"1987-07-05T17:45:56.123+08:00\"\n },\n \"wita2\": {\n \"type\": \"datetime\",\n \"value\": \"1987-07-05T17:45:56.600+08:00\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Datetime_Timezone(t *testing.T) { input := "utc = 1987-07-05T17:45:56Z\npdt = 1987-07-05T17:45:56-05:00\nnzst = 1987-07-05T17:45:56+12:00\nnzdt = 1987-07-05T17:45:56+13:00 # DST\n" jsonRef := "{\n \"nzdt\": {\n \"type\": \"datetime\",\n \"value\": \"1987-07-05T17:45:56+13:00\"\n },\n \"nzst\": {\n \"type\": \"datetime\",\n \"value\": \"1987-07-05T17:45:56+12:00\"\n },\n \"pdt\": {\n \"type\": \"datetime\",\n \"value\": \"1987-07-05T17:45:56-05:00\"\n },\n \"utc\": {\n \"type\": \"datetime\",\n \"value\": \"1987-07-05T17:45:56Z\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Float_Exponent(t *testing.T) { input := "lower = 3e2\nupper = 3E2\nneg = 3e-2\npos = 3E+2\nzero = 3e0\npointlower = 3.1e2\npointupper = 3.1E2\nminustenth = -1E-1\n" jsonRef := "{\n \"lower\": {\n \"type\": \"float\",\n \"value\": \"300.0\"\n },\n \"minustenth\": {\n \"type\": \"float\",\n \"value\": \"-0.1\"\n },\n \"neg\": {\n \"type\": \"float\",\n \"value\": \"0.03\"\n },\n \"pointlower\": {\n \"type\": \"float\",\n \"value\": \"310.0\"\n },\n \"pointupper\": {\n \"type\": \"float\",\n \"value\": \"310.0\"\n },\n \"pos\": {\n \"type\": \"float\",\n \"value\": \"300.0\"\n },\n \"upper\": {\n \"type\": \"float\",\n \"value\": \"300.0\"\n },\n \"zero\": {\n \"type\": \"float\",\n \"value\": \"3.0\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Float_Float(t *testing.T) { input := "pi = 3.14\npospi = +3.14\nnegpi = -3.14\nzero-intpart = 0.123\n" jsonRef := "{\n \"negpi\": {\n \"type\": \"float\",\n \"value\": \"-3.14\"\n },\n \"pi\": {\n \"type\": \"float\",\n \"value\": \"3.14\"\n },\n \"pospi\": {\n \"type\": \"float\",\n \"value\": \"3.14\"\n },\n \"zero-intpart\": {\n \"type\": \"float\",\n \"value\": \"0.123\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Float_InfAndNan(t *testing.T) { input := "# We don't encode +nan and -nan back with the signs; many languages don't\n# support a sign on NaN (it doesn't really make much sense).\nnan = nan\nnan_neg = -nan\nnan_plus = +nan\ninfinity = inf\ninfinity_neg = -inf\ninfinity_plus = +inf\n" jsonRef := "{\n \"infinity\": {\n \"type\": \"float\",\n \"value\": \"inf\"\n },\n \"infinity_neg\": {\n \"type\": \"float\",\n \"value\": \"-inf\"\n },\n \"infinity_plus\": {\n \"type\": \"float\",\n \"value\": \"+inf\"\n },\n \"nan\": {\n \"type\": \"float\",\n \"value\": \"nan\"\n },\n \"nan_neg\": {\n \"type\": \"float\",\n \"value\": \"nan\"\n },\n \"nan_plus\": {\n \"type\": \"float\",\n \"value\": \"nan\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Float_Long(t *testing.T) { input := "longpi = 3.141592653589793\nneglongpi = -3.141592653589793\n" jsonRef := "{\n \"longpi\": {\n \"type\": \"float\",\n \"value\": \"3.141592653589793\"\n },\n \"neglongpi\": {\n \"type\": \"float\",\n \"value\": \"-3.141592653589793\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Float_Underscore(t *testing.T) { input := "before = 3_141.5927\nafter = 3141.592_7\nexponent = 3e1_4\n" jsonRef := "{\n \"after\": {\n \"type\": \"float\",\n \"value\": \"3141.5927\"\n },\n \"before\": {\n \"type\": \"float\",\n \"value\": \"3141.5927\"\n },\n \"exponent\": {\n \"type\": \"float\",\n \"value\": \"3.0e14\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Float_Zero(t *testing.T) { input := "zero = 0.0\nsigned-pos = +0.0\nsigned-neg = -0.0\nexponent = 0e0\nexponent-two-0 = 0e00\nexponent-signed-pos = +0e0\nexponent-signed-neg = -0e0\n" jsonRef := "{\n \"exponent\": {\n \"type\": \"float\",\n \"value\": \"0\"\n },\n \"exponent-signed-neg\": {\n \"type\": \"float\",\n \"value\": \"-0\"\n },\n \"exponent-signed-pos\": {\n \"type\": \"float\",\n \"value\": \"0\"\n },\n \"exponent-two-0\": {\n \"type\": \"float\",\n \"value\": \"0\"\n },\n \"signed-neg\": {\n \"type\": \"float\",\n \"value\": \"-0\"\n },\n \"signed-pos\": {\n \"type\": \"float\",\n \"value\": \"0\"\n },\n \"zero\": {\n \"type\": \"float\",\n \"value\": \"0\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_InlineTable_Array(t *testing.T) { input := "people = [{first_name = \"Bruce\", last_name = \"Springsteen\"},\n {first_name = \"Eric\", last_name = \"Clapton\"},\n {first_name = \"Bob\", last_name = \"Seger\"}]\n" jsonRef := "{\n \"people\": [\n {\n \"first_name\": {\n \"type\": \"string\",\n \"value\": \"Bruce\"\n },\n \"last_name\": {\n \"type\": \"string\",\n \"value\": \"Springsteen\"\n }\n },\n {\n \"first_name\": {\n \"type\": \"string\",\n \"value\": \"Eric\"\n },\n \"last_name\": {\n \"type\": \"string\",\n \"value\": \"Clapton\"\n }\n },\n {\n \"first_name\": {\n \"type\": \"string\",\n \"value\": \"Bob\"\n },\n \"last_name\": {\n \"type\": \"string\",\n \"value\": \"Seger\"\n }\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_InlineTable_Bool(t *testing.T) { input := "a = {a = true, b = false}\n" jsonRef := "{\n \"a\": {\n \"a\": {\n \"type\": \"bool\",\n \"value\": \"true\"\n },\n \"b\": {\n \"type\": \"bool\",\n \"value\": \"false\"\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_InlineTable_Empty(t *testing.T) { input := "empty1 = {}\nempty2 = { }\nempty_in_array = [ { not_empty = 1 }, {} ]\nempty_in_array2 = [{},{not_empty=1}]\nmany_empty = [{},{},{}]\nnested_empty = {\"empty\"={}}\nwith_cmt ={ }#nothing here\n" jsonRef := "{\n \"empty1\": {},\n \"empty2\": {},\n \"empty_in_array\": [\n {\n \"not_empty\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n },\n {}\n ],\n \"empty_in_array2\": [\n {},\n {\n \"not_empty\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n }\n ],\n \"many_empty\": [\n {},\n {},\n {}\n ],\n \"nested_empty\": {\n \"empty\": {}\n },\n \"with_cmt\": {}\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_InlineTable_EndInBool(t *testing.T) { input := "black = { python=\">3.6\", version=\">=18.9b0\", allow_prereleases=true }\n" jsonRef := "{\n \"black\": {\n \"allow_prereleases\": {\n \"type\": \"bool\",\n \"value\": \"true\"\n },\n \"python\": {\n \"type\": \"string\",\n \"value\": \"\\u003e3.6\"\n },\n \"version\": {\n \"type\": \"string\",\n \"value\": \"\\u003e=18.9b0\"\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_InlineTable_InlineTable(t *testing.T) { input := "name = { first = \"Tom\", last = \"Preston-Werner\" }\npoint = { x = 1, y = 2 }\nsimple = { a = 1 }\nstr-key = { \"a\" = 1 }\ntable-array = [{ \"a\" = 1 }, { \"b\" = 2 }]\n" jsonRef := "{\n \"name\": {\n \"first\": {\n \"type\": \"string\",\n \"value\": \"Tom\"\n },\n \"last\": {\n \"type\": \"string\",\n \"value\": \"Preston-Werner\"\n }\n },\n \"point\": {\n \"x\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n \"y\": {\n \"type\": \"integer\",\n \"value\": \"2\"\n }\n },\n \"simple\": {\n \"a\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n },\n \"str-key\": {\n \"a\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n },\n \"table-array\": [\n {\n \"a\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n },\n {\n \"b\": {\n \"type\": \"integer\",\n \"value\": \"2\"\n }\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_InlineTable_KeyDotted(t *testing.T) { input := "inline = {a.b = 42}\n\nmany.dots.here.dot.dot.dot = {a.b.c = 1, a.b.d = 2}\n\na = { a.b = 1 }\nb = { \"a\".\"b\" = 1 }\nc = { a . b = 1 }\nd = { 'a' . \"b\" = 1 }\ne = {a.b=1}\n\n[tbl]\na.b.c = {d.e=1}\n\n[tbl.x]\na.b.c = {d.e=1}\n\n[[arr]]\nt = {a.b=1}\nT = {a.b=1}\n\n[[arr]]\nt = {a.b=2}\nT = {a.b=2}\n" jsonRef := "{\n \"a\": {\n \"a\": {\n \"b\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n }\n },\n \"arr\": [\n {\n \"T\": {\n \"a\": {\n \"b\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n }\n },\n \"t\": {\n \"a\": {\n \"b\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n }\n }\n },\n {\n \"T\": {\n \"a\": {\n \"b\": {\n \"type\": \"integer\",\n \"value\": \"2\"\n }\n }\n },\n \"t\": {\n \"a\": {\n \"b\": {\n \"type\": \"integer\",\n \"value\": \"2\"\n }\n }\n }\n }\n ],\n \"b\": {\n \"a\": {\n \"b\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n }\n },\n \"c\": {\n \"a\": {\n \"b\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n }\n },\n \"d\": {\n \"a\": {\n \"b\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n }\n },\n \"e\": {\n \"a\": {\n \"b\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n }\n },\n \"inline\": {\n \"a\": {\n \"b\": {\n \"type\": \"integer\",\n \"value\": \"42\"\n }\n }\n },\n \"many\": {\n \"dots\": {\n \"here\": {\n \"dot\": {\n \"dot\": {\n \"dot\": {\n \"a\": {\n \"b\": {\n \"c\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n \"d\": {\n \"type\": \"integer\",\n \"value\": \"2\"\n }\n }\n }\n }\n }\n }\n }\n }\n },\n \"tbl\": {\n \"a\": {\n \"b\": {\n \"c\": {\n \"d\": {\n \"e\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n }\n }\n }\n },\n \"x\": {\n \"a\": {\n \"b\": {\n \"c\": {\n \"d\": {\n \"e\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n }\n }\n }\n }\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_InlineTable_Multiline(t *testing.T) { input := "tbl_multiline = { a = 1, b = \"\"\"\nmultiline\n\"\"\", c = \"\"\"and yet\nanother line\"\"\", d = 4 }\n" jsonRef := "{\n \"tbl_multiline\": {\n \"a\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n \"b\": {\n \"type\": \"string\",\n \"value\": \"multiline\\n\"\n },\n \"c\": {\n \"type\": \"string\",\n \"value\": \"and yet\\nanother line\"\n },\n \"d\": {\n \"type\": \"integer\",\n \"value\": \"4\"\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_InlineTable_Nest(t *testing.T) { input := "tbl_tbl_empty = { tbl_0 = {} }\ntbl_tbl_val = { tbl_1 = { one = 1 } }\ntbl_arr_tbl = { arr_tbl = [ { one = 1 } ] }\narr_tbl_tbl = [ { tbl = { one = 1 } } ]\n\n# Array-of-array-of-table is interesting because it can only\n# be represented in inline form.\narr_arr_tbl_empty = [ [ {} ] ]\narr_arr_tbl_val = [ [ { one = 1 } ] ]\narr_arr_tbls = [ [ { one = 1 }, { two = 2 } ] ]\n" jsonRef := "{\n \"arr_arr_tbl_empty\": [\n [\n {}\n ]\n ],\n \"arr_arr_tbl_val\": [\n [\n {\n \"one\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n }\n ]\n ],\n \"arr_arr_tbls\": [\n [\n {\n \"one\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n },\n {\n \"two\": {\n \"type\": \"integer\",\n \"value\": \"2\"\n }\n }\n ]\n ],\n \"arr_tbl_tbl\": [\n {\n \"tbl\": {\n \"one\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n }\n }\n ],\n \"tbl_arr_tbl\": {\n \"arr_tbl\": [\n {\n \"one\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n }\n ]\n },\n \"tbl_tbl_empty\": {\n \"tbl_0\": {}\n },\n \"tbl_tbl_val\": {\n \"tbl_1\": {\n \"one\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_InlineTable_Spaces(t *testing.T) { input := "# https://github.com/toml-lang/toml-test/issues/146\nclap-1 = { version = \"4\" , features = [\"derive\", \"cargo\"] }\n\n# Contains some literal tabs!\nclap-2 = { version = \"4\"\t \t,\t \tfeatures = [ \"derive\" \t , \t \"cargo\" ] , nest = { \t \"a\" = 'x' , \t 'b' = [ 1.5 , 9.0 ] } }\n" jsonRef := "{\n \"clap-1\": {\n \"features\": [\n {\n \"type\": \"string\",\n \"value\": \"derive\"\n },\n {\n \"type\": \"string\",\n \"value\": \"cargo\"\n }\n ],\n \"version\": {\n \"type\": \"string\",\n \"value\": \"4\"\n }\n },\n \"clap-2\": {\n \"features\": [\n {\n \"type\": \"string\",\n \"value\": \"derive\"\n },\n {\n \"type\": \"string\",\n \"value\": \"cargo\"\n }\n ],\n \"nest\": {\n \"a\": {\n \"type\": \"string\",\n \"value\": \"x\"\n },\n \"b\": [\n {\n \"type\": \"float\",\n \"value\": \"1.5\"\n },\n {\n \"type\": \"float\",\n \"value\": \"9\"\n }\n ]\n },\n \"version\": {\n \"type\": \"string\",\n \"value\": \"4\"\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Integer_Integer(t *testing.T) { input := "answer = 42\nposanswer = +42\nneganswer = -42\nzero = 0\n" jsonRef := "{\n \"answer\": {\n \"type\": \"integer\",\n \"value\": \"42\"\n },\n \"neganswer\": {\n \"type\": \"integer\",\n \"value\": \"-42\"\n },\n \"posanswer\": {\n \"type\": \"integer\",\n \"value\": \"42\"\n },\n \"zero\": {\n \"type\": \"integer\",\n \"value\": \"0\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Integer_Literals(t *testing.T) { input := "bin1 = 0b11010110\nbin2 = 0b1_0_1\n\noct1 = 0o01234567\noct2 = 0o755\noct3 = 0o7_6_5\n\nhex1 = 0xDEADBEEF\nhex2 = 0xdeadbeef\nhex3 = 0xdead_beef\nhex4 = 0x00987\n" jsonRef := "{\n \"bin1\": {\n \"type\": \"integer\",\n \"value\": \"214\"\n },\n \"bin2\": {\n \"type\": \"integer\",\n \"value\": \"5\"\n },\n \"hex1\": {\n \"type\": \"integer\",\n \"value\": \"3735928559\"\n },\n \"hex2\": {\n \"type\": \"integer\",\n \"value\": \"3735928559\"\n },\n \"hex3\": {\n \"type\": \"integer\",\n \"value\": \"3735928559\"\n },\n \"hex4\": {\n \"type\": \"integer\",\n \"value\": \"2439\"\n },\n \"oct1\": {\n \"type\": \"integer\",\n \"value\": \"342391\"\n },\n \"oct2\": {\n \"type\": \"integer\",\n \"value\": \"493\"\n },\n \"oct3\": {\n \"type\": \"integer\",\n \"value\": \"501\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Integer_Long(t *testing.T) { input := "int64-max = 9223372036854775807\nint64-max-neg = -9223372036854775808\n" jsonRef := "{\n \"int64-max\": {\n \"type\": \"integer\",\n \"value\": \"9223372036854775807\"\n },\n \"int64-max-neg\": {\n \"type\": \"integer\",\n \"value\": \"-9223372036854775808\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Integer_Underscore(t *testing.T) { input := "kilo = 1_000\nx = 1_1_1_1\n" jsonRef := "{\n \"kilo\": {\n \"type\": \"integer\",\n \"value\": \"1000\"\n },\n \"x\": {\n \"type\": \"integer\",\n \"value\": \"1111\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Integer_Zero(t *testing.T) { input := "d1 = 0\nd2 = +0\nd3 = -0\n\nh1 = 0x0\nh2 = 0x00\nh3 = 0x00000\n\no1 = 0o0\na2 = 0o00\na3 = 0o00000\n\nb1 = 0b0\nb2 = 0b00\nb3 = 0b00000\n" jsonRef := "{\n \"a2\": {\n \"type\": \"integer\",\n \"value\": \"0\"\n },\n \"a3\": {\n \"type\": \"integer\",\n \"value\": \"0\"\n },\n \"b1\": {\n \"type\": \"integer\",\n \"value\": \"0\"\n },\n \"b2\": {\n \"type\": \"integer\",\n \"value\": \"0\"\n },\n \"b3\": {\n \"type\": \"integer\",\n \"value\": \"0\"\n },\n \"d1\": {\n \"type\": \"integer\",\n \"value\": \"0\"\n },\n \"d2\": {\n \"type\": \"integer\",\n \"value\": \"0\"\n },\n \"d3\": {\n \"type\": \"integer\",\n \"value\": \"0\"\n },\n \"h1\": {\n \"type\": \"integer\",\n \"value\": \"0\"\n },\n \"h2\": {\n \"type\": \"integer\",\n \"value\": \"0\"\n },\n \"h3\": {\n \"type\": \"integer\",\n \"value\": \"0\"\n },\n \"o1\": {\n \"type\": \"integer\",\n \"value\": \"0\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Key_Alphanum(t *testing.T) { input := "alpha = \"a\"\n123 = \"num\"\n000111 = \"leading\"\n10e3 = \"false float\"\none1two2 = \"mixed\"\nwith-dash = \"dashed\"\nunder_score = \"___\"\n34-11 = 23\n\n[2018_10]\n001 = 1\n\n[a-a-a]\n_ = false\n" jsonRef := "{\n \"000111\": {\n \"type\": \"string\",\n \"value\": \"leading\"\n },\n \"10e3\": {\n \"type\": \"string\",\n \"value\": \"false float\"\n },\n \"123\": {\n \"type\": \"string\",\n \"value\": \"num\"\n },\n \"2018_10\": {\n \"001\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n },\n \"34-11\": {\n \"type\": \"integer\",\n \"value\": \"23\"\n },\n \"a-a-a\": {\n \"_\": {\n \"type\": \"bool\",\n \"value\": \"false\"\n }\n },\n \"alpha\": {\n \"type\": \"string\",\n \"value\": \"a\"\n },\n \"one1two2\": {\n \"type\": \"string\",\n \"value\": \"mixed\"\n },\n \"under_score\": {\n \"type\": \"string\",\n \"value\": \"___\"\n },\n \"with-dash\": {\n \"type\": \"string\",\n \"value\": \"dashed\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Key_CaseSensitive(t *testing.T) { input := "sectioN = \"NN\"\n\n[section]\nname = \"lower\"\nNAME = \"upper\"\nName = \"capitalized\"\n\n[Section]\nname = \"different section!!\"\n\"μ\" = \"greek small letter mu\"\n\"Μ\" = \"greek capital letter MU\"\nM = \"latin letter M\"\n\n" jsonRef := "{\n \"Section\": {\n \"M\": {\n \"type\": \"string\",\n \"value\": \"latin letter M\"\n },\n \"name\": {\n \"type\": \"string\",\n \"value\": \"different section!!\"\n },\n \"Μ\": {\n \"type\": \"string\",\n \"value\": \"greek capital letter MU\"\n },\n \"μ\": {\n \"type\": \"string\",\n \"value\": \"greek small letter mu\"\n }\n },\n \"sectioN\": {\n \"type\": \"string\",\n \"value\": \"NN\"\n },\n \"section\": {\n \"NAME\": {\n \"type\": \"string\",\n \"value\": \"upper\"\n },\n \"Name\": {\n \"type\": \"string\",\n \"value\": \"capitalized\"\n },\n \"name\": {\n \"type\": \"string\",\n \"value\": \"lower\"\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Key_DottedEmpty(t *testing.T) { input := "''.x = \"empty.x\"\nx.\"\" = \"x.empty\"\n[a]\n\"\".'' = \"empty.empty\"\n" jsonRef := "{\n \"\": {\n \"x\": {\n \"type\": \"string\",\n \"value\": \"empty.x\"\n }\n },\n \"a\": {\n \"\": {\n \"\": {\n \"type\": \"string\",\n \"value\": \"empty.empty\"\n }\n }\n },\n \"x\": {\n \"\": {\n \"type\": \"string\",\n \"value\": \"x.empty\"\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Key_Dotted(t *testing.T) { input := "# Note: this file contains literal tab characters.\n\nname.first = \"Arthur\"\n\"name\".'last' = \"Dent\"\n\nmany.dots.here.dot.dot.dot = 42\n\n# Space are ignored, and key parts can be quoted.\ncount.a = 1\ncount . b = 2\n\"count\".\"c\" = 3\n\"count\" . \"d\" = 4\n'count'.'e' = 5\n'count' . 'f' = 6\n\"count\".'g' = 7\n\"count\" . 'h' = 8\ncount.'i' = 9\ncount \t.\t 'j'\t = 10\n\"count\".k = 11\n\"count\" . l = 12\n\n[tbl]\na.b.c = 42.666\n\n[a.few.dots]\npolka.dot = \"again?\"\npolka.dance-with = \"Dot\"\n\n[[arr]]\na.b.c=1\na.b.d=2\n\n[[arr]]\na.b.c=3\na.b.d=4\n" jsonRef := "{\n \"a\": {\n \"few\": {\n \"dots\": {\n \"polka\": {\n \"dance-with\": {\n \"type\": \"string\",\n \"value\": \"Dot\"\n },\n \"dot\": {\n \"type\": \"string\",\n \"value\": \"again?\"\n }\n }\n }\n }\n },\n \"arr\": [\n {\n \"a\": {\n \"b\": {\n \"c\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n \"d\": {\n \"type\": \"integer\",\n \"value\": \"2\"\n }\n }\n }\n },\n {\n \"a\": {\n \"b\": {\n \"c\": {\n \"type\": \"integer\",\n \"value\": \"3\"\n },\n \"d\": {\n \"type\": \"integer\",\n \"value\": \"4\"\n }\n }\n }\n }\n ],\n \"count\": {\n \"a\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n \"b\": {\n \"type\": \"integer\",\n \"value\": \"2\"\n },\n \"c\": {\n \"type\": \"integer\",\n \"value\": \"3\"\n },\n \"d\": {\n \"type\": \"integer\",\n \"value\": \"4\"\n },\n \"e\": {\n \"type\": \"integer\",\n \"value\": \"5\"\n },\n \"f\": {\n \"type\": \"integer\",\n \"value\": \"6\"\n },\n \"g\": {\n \"type\": \"integer\",\n \"value\": \"7\"\n },\n \"h\": {\n \"type\": \"integer\",\n \"value\": \"8\"\n },\n \"i\": {\n \"type\": \"integer\",\n \"value\": \"9\"\n },\n \"j\": {\n \"type\": \"integer\",\n \"value\": \"10\"\n },\n \"k\": {\n \"type\": \"integer\",\n \"value\": \"11\"\n },\n \"l\": {\n \"type\": \"integer\",\n \"value\": \"12\"\n }\n },\n \"many\": {\n \"dots\": {\n \"here\": {\n \"dot\": {\n \"dot\": {\n \"dot\": {\n \"type\": \"integer\",\n \"value\": \"42\"\n }\n }\n }\n }\n }\n },\n \"name\": {\n \"first\": {\n \"type\": \"string\",\n \"value\": \"Arthur\"\n },\n \"last\": {\n \"type\": \"string\",\n \"value\": \"Dent\"\n }\n },\n \"tbl\": {\n \"a\": {\n \"b\": {\n \"c\": {\n \"type\": \"float\",\n \"value\": \"42.666\"\n }\n }\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Key_Empty1(t *testing.T) { input := "\"\" = \"blank\"\n" jsonRef := "{\n \"\": {\n \"type\": \"string\",\n \"value\": \"blank\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Key_Empty2(t *testing.T) { input := "'' = \"blank\"\n" jsonRef := "{\n \"\": {\n \"type\": \"string\",\n \"value\": \"blank\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Key_Empty3(t *testing.T) { input := "''=0\n" jsonRef := "{\n \"\": {\n \"type\": \"integer\",\n \"value\": \"0\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Key_EqualsNospace(t *testing.T) { input := "answer=42\n" jsonRef := "{\n \"answer\": {\n \"type\": \"integer\",\n \"value\": \"42\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Key_Escapes(t *testing.T) { input := "\"\\n\" = \"newline\"\n\"\\b\" = \"bell\"\n\"\\u00c0\" = \"latin capital letter A with grave\"\n\"\\\"\" = \"just a quote\"\n\n[\"backsp\\b\\b\"]\n\n[\"\\\"quoted\\\"\"]\nquote = true\n\n[\"a.b\".\"\\u00c0\"]\n" jsonRef := "{\n \"\\u0008\": {\n \"type\": \"string\",\n \"value\": \"bell\"\n },\n \"\\n\": {\n \"type\": \"string\",\n \"value\": \"newline\"\n },\n \"\\\"\": {\n \"type\": \"string\",\n \"value\": \"just a quote\"\n },\n \"\\\"quoted\\\"\": {\n \"quote\": {\n \"type\": \"bool\",\n \"value\": \"true\"\n }\n },\n \"a.b\": {\n \"À\": {}\n },\n \"backsp\\u0008\\u0008\": {},\n \"À\": {\n \"type\": \"string\",\n \"value\": \"latin capital letter A with grave\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Key_NumericDotted(t *testing.T) { input := "1.2 = 3\n" jsonRef := "{\n \"1\": {\n \"2\": {\n \"type\": \"integer\",\n \"value\": \"3\"\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Key_Numeric(t *testing.T) { input := "1 = 1\n" jsonRef := "{\n \"1\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Key_QuotedDots(t *testing.T) { input := "plain = 1\n\"with.dot\" = 2\n\n[plain_table]\nplain = 3\n\"with.dot\" = 4\n\n[table.withdot]\nplain = 5\n\"key.with.dots\" = 6\n" jsonRef := "{\n \"plain\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n \"plain_table\": {\n \"plain\": {\n \"type\": \"integer\",\n \"value\": \"3\"\n },\n \"with.dot\": {\n \"type\": \"integer\",\n \"value\": \"4\"\n }\n },\n \"table\": {\n \"withdot\": {\n \"key.with.dots\": {\n \"type\": \"integer\",\n \"value\": \"6\"\n },\n \"plain\": {\n \"type\": \"integer\",\n \"value\": \"5\"\n }\n }\n },\n \"with.dot\": {\n \"type\": \"integer\",\n \"value\": \"2\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Key_QuotedUnicode(t *testing.T) { input := "\n\"\\u0000\" = \"null\"\n'\\u0000' = \"different key\"\n\"\\u0008 \\u000c \\U00000041 \\u007f \\u0080 \\u00ff \\ud7ff \\ue000 \\uffff \\U00010000 \\U0010ffff\" = \"escaped key\"\n\n\"~ \u0080 ÿ \ud7ff \ue000 \uffff 𐀀 \U0010ffff\" = \"basic key\"\n'l ~ \u0080 ÿ \ud7ff \ue000 \uffff 𐀀 \U0010ffff' = \"literal key\"\n" jsonRef := "{\n \"\\u0000\": {\n \"type\": \"string\",\n \"value\": \"null\"\n },\n \"\\u0008 \\u000c A \x7f \u0080 ÿ \ud7ff \ue000 \uffff 𐀀 \U0010ffff\": {\n \"type\": \"string\",\n \"value\": \"escaped key\"\n },\n \"\\\\u0000\": {\n \"type\": \"string\",\n \"value\": \"different key\"\n },\n \"l ~ \u0080 ÿ \ud7ff \ue000 \uffff 𐀀 \U0010ffff\": {\n \"type\": \"string\",\n \"value\": \"literal key\"\n },\n \"~ \u0080 ÿ \ud7ff \ue000 \uffff 𐀀 \U0010ffff\": {\n \"type\": \"string\",\n \"value\": \"basic key\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Key_Space(t *testing.T) { input := "# Keep whitespace inside quotes keys at all positions.\n\"a b\" = 1\n\" c d \" = 2\n\n[ \" tbl \" ]\n\"\\ttab\\ttab\\t\" = \"tab\"\n" jsonRef := "{\n \" c d \": {\n \"type\": \"integer\",\n \"value\": \"2\"\n },\n \" tbl \": {\n \"\\ttab\\ttab\\t\": {\n \"type\": \"string\",\n \"value\": \"tab\"\n }\n },\n \"a b\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Key_SpecialChars(t *testing.T) { input := "\"=~!@$^&*()_+-`1234567890[]|/?><.,;:'=\" = 1\n" jsonRef := "{\n \"=~!@$^\\u0026*()_+-`1234567890[]|/?\\u003e\\u003c.,;:'=\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Key_SpecialWord(t *testing.T) { input := "false = false\ntrue = 1\ninf = 100000000\nnan = \"ceci n'est pas un nombre\"\n\n" jsonRef := "{\n \"false\": {\n \"type\": \"bool\",\n \"value\": \"false\"\n },\n \"inf\": {\n \"type\": \"integer\",\n \"value\": \"100000000\"\n },\n \"nan\": {\n \"type\": \"string\",\n \"value\": \"ceci n'est pas un nombre\"\n },\n \"true\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Key_Zero(t *testing.T) { input := "0=0\n" jsonRef := "{\n \"0\": {\n \"type\": \"integer\",\n \"value\": \"0\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Array0(t *testing.T) { input := "integers = [ 1, 2, 3 ]\ncolors = [ \"red\", \"yellow\", \"green\" ]\nnested_arrays_of_ints = [ [ 1, 2 ], [3, 4, 5] ]\nnested_mixed_array = [ [ 1, 2 ], [\"a\", \"b\", \"c\"] ]\nstring_array = [ \"all\", 'strings', \"\"\"are the same\"\"\", '''type''' ]\n\n# Mixed-type arrays are allowed\nnumbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ]\ncontributors = [\n \"Foo Bar \",\n { name = \"Baz Qux\", email = \"bazqux@example.com\", url = \"https://example.com/bazqux\" }\n]\n" jsonRef := "{\n \"colors\": [\n {\n \"type\": \"string\",\n \"value\": \"red\"\n },\n {\n \"type\": \"string\",\n \"value\": \"yellow\"\n },\n {\n \"type\": \"string\",\n \"value\": \"green\"\n }\n ],\n \"contributors\": [\n {\n \"type\": \"string\",\n \"value\": \"Foo Bar \\u003cfoo@example.com\\u003e\"\n },\n {\n \"email\": {\n \"type\": \"string\",\n \"value\": \"bazqux@example.com\"\n },\n \"name\": {\n \"type\": \"string\",\n \"value\": \"Baz Qux\"\n },\n \"url\": {\n \"type\": \"string\",\n \"value\": \"https://example.com/bazqux\"\n }\n }\n ],\n \"integers\": [\n {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"2\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"3\"\n }\n ],\n \"nested_arrays_of_ints\": [\n [\n {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"2\"\n }\n ],\n [\n {\n \"type\": \"integer\",\n \"value\": \"3\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"4\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"5\"\n }\n ]\n ],\n \"nested_mixed_array\": [\n [\n {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"2\"\n }\n ],\n [\n {\n \"type\": \"string\",\n \"value\": \"a\"\n },\n {\n \"type\": \"string\",\n \"value\": \"b\"\n },\n {\n \"type\": \"string\",\n \"value\": \"c\"\n }\n ]\n ],\n \"numbers\": [\n {\n \"type\": \"float\",\n \"value\": \"0.1\"\n },\n {\n \"type\": \"float\",\n \"value\": \"0.2\"\n },\n {\n \"type\": \"float\",\n \"value\": \"0.5\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"2\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"5\"\n }\n ],\n \"string_array\": [\n {\n \"type\": \"string\",\n \"value\": \"all\"\n },\n {\n \"type\": \"string\",\n \"value\": \"strings\"\n },\n {\n \"type\": \"string\",\n \"value\": \"are the same\"\n },\n {\n \"type\": \"string\",\n \"value\": \"type\"\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Array1(t *testing.T) { input := "integers2 = [\n 1, 2, 3\n]\n\nintegers3 = [\n 1,\n 2, # this is ok\n]\n" jsonRef := "{\n \"integers2\": [\n {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"2\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"3\"\n }\n ],\n \"integers3\": [\n {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n {\n \"type\": \"integer\",\n \"value\": \"2\"\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_ArrayOfTables0(t *testing.T) { input := "[[products]]\nname = \"Hammer\"\nsku = 738594937\n\n[[products]] # empty table within the array\n\n[[products]]\nname = \"Nail\"\nsku = 284758393\n\ncolor = \"gray\"\n" jsonRef := "{\n \"products\": [\n {\n \"name\": {\n \"type\": \"string\",\n \"value\": \"Hammer\"\n },\n \"sku\": {\n \"type\": \"integer\",\n \"value\": \"738594937\"\n }\n },\n {},\n {\n \"color\": {\n \"type\": \"string\",\n \"value\": \"gray\"\n },\n \"name\": {\n \"type\": \"string\",\n \"value\": \"Nail\"\n },\n \"sku\": {\n \"type\": \"integer\",\n \"value\": \"284758393\"\n }\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_ArrayOfTables1(t *testing.T) { input := "[[fruits]]\nname = \"apple\"\n\n[fruits.physical] # subtable\ncolor = \"red\"\nshape = \"round\"\n\n[[fruits.varieties]] # nested array of tables\nname = \"red delicious\"\n\n[[fruits.varieties]]\nname = \"granny smith\"\n\n\n[[fruits]]\nname = \"banana\"\n\n[[fruits.varieties]]\nname = \"plantain\"\n" jsonRef := "{\n \"fruits\": [\n {\n \"name\": {\n \"type\": \"string\",\n \"value\": \"apple\"\n },\n \"physical\": {\n \"color\": {\n \"type\": \"string\",\n \"value\": \"red\"\n },\n \"shape\": {\n \"type\": \"string\",\n \"value\": \"round\"\n }\n },\n \"varieties\": [\n {\n \"name\": {\n \"type\": \"string\",\n \"value\": \"red delicious\"\n }\n },\n {\n \"name\": {\n \"type\": \"string\",\n \"value\": \"granny smith\"\n }\n }\n ]\n },\n {\n \"name\": {\n \"type\": \"string\",\n \"value\": \"banana\"\n },\n \"varieties\": [\n {\n \"name\": {\n \"type\": \"string\",\n \"value\": \"plantain\"\n }\n }\n ]\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_ArrayOfTables2(t *testing.T) { input := "points = [ { x = 1, y = 2, z = 3 },\n { x = 7, y = 8, z = 9 },\n { x = 2, y = 4, z = 8 } ]\n" jsonRef := "{\n \"points\": [\n {\n \"x\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n \"y\": {\n \"type\": \"integer\",\n \"value\": \"2\"\n },\n \"z\": {\n \"type\": \"integer\",\n \"value\": \"3\"\n }\n },\n {\n \"x\": {\n \"type\": \"integer\",\n \"value\": \"7\"\n },\n \"y\": {\n \"type\": \"integer\",\n \"value\": \"8\"\n },\n \"z\": {\n \"type\": \"integer\",\n \"value\": \"9\"\n }\n },\n {\n \"x\": {\n \"type\": \"integer\",\n \"value\": \"2\"\n },\n \"y\": {\n \"type\": \"integer\",\n \"value\": \"4\"\n },\n \"z\": {\n \"type\": \"integer\",\n \"value\": \"8\"\n }\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Boolean0(t *testing.T) { input := "bool1 = true\nbool2 = false\n" jsonRef := "{\n \"bool1\": {\n \"type\": \"bool\",\n \"value\": \"true\"\n },\n \"bool2\": {\n \"type\": \"bool\",\n \"value\": \"false\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Comment0(t *testing.T) { input := "# This is a full-line comment\nkey = \"value\" # This is a comment at the end of a line\nanother = \"# This is not a comment\"\n" jsonRef := "{\n \"another\": {\n \"type\": \"string\",\n \"value\": \"# This is not a comment\"\n },\n \"key\": {\n \"type\": \"string\",\n \"value\": \"value\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Float0(t *testing.T) { input := "# fractional\nflt1 = +1.0\nflt2 = 3.1415\nflt3 = -0.01\n\n# exponent\nflt4 = 5e+22\nflt5 = 1e06\nflt6 = -2E-2\n\n# both\nflt7 = 6.626e-34\n" jsonRef := "{\n \"flt1\": {\n \"type\": \"float\",\n \"value\": \"1\"\n },\n \"flt2\": {\n \"type\": \"float\",\n \"value\": \"3.1415\"\n },\n \"flt3\": {\n \"type\": \"float\",\n \"value\": \"-0.01\"\n },\n \"flt4\": {\n \"type\": \"float\",\n \"value\": \"5e+22\"\n },\n \"flt5\": {\n \"type\": \"float\",\n \"value\": \"1e+06\"\n },\n \"flt6\": {\n \"type\": \"float\",\n \"value\": \"-0.02\"\n },\n \"flt7\": {\n \"type\": \"float\",\n \"value\": \"6.626e-34\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Float1(t *testing.T) { input := "flt8 = 224_617.445_991_228\n" jsonRef := "{\n \"flt8\": {\n \"type\": \"float\",\n \"value\": \"224617.445991228\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Float2(t *testing.T) { input := "# infinity\nsf1 = inf # positive infinity\nsf2 = +inf # positive infinity\nsf3 = -inf # negative infinity\n\n# not a number\nsf4 = nan # actual sNaN/qNaN encoding is implementation-specific\nsf5 = +nan # same as `nan`\nsf6 = -nan # valid, actual encoding is implementation-specific\n" jsonRef := "{\n \"sf1\": {\n \"type\": \"float\",\n \"value\": \"+inf\"\n },\n \"sf2\": {\n \"type\": \"float\",\n \"value\": \"+inf\"\n },\n \"sf3\": {\n \"type\": \"float\",\n \"value\": \"-inf\"\n },\n \"sf4\": {\n \"type\": \"float\",\n \"value\": \"nan\"\n },\n \"sf5\": {\n \"type\": \"float\",\n \"value\": \"nan\"\n },\n \"sf6\": {\n \"type\": \"float\",\n \"value\": \"nan\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_InlineTable0(t *testing.T) { input := "name = { first = \"Tom\", last = \"Preston-Werner\" }\npoint = { x = 1, y = 2 }\nanimal = { type.name = \"pug\" }\n" jsonRef := "{\n \"animal\": {\n \"type\": {\n \"name\": {\n \"type\": \"string\",\n \"value\": \"pug\"\n }\n }\n },\n \"name\": {\n \"first\": {\n \"type\": \"string\",\n \"value\": \"Tom\"\n },\n \"last\": {\n \"type\": \"string\",\n \"value\": \"Preston-Werner\"\n }\n },\n \"point\": {\n \"x\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n \"y\": {\n \"type\": \"integer\",\n \"value\": \"2\"\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_InlineTable1(t *testing.T) { input := "[name]\nfirst = \"Tom\"\nlast = \"Preston-Werner\"\n\n[point]\nx = 1\ny = 2\n\n[animal]\ntype.name = \"pug\"\n" jsonRef := "{\n \"animal\": {\n \"type\": {\n \"name\": {\n \"type\": \"string\",\n \"value\": \"pug\"\n }\n }\n },\n \"name\": {\n \"first\": {\n \"type\": \"string\",\n \"value\": \"Tom\"\n },\n \"last\": {\n \"type\": \"string\",\n \"value\": \"Preston-Werner\"\n }\n },\n \"point\": {\n \"x\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n },\n \"y\": {\n \"type\": \"integer\",\n \"value\": \"2\"\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_InlineTable2(t *testing.T) { input := "[product]\ntype = { name = \"Nail\" }\n# type.edible = false # INVALID\n" jsonRef := "{\n \"product\": {\n \"type\": {\n \"name\": {\n \"type\": \"string\",\n \"value\": \"Nail\"\n }\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_InlineTable3(t *testing.T) { input := "[product]\ntype.name = \"Nail\"\n# type = { edible = false } # INVALID\n" jsonRef := "{\n \"product\": {\n \"type\": {\n \"name\": {\n \"type\": \"string\",\n \"value\": \"Nail\"\n }\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Integer0(t *testing.T) { input := "int1 = +99\nint2 = 42\nint3 = 0\nint4 = -17\n" jsonRef := "{\n \"int1\": {\n \"type\": \"integer\",\n \"value\": \"99\"\n },\n \"int2\": {\n \"type\": \"integer\",\n \"value\": \"42\"\n },\n \"int3\": {\n \"type\": \"integer\",\n \"value\": \"0\"\n },\n \"int4\": {\n \"type\": \"integer\",\n \"value\": \"-17\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Integer1(t *testing.T) { input := "int5 = 1_000\nint6 = 5_349_221\nint7 = 53_49_221 # Indian number system grouping\nint8 = 1_2_3_4_5 # VALID but discouraged\n" jsonRef := "{\n \"int5\": {\n \"type\": \"integer\",\n \"value\": \"1000\"\n },\n \"int6\": {\n \"type\": \"integer\",\n \"value\": \"5349221\"\n },\n \"int7\": {\n \"type\": \"integer\",\n \"value\": \"5349221\"\n },\n \"int8\": {\n \"type\": \"integer\",\n \"value\": \"12345\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Integer2(t *testing.T) { input := "# hexadecimal with prefix `0x`\nhex1 = 0xDEADBEEF\nhex2 = 0xdeadbeef\nhex3 = 0xdead_beef\n\n# octal with prefix `0o`\noct1 = 0o01234567\noct2 = 0o755 # useful for Unix file permissions\n\n# binary with prefix `0b`\nbin1 = 0b11010110\n" jsonRef := "{\n \"bin1\": {\n \"type\": \"integer\",\n \"value\": \"214\"\n },\n \"hex1\": {\n \"type\": \"integer\",\n \"value\": \"3735928559\"\n },\n \"hex2\": {\n \"type\": \"integer\",\n \"value\": \"3735928559\"\n },\n \"hex3\": {\n \"type\": \"integer\",\n \"value\": \"3735928559\"\n },\n \"oct1\": {\n \"type\": \"integer\",\n \"value\": \"342391\"\n },\n \"oct2\": {\n \"type\": \"integer\",\n \"value\": \"493\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_KeyValuePair0(t *testing.T) { input := "key = \"value\"\n" jsonRef := "{\n \"key\": {\n \"type\": \"string\",\n \"value\": \"value\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Keys0(t *testing.T) { input := "key = \"value\"\nbare_key = \"value\"\nbare-key = \"value\"\n1234 = \"value\"\n" jsonRef := "{\n \"1234\": {\n \"type\": \"string\",\n \"value\": \"value\"\n },\n \"bare-key\": {\n \"type\": \"string\",\n \"value\": \"value\"\n },\n \"bare_key\": {\n \"type\": \"string\",\n \"value\": \"value\"\n },\n \"key\": {\n \"type\": \"string\",\n \"value\": \"value\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Keys1(t *testing.T) { input := "\"127.0.0.1\" = \"value\"\n\"character encoding\" = \"value\"\n\"ʎǝʞ\" = \"value\"\n'key2' = \"value\"\n'quoted \"value\"' = \"value\"\n" jsonRef := "{\n \"127.0.0.1\": {\n \"type\": \"string\",\n \"value\": \"value\"\n },\n \"character encoding\": {\n \"type\": \"string\",\n \"value\": \"value\"\n },\n \"key2\": {\n \"type\": \"string\",\n \"value\": \"value\"\n },\n \"quoted \\\"value\\\"\": {\n \"type\": \"string\",\n \"value\": \"value\"\n },\n \"ʎǝʞ\": {\n \"type\": \"string\",\n \"value\": \"value\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Keys3(t *testing.T) { input := "name = \"Orange\"\nphysical.color = \"orange\"\nphysical.shape = \"round\"\nsite.\"google.com\" = true\n" jsonRef := "{\n \"name\": {\n \"type\": \"string\",\n \"value\": \"Orange\"\n },\n \"physical\": {\n \"color\": {\n \"type\": \"string\",\n \"value\": \"orange\"\n },\n \"shape\": {\n \"type\": \"string\",\n \"value\": \"round\"\n }\n },\n \"site\": {\n \"google.com\": {\n \"type\": \"bool\",\n \"value\": \"true\"\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Keys4(t *testing.T) { input := "fruit.name = \"banana\" # this is best practice\nfruit. color = \"yellow\" # same as fruit.color\nfruit . flavor = \"banana\" # same as fruit.flavor\n" jsonRef := "{\n \"fruit\": {\n \"color\": {\n \"type\": \"string\",\n \"value\": \"yellow\"\n },\n \"flavor\": {\n \"type\": \"string\",\n \"value\": \"banana\"\n },\n \"name\": {\n \"type\": \"string\",\n \"value\": \"banana\"\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Keys5(t *testing.T) { input := "# VALID BUT DISCOURAGED\n\napple.type = \"fruit\"\norange.type = \"fruit\"\n\napple.skin = \"thin\"\norange.skin = \"thick\"\n\napple.color = \"red\"\norange.color = \"orange\"\n" jsonRef := "{\n \"apple\": {\n \"color\": {\n \"type\": \"string\",\n \"value\": \"red\"\n },\n \"skin\": {\n \"type\": \"string\",\n \"value\": \"thin\"\n },\n \"type\": {\n \"type\": \"string\",\n \"value\": \"fruit\"\n }\n },\n \"orange\": {\n \"color\": {\n \"type\": \"string\",\n \"value\": \"orange\"\n },\n \"skin\": {\n \"type\": \"string\",\n \"value\": \"thick\"\n },\n \"type\": {\n \"type\": \"string\",\n \"value\": \"fruit\"\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Keys6(t *testing.T) { input := "# RECOMMENDED\n\napple.type = \"fruit\"\napple.skin = \"thin\"\napple.color = \"red\"\n\norange.type = \"fruit\"\norange.skin = \"thick\"\norange.color = \"orange\"\n" jsonRef := "{\n \"apple\": {\n \"color\": {\n \"type\": \"string\",\n \"value\": \"red\"\n },\n \"skin\": {\n \"type\": \"string\",\n \"value\": \"thin\"\n },\n \"type\": {\n \"type\": \"string\",\n \"value\": \"fruit\"\n }\n },\n \"orange\": {\n \"color\": {\n \"type\": \"string\",\n \"value\": \"orange\"\n },\n \"skin\": {\n \"type\": \"string\",\n \"value\": \"thick\"\n },\n \"type\": {\n \"type\": \"string\",\n \"value\": \"fruit\"\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Keys7(t *testing.T) { input := "3.14159 = \"pi\"\n" jsonRef := "{\n \"3\": {\n \"14159\": {\n \"type\": \"string\",\n \"value\": \"pi\"\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_LocalDate0(t *testing.T) { input := "ld1 = 1979-05-27\n" jsonRef := "{\n \"ld1\": {\n \"type\": \"date-local\",\n \"value\": \"1979-05-27\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_LocalDateTime0(t *testing.T) { input := "ldt1 = 1979-05-27T07:32:00\nldt2 = 1979-05-27T00:32:00.999999\n" jsonRef := "{\n \"ldt1\": {\n \"type\": \"datetime-local\",\n \"value\": \"1979-05-27T07:32:00\"\n },\n \"ldt2\": {\n \"type\": \"datetime-local\",\n \"value\": \"1979-05-27T00:32:00.999999\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_LocalTime0(t *testing.T) { input := "lt1 = 07:32:00\nlt2 = 00:32:00.999999\n" jsonRef := "{\n \"lt1\": {\n \"type\": \"time-local\",\n \"value\": \"07:32:00\"\n },\n \"lt2\": {\n \"type\": \"time-local\",\n \"value\": \"00:32:00.999999\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_OffsetDateTime0(t *testing.T) { input := "odt1 = 1979-05-27T07:32:00Z\nodt2 = 1979-05-27T00:32:00-07:00\nodt3 = 1979-05-27T00:32:00.999999-07:00\n" jsonRef := "{\n \"odt1\": {\n \"type\": \"datetime\",\n \"value\": \"1979-05-27T07:32:00Z\"\n },\n \"odt2\": {\n \"type\": \"datetime\",\n \"value\": \"1979-05-27T00:32:00-07:00\"\n },\n \"odt3\": {\n \"type\": \"datetime\",\n \"value\": \"1979-05-27T00:32:00.999999-07:00\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_OffsetDateTime1(t *testing.T) { input := "odt4 = 1979-05-27 07:32:00Z\n" jsonRef := "{\n \"odt4\": {\n \"type\": \"datetime\",\n \"value\": \"1979-05-27T07:32:00Z\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_String0(t *testing.T) { input := "str = \"I'm a string. \\\"You can quote me\\\". Name\\tJos\\u00E9\\nLocation\\tSF.\"\n" jsonRef := "{\n \"str\": {\n \"type\": \"string\",\n \"value\": \"I'm a string. \\\"You can quote me\\\". Name\\tJosé\\nLocation\\tSF.\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_String1(t *testing.T) { input := "str1 = \"\"\"\nRoses are red\nViolets are blue\"\"\"\n" jsonRef := "{\n \"str1\": {\n \"type\": \"string\",\n \"value\": \"Roses are red\\nViolets are blue\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_String2(t *testing.T) { input := "# On a Unix system, the above multi-line string will most likely be the same as:\nstr2 = \"Roses are red\\nViolets are blue\"\n\n# On a Windows system, it will most likely be equivalent to:\nstr3 = \"Roses are red\\r\\nViolets are blue\"\n" jsonRef := "{\n \"str2\": {\n \"type\": \"string\",\n \"value\": \"Roses are red\\nViolets are blue\"\n },\n \"str3\": {\n \"type\": \"string\",\n \"value\": \"Roses are red\\r\\nViolets are blue\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_String3(t *testing.T) { input := "# The following strings are byte-for-byte equivalent:\nstr1 = \"The quick brown fox jumps over the lazy dog.\"\n\nstr2 = \"\"\"\nThe quick brown \\\n\n\n fox jumps over \\\n the lazy dog.\"\"\"\n\nstr3 = \"\"\"\\\n The quick brown \\\n fox jumps over \\\n the lazy dog.\\\n \"\"\"\n" jsonRef := "{\n \"str1\": {\n \"type\": \"string\",\n \"value\": \"The quick brown fox jumps over the lazy dog.\"\n },\n \"str2\": {\n \"type\": \"string\",\n \"value\": \"The quick brown fox jumps over the lazy dog.\"\n },\n \"str3\": {\n \"type\": \"string\",\n \"value\": \"The quick brown fox jumps over the lazy dog.\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_String4(t *testing.T) { input := "str4 = \"\"\"Here are two quotation marks: \"\". Simple enough.\"\"\"\n# str5 = \"\"\"Here are three quotation marks: \"\"\".\"\"\" # INVALID\nstr5 = \"\"\"Here are three quotation marks: \"\"\\\".\"\"\"\nstr6 = \"\"\"Here are fifteen quotation marks: \"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\"\"\"\\\".\"\"\"\n\n# \"This,\" she said, \"is just a pointless statement.\"\nstr7 = \"\"\"\"This,\" she said, \"is just a pointless statement.\"\"\"\"\n" jsonRef := "{\n \"str4\": {\n \"type\": \"string\",\n \"value\": \"Here are two quotation marks: \\\"\\\". Simple enough.\"\n },\n \"str5\": {\n \"type\": \"string\",\n \"value\": \"Here are three quotation marks: \\\"\\\"\\\".\"\n },\n \"str6\": {\n \"type\": \"string\",\n \"value\": \"Here are fifteen quotation marks: \\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\".\"\n },\n \"str7\": {\n \"type\": \"string\",\n \"value\": \"\\\"This,\\\" she said, \\\"is just a pointless statement.\\\"\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_String5(t *testing.T) { input := "# What you see is what you get.\nwinpath = 'C:\\Users\\nodejs\\templates'\nwinpath2 = '\\\\ServerX\\admin$\\system32\\'\nquoted = 'Tom \"Dubs\" Preston-Werner'\nregex = '<\\i\\c*\\s*>'\n" jsonRef := "{\n \"quoted\": {\n \"type\": \"string\",\n \"value\": \"Tom \\\"Dubs\\\" Preston-Werner\"\n },\n \"regex\": {\n \"type\": \"string\",\n \"value\": \"\\u003c\\\\i\\\\c*\\\\s*\\u003e\"\n },\n \"winpath\": {\n \"type\": \"string\",\n \"value\": \"C:\\\\Users\\\\nodejs\\\\templates\"\n },\n \"winpath2\": {\n \"type\": \"string\",\n \"value\": \"\\\\\\\\ServerX\\\\admin$\\\\system32\\\\\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_String6(t *testing.T) { input := "regex2 = '''I [dw]on't need \\d{2} apples'''\nlines = '''\nThe first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n'''\n" jsonRef := "{\n \"lines\": {\n \"type\": \"string\",\n \"value\": \"The first newline is\\ntrimmed in raw strings.\\n All other whitespace\\n is preserved.\\n\"\n },\n \"regex2\": {\n \"type\": \"string\",\n \"value\": \"I [dw]on't need \\\\d{2} apples\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_String7(t *testing.T) { input := "quot15 = '''Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"'''\n\n# apos15 = '''Here are fifteen apostrophes: '''''''''''''''''' # INVALID\napos15 = \"Here are fifteen apostrophes: '''''''''''''''\"\n\n# 'That,' she said, 'is still pointless.'\nstr = ''''That,' she said, 'is still pointless.''''\n" jsonRef := "{\n \"apos15\": {\n \"type\": \"string\",\n \"value\": \"Here are fifteen apostrophes: '''''''''''''''\"\n },\n \"quot15\": {\n \"type\": \"string\",\n \"value\": \"Here are fifteen quotation marks: \\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\"\n },\n \"str\": {\n \"type\": \"string\",\n \"value\": \"'That,' she said, 'is still pointless.'\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Table0(t *testing.T) { input := "[table]\n" jsonRef := "{\n \"table\": {}\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Table1(t *testing.T) { input := "[table-1]\nkey1 = \"some string\"\nkey2 = 123\n\n[table-2]\nkey1 = \"another string\"\nkey2 = 456\n" jsonRef := "{\n \"table-1\": {\n \"key1\": {\n \"type\": \"string\",\n \"value\": \"some string\"\n },\n \"key2\": {\n \"type\": \"integer\",\n \"value\": \"123\"\n }\n },\n \"table-2\": {\n \"key1\": {\n \"type\": \"string\",\n \"value\": \"another string\"\n },\n \"key2\": {\n \"type\": \"integer\",\n \"value\": \"456\"\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Table2(t *testing.T) { input := "[dog.\"tater.man\"]\ntype.name = \"pug\"\n" jsonRef := "{\n \"dog\": {\n \"tater.man\": {\n \"type\": {\n \"name\": {\n \"type\": \"string\",\n \"value\": \"pug\"\n }\n }\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Table3(t *testing.T) { input := "[a.b.c] # this is best practice\n[ d.e.f ] # same as [d.e.f]\n[ g . h . i ] # same as [g.h.i]\n[ j . \"ʞ\" . 'l' ] # same as [j.\"ʞ\".'l']\n" jsonRef := "{\n \"a\": {\n \"b\": {\n \"c\": {}\n }\n },\n \"d\": {\n \"e\": {\n \"f\": {}\n }\n },\n \"g\": {\n \"h\": {\n \"i\": {}\n }\n },\n \"j\": {\n \"ʞ\": {\n \"l\": {}\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Table4(t *testing.T) { input := "# [x] you\n# [x.y] don't\n# [x.y.z] need these\n[x.y.z.w] # for this to work\n\n[x] # defining a super-table afterward is ok\n" jsonRef := "{\n \"x\": {\n \"y\": {\n \"z\": {\n \"w\": {}\n }\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Table5(t *testing.T) { input := "# VALID BUT DISCOURAGED\n[fruit.apple]\n[animal]\n[fruit.orange]\n" jsonRef := "{\n \"animal\": {},\n \"fruit\": {\n \"apple\": {},\n \"orange\": {}\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Table6(t *testing.T) { input := "# RECOMMENDED\n[fruit.apple]\n[fruit.orange]\n[animal]\n" jsonRef := "{\n \"animal\": {},\n \"fruit\": {\n \"apple\": {},\n \"orange\": {}\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Table7(t *testing.T) { input := "# Top-level table begins.\nname = \"Fido\"\nbreed = \"pug\"\n\n# Top-level table ends.\n[owner]\nname = \"Regina Dogman\"\nmember_since = 1999-08-04\n" jsonRef := "{\n \"breed\": {\n \"type\": \"string\",\n \"value\": \"pug\"\n },\n \"name\": {\n \"type\": \"string\",\n \"value\": \"Fido\"\n },\n \"owner\": {\n \"member_since\": {\n \"type\": \"date-local\",\n \"value\": \"1999-08-04\"\n },\n \"name\": {\n \"type\": \"string\",\n \"value\": \"Regina Dogman\"\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Table8(t *testing.T) { input := "fruit.apple.color = \"red\"\n# Defines a table named fruit\n# Defines a table named fruit.apple\n\nfruit.apple.taste.sweet = true\n# Defines a table named fruit.apple.taste\n# fruit and fruit.apple were already created\n" jsonRef := "{\n \"fruit\": {\n \"apple\": {\n \"color\": {\n \"type\": \"string\",\n \"value\": \"red\"\n },\n \"taste\": {\n \"sweet\": {\n \"type\": \"bool\",\n \"value\": \"true\"\n }\n }\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Spec_Table9(t *testing.T) { input := "[fruit]\napple.color = \"red\"\napple.taste.sweet = true\n\n# [fruit.apple] # INVALID\n# [fruit.apple.taste] # INVALID\n\n[fruit.apple.texture] # you can add sub-tables\nsmooth = true\n" jsonRef := "{\n \"fruit\": {\n \"apple\": {\n \"color\": {\n \"type\": \"string\",\n \"value\": \"red\"\n },\n \"taste\": {\n \"sweet\": {\n \"type\": \"bool\",\n \"value\": \"true\"\n }\n },\n \"texture\": {\n \"smooth\": {\n \"type\": \"bool\",\n \"value\": \"true\"\n }\n }\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_String_DoubleQuoteEscape(t *testing.T) { input := "test = \"\\\"one\\\"\"\n" jsonRef := "{\n \"test\": {\n \"type\": \"string\",\n \"value\": \"\\\"one\\\"\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_String_Empty(t *testing.T) { input := "answer = \"\"\n" jsonRef := "{\n \"answer\": {\n \"type\": \"string\",\n \"value\": \"\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_String_EndsInWhitespaceEscape(t *testing.T) { input := "beee = \"\"\"\nheeee\ngeeee\\ \n\n\n \"\"\"\n" jsonRef := "{\n \"beee\": {\n \"type\": \"string\",\n \"value\": \"heeee\\ngeeee\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_String_EscapeTricky(t *testing.T) { input := "end_esc = \"String does not end here\\\" but ends here\\\\\"\nlit_end_esc = 'String ends here\\'\n\nmultiline_unicode = \"\"\"\n\\u00a0\"\"\"\n\nmultiline_not_unicode = \"\"\"\n\\\\u0041\"\"\"\n\nmultiline_end_esc = \"\"\"When will it end? \\\"\"\"...\"\"\\\" should be here\\\"\"\"\"\n\nlit_multiline_not_unicode = '''\n\\u007f'''\n\nlit_multiline_end = '''There is no escape\\'''\n" jsonRef := "{\n \"end_esc\": {\n \"type\": \"string\",\n \"value\": \"String does not end here\\\" but ends here\\\\\"\n },\n \"lit_end_esc\": {\n \"type\": \"string\",\n \"value\": \"String ends here\\\\\"\n },\n \"lit_multiline_end\": {\n \"type\": \"string\",\n \"value\": \"There is no escape\\\\\"\n },\n \"lit_multiline_not_unicode\": {\n \"type\": \"string\",\n \"value\": \"\\\\u007f\"\n },\n \"multiline_end_esc\": {\n \"type\": \"string\",\n \"value\": \"When will it end? \\\"\\\"\\\"...\\\"\\\"\\\" should be here\\\"\"\n },\n \"multiline_not_unicode\": {\n \"type\": \"string\",\n \"value\": \"\\\\u0041\"\n },\n \"multiline_unicode\": {\n \"type\": \"string\",\n \"value\": \"\u00a0\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_String_EscapedEscape(t *testing.T) { input := "answer = \"\\\\x64\"\n" jsonRef := "{\n \"answer\": {\n \"type\": \"string\",\n \"value\": \"\\\\x64\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_String_Escapes(t *testing.T) { input := "backspace = \"This string has a \\b backspace character.\"\ntab = \"This string has a \\t tab character.\"\nnewline = \"This string has a \\n new line character.\"\nformfeed = \"This string has a \\f form feed character.\"\ncarriage = \"This string has a \\r carriage return character.\"\nquote = \"This string has a \\\" quote character.\"\nbackslash = \"This string has a \\\\ backslash character.\"\nnotunicode1 = \"This string does not have a unicode \\\\u escape.\"\nnotunicode2 = \"This string does not have a unicode \\u005Cu escape.\"\nnotunicode3 = \"This string does not have a unicode \\\\u0075 escape.\"\nnotunicode4 = \"This string does not have a unicode \\\\\\u0075 escape.\"\ndelete = \"This string has a \\u007F delete control code.\"\nunitseparator = \"This string has a \\u001F unit separator control code.\"\n" jsonRef := "{\n \"backslash\": {\n \"type\": \"string\",\n \"value\": \"This string has a \\\\ backslash character.\"\n },\n \"backspace\": {\n \"type\": \"string\",\n \"value\": \"This string has a \\u0008 backspace character.\"\n },\n \"carriage\": {\n \"type\": \"string\",\n \"value\": \"This string has a \\r carriage return character.\"\n },\n \"delete\": {\n \"type\": \"string\",\n \"value\": \"This string has a \x7f delete control code.\"\n },\n \"formfeed\": {\n \"type\": \"string\",\n \"value\": \"This string has a \\u000c form feed character.\"\n },\n \"newline\": {\n \"type\": \"string\",\n \"value\": \"This string has a \\n new line character.\"\n },\n \"notunicode1\": {\n \"type\": \"string\",\n \"value\": \"This string does not have a unicode \\\\u escape.\"\n },\n \"notunicode2\": {\n \"type\": \"string\",\n \"value\": \"This string does not have a unicode \\\\u escape.\"\n },\n \"notunicode3\": {\n \"type\": \"string\",\n \"value\": \"This string does not have a unicode \\\\u0075 escape.\"\n },\n \"notunicode4\": {\n \"type\": \"string\",\n \"value\": \"This string does not have a unicode \\\\u escape.\"\n },\n \"quote\": {\n \"type\": \"string\",\n \"value\": \"This string has a \\\" quote character.\"\n },\n \"tab\": {\n \"type\": \"string\",\n \"value\": \"This string has a \\t tab character.\"\n },\n \"unitseparator\": {\n \"type\": \"string\",\n \"value\": \"This string has a \\u001f unit separator control code.\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_String_MultilineEscapedCrlf(t *testing.T) { input := "# The following line should be an unescaped backslash followed by a Windows\r\n# newline sequence (\"\\r\\n\")\r\n0=\"\"\"\\\r\n\"\"\"\r\n" jsonRef := "{\n \"0\": {\n \"type\": \"string\",\n \"value\": \"\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_String_MultilineQuotes(t *testing.T) { input := "# Make sure that quotes inside multiline strings are allowed, including right\n# after the opening '''/\"\"\" and before the closing '''/\"\"\"\n\nlit_one = ''''one quote''''\nlit_two = '''''two quotes'''''\nlit_one_space = ''' 'one quote' '''\nlit_two_space = ''' ''two quotes'' '''\n\none = \"\"\"\"one quote\"\"\"\"\ntwo = \"\"\"\"\"two quotes\"\"\"\"\"\none_space = \"\"\" \"one quote\" \"\"\"\ntwo_space = \"\"\" \"\"two quotes\"\" \"\"\"\n\nmismatch1 = \"\"\"aaa'''bbb\"\"\"\nmismatch2 = '''aaa\"\"\"bbb'''\n\n# Three opening \"\"\", then one escaped \", then two \"\" (allowed), and then three\n# closing \"\"\"\nescaped = \"\"\"lol\\\"\"\"\"\"\"\n\nfive-quotes = \"\"\"\nClosing with five quotes\n\"\"\"\"\"\nfour-quotes = \"\"\"\nClosing with four quotes\n\"\"\"\"\n" jsonRef := "{\n \"escaped\": {\n \"type\": \"string\",\n \"value\": \"lol\\\"\\\"\\\"\"\n },\n \"five-quotes\": {\n \"type\": \"string\",\n \"value\": \"Closing with five quotes\\n\\\"\\\"\"\n },\n \"four-quotes\": {\n \"type\": \"string\",\n \"value\": \"Closing with four quotes\\n\\\"\"\n },\n \"lit_one\": {\n \"type\": \"string\",\n \"value\": \"'one quote'\"\n },\n \"lit_one_space\": {\n \"type\": \"string\",\n \"value\": \" 'one quote' \"\n },\n \"lit_two\": {\n \"type\": \"string\",\n \"value\": \"''two quotes''\"\n },\n \"lit_two_space\": {\n \"type\": \"string\",\n \"value\": \" ''two quotes'' \"\n },\n \"mismatch1\": {\n \"type\": \"string\",\n \"value\": \"aaa'''bbb\"\n },\n \"mismatch2\": {\n \"type\": \"string\",\n \"value\": \"aaa\\\"\\\"\\\"bbb\"\n },\n \"one\": {\n \"type\": \"string\",\n \"value\": \"\\\"one quote\\\"\"\n },\n \"one_space\": {\n \"type\": \"string\",\n \"value\": \" \\\"one quote\\\" \"\n },\n \"two\": {\n \"type\": \"string\",\n \"value\": \"\\\"\\\"two quotes\\\"\\\"\"\n },\n \"two_space\": {\n \"type\": \"string\",\n \"value\": \" \\\"\\\"two quotes\\\"\\\" \"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_String_Multiline(t *testing.T) { input := "# NOTE: this file includes some literal tab characters.\n\nmultiline_empty_one = \"\"\"\"\"\"\n\n# A newline immediately following the opening delimiter will be trimmed.\nmultiline_empty_two = \"\"\"\n\"\"\"\n\n# \\ at the end of line trims newlines as well; note that last \\ is followed by\n# two spaces, which are ignored.\nmultiline_empty_three = \"\"\"\\\n \"\"\"\nmultiline_empty_four = \"\"\"\\\n \\\n \\ \n \"\"\"\n\nequivalent_one = \"The quick brown fox jumps over the lazy dog.\"\nequivalent_two = \"\"\"\nThe quick brown \\\n\n\n fox jumps over \\\n the lazy dog.\"\"\"\n\nequivalent_three = \"\"\"\\\n The quick brown \\\n fox jumps over \\\n the lazy dog.\\\n \"\"\"\n\nwhitespace-after-bs = \"\"\"\\\n The quick brown \\\n fox jumps over \\ \n the lazy dog.\\\t\n \"\"\"\n\nno-space = \"\"\"a\\\n b\"\"\"\n\n# Has tab character.\nkeep-ws-before = \"\"\"a \t\\\n b\"\"\"\n\nescape-bs-1 = \"\"\"a \\\\\nb\"\"\"\n\nescape-bs-2 = \"\"\"a \\\\\\\nb\"\"\"\n\nescape-bs-3 = \"\"\"a \\\\\\\\\n b\"\"\"\n" jsonRef := "{\n \"equivalent_one\": {\n \"type\": \"string\",\n \"value\": \"The quick brown fox jumps over the lazy dog.\"\n },\n \"equivalent_three\": {\n \"type\": \"string\",\n \"value\": \"The quick brown fox jumps over the lazy dog.\"\n },\n \"equivalent_two\": {\n \"type\": \"string\",\n \"value\": \"The quick brown fox jumps over the lazy dog.\"\n },\n \"escape-bs-1\": {\n \"type\": \"string\",\n \"value\": \"a \\\\\\nb\"\n },\n \"escape-bs-2\": {\n \"type\": \"string\",\n \"value\": \"a \\\\b\"\n },\n \"escape-bs-3\": {\n \"type\": \"string\",\n \"value\": \"a \\\\\\\\\\n b\"\n },\n \"keep-ws-before\": {\n \"type\": \"string\",\n \"value\": \"a \\tb\"\n },\n \"multiline_empty_four\": {\n \"type\": \"string\",\n \"value\": \"\"\n },\n \"multiline_empty_one\": {\n \"type\": \"string\",\n \"value\": \"\"\n },\n \"multiline_empty_three\": {\n \"type\": \"string\",\n \"value\": \"\"\n },\n \"multiline_empty_two\": {\n \"type\": \"string\",\n \"value\": \"\"\n },\n \"no-space\": {\n \"type\": \"string\",\n \"value\": \"ab\"\n },\n \"whitespace-after-bs\": {\n \"type\": \"string\",\n \"value\": \"The quick brown fox jumps over the lazy dog.\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_String_Nl(t *testing.T) { input := "nl_mid = \"val\\nue\"\nnl_end = \"\"\"value\\n\"\"\"\n\nlit_nl_end = '''value\\n'''\nlit_nl_mid = 'val\\nue'\nlit_nl_uni = 'val\\ue'\n" jsonRef := "{\n \"lit_nl_end\": {\n \"type\": \"string\",\n \"value\": \"value\\\\n\"\n },\n \"lit_nl_mid\": {\n \"type\": \"string\",\n \"value\": \"val\\\\nue\"\n },\n \"lit_nl_uni\": {\n \"type\": \"string\",\n \"value\": \"val\\\\ue\"\n },\n \"nl_end\": {\n \"type\": \"string\",\n \"value\": \"value\\n\"\n },\n \"nl_mid\": {\n \"type\": \"string\",\n \"value\": \"val\\nue\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_String_QuotedUnicode(t *testing.T) { input := "\nescaped_string = \"\\u0000 \\u0008 \\u000c \\U00000041 \\u007f \\u0080 \\u00ff \\ud7ff \\ue000 \\uffff \\U00010000 \\U0010ffff\"\nnot_escaped_string = '\\u0000 \\u0008 \\u000c \\U00000041 \\u007f \\u0080 \\u00ff \\ud7ff \\ue000 \\uffff \\U00010000 \\U0010ffff'\n\nbasic_string = \"~ \u0080 ÿ \ud7ff \ue000 \uffff 𐀀 \U0010ffff\"\nliteral_string = '~ \u0080 ÿ \ud7ff \ue000 \uffff 𐀀 \U0010ffff'\n" jsonRef := "{\n \"basic_string\": {\n \"type\": \"string\",\n \"value\": \"~ \u0080 ÿ \ud7ff \ue000 \uffff 𐀀 \U0010ffff\"\n },\n \"escaped_string\": {\n \"type\": \"string\",\n \"value\": \"\\u0000 \\u0008 \\u000c A \x7f \u0080 ÿ \ud7ff \ue000 \uffff 𐀀 \U0010ffff\"\n },\n \"literal_string\": {\n \"type\": \"string\",\n \"value\": \"~ \u0080 ÿ \ud7ff \ue000 \uffff 𐀀 \U0010ffff\"\n },\n \"not_escaped_string\": {\n \"type\": \"string\",\n \"value\": \"\\\\u0000 \\\\u0008 \\\\u000c \\\\U00000041 \\\\u007f \\\\u0080 \\\\u00ff \\\\ud7ff \\\\ue000 \\\\uffff \\\\U00010000 \\\\U0010ffff\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_String_RawMultiline(t *testing.T) { input := "# Single ' should be allowed.\noneline = '''This string has a ' quote character.'''\n\n# A newline immediately following the opening delimiter will be trimmed.\nfirstnl = '''\nThis string has a ' quote character.'''\n\n# All other whitespace and newline characters remain intact.\nmultiline = '''\nThis string\nhas ' a quote character\nand more than\none newline\nin it.'''\n\n# Tab character in literal string does not need to be escaped\nmultiline_with_tab = '''First line\n\t Followed by a tab'''\n\nthis-str-has-apostrophes='''' there's one already\n'' two more\n'''''\n" jsonRef := "{\n \"firstnl\": {\n \"type\": \"string\",\n \"value\": \"This string has a ' quote character.\"\n },\n \"multiline\": {\n \"type\": \"string\",\n \"value\": \"This string\\nhas ' a quote character\\nand more than\\none newline\\nin it.\"\n },\n \"multiline_with_tab\": {\n \"type\": \"string\",\n \"value\": \"First line\\n\\t Followed by a tab\"\n },\n \"oneline\": {\n \"type\": \"string\",\n \"value\": \"This string has a ' quote character.\"\n },\n \"this-str-has-apostrophes\": {\n \"type\": \"string\",\n \"value\": \"' there's one already\\n'' two more\\n''\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_String_Raw(t *testing.T) { input := "backspace = 'This string has a \\b backspace character.'\ntab = 'This string has a \\t tab character.'\nunescaped_tab = 'This string has an \t unescaped tab character.'\nnewline = 'This string has a \\n new line character.'\nformfeed = 'This string has a \\f form feed character.'\ncarriage = 'This string has a \\r carriage return character.'\nslash = 'This string has a \\/ slash character.'\nbackslash = 'This string has a \\\\ backslash character.'\n" jsonRef := "{\n \"backslash\": {\n \"type\": \"string\",\n \"value\": \"This string has a \\\\\\\\ backslash character.\"\n },\n \"backspace\": {\n \"type\": \"string\",\n \"value\": \"This string has a \\\\b backspace character.\"\n },\n \"carriage\": {\n \"type\": \"string\",\n \"value\": \"This string has a \\\\r carriage return character.\"\n },\n \"formfeed\": {\n \"type\": \"string\",\n \"value\": \"This string has a \\\\f form feed character.\"\n },\n \"newline\": {\n \"type\": \"string\",\n \"value\": \"This string has a \\\\n new line character.\"\n },\n \"slash\": {\n \"type\": \"string\",\n \"value\": \"This string has a \\\\/ slash character.\"\n },\n \"tab\": {\n \"type\": \"string\",\n \"value\": \"This string has a \\\\t tab character.\"\n },\n \"unescaped_tab\": {\n \"type\": \"string\",\n \"value\": \"This string has an \\t unescaped tab character.\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_String_Simple(t *testing.T) { input := "answer = \"You are not drinking enough whisky.\"\n" jsonRef := "{\n \"answer\": {\n \"type\": \"string\",\n \"value\": \"You are not drinking enough whisky.\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_String_UnicodeEscape(t *testing.T) { input := "delta-1 = \"\\u03B4\"\ndelta-2 = \"\\U000003B4\"\na = \"\\u0061\"\nb = \"\\u0062\"\nc = \"\\U00000063\"\nnull-1 = \"\\u0000\"\nnull-2 = \"\\U00000000\"\n\nml-delta-1 = \"\"\"\\u03B4\"\"\"\nml-delta-2 = \"\"\"\\U000003B4\"\"\"\nml-a = \"\"\"\\u0061\"\"\"\nml-b = \"\"\"\\u0062\"\"\"\nml-c = \"\"\"\\U00000063\"\"\"\nml-null-1 = \"\"\"\\u0000\"\"\"\nml-null-2 = \"\"\"\\U00000000\"\"\"\n" jsonRef := "{\n \"a\": {\n \"type\": \"string\",\n \"value\": \"a\"\n },\n \"b\": {\n \"type\": \"string\",\n \"value\": \"b\"\n },\n \"c\": {\n \"type\": \"string\",\n \"value\": \"c\"\n },\n \"delta-1\": {\n \"type\": \"string\",\n \"value\": \"δ\"\n },\n \"delta-2\": {\n \"type\": \"string\",\n \"value\": \"δ\"\n },\n \"ml-a\": {\n \"type\": \"string\",\n \"value\": \"a\"\n },\n \"ml-b\": {\n \"type\": \"string\",\n \"value\": \"b\"\n },\n \"ml-c\": {\n \"type\": \"string\",\n \"value\": \"c\"\n },\n \"ml-delta-1\": {\n \"type\": \"string\",\n \"value\": \"δ\"\n },\n \"ml-delta-2\": {\n \"type\": \"string\",\n \"value\": \"δ\"\n },\n \"ml-null-1\": {\n \"type\": \"string\",\n \"value\": \"\\u0000\"\n },\n \"ml-null-2\": {\n \"type\": \"string\",\n \"value\": \"\\u0000\"\n },\n \"null-1\": {\n \"type\": \"string\",\n \"value\": \"\\u0000\"\n },\n \"null-2\": {\n \"type\": \"string\",\n \"value\": \"\\u0000\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_String_UnicodeLiteral(t *testing.T) { input := "answer = \"δ\"\n" jsonRef := "{\n \"answer\": {\n \"type\": \"string\",\n \"value\": \"δ\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_String_WithPound(t *testing.T) { input := "pound = \"We see no # comments here.\"\npoundcomment = \"But there are # some comments here.\" # Did I # mess you up?\n" jsonRef := "{\n \"pound\": {\n \"type\": \"string\",\n \"value\": \"We see no # comments here.\"\n },\n \"poundcomment\": {\n \"type\": \"string\",\n \"value\": \"But there are # some comments here.\"\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Table_ArrayImplicitAndExplicitAfter(t *testing.T) { input := "[[a.b]]\nx = 1\n\n[a]\ny = 2\n" jsonRef := "{\n \"a\": {\n \"b\": [\n {\n \"x\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n }\n ],\n \"y\": {\n \"type\": \"integer\",\n \"value\": \"2\"\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Table_ArrayImplicit(t *testing.T) { input := "[[albums.songs]]\nname = \"Glory Days\"\n" jsonRef := "{\n \"albums\": {\n \"songs\": [\n {\n \"name\": {\n \"type\": \"string\",\n \"value\": \"Glory Days\"\n }\n }\n ]\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Table_ArrayMany(t *testing.T) { input := "[[people]]\nfirst_name = \"Bruce\"\nlast_name = \"Springsteen\"\n\n[[people]]\nfirst_name = \"Eric\"\nlast_name = \"Clapton\"\n\n[[people]]\nfirst_name = \"Bob\"\nlast_name = \"Seger\"\n" jsonRef := "{\n \"people\": [\n {\n \"first_name\": {\n \"type\": \"string\",\n \"value\": \"Bruce\"\n },\n \"last_name\": {\n \"type\": \"string\",\n \"value\": \"Springsteen\"\n }\n },\n {\n \"first_name\": {\n \"type\": \"string\",\n \"value\": \"Eric\"\n },\n \"last_name\": {\n \"type\": \"string\",\n \"value\": \"Clapton\"\n }\n },\n {\n \"first_name\": {\n \"type\": \"string\",\n \"value\": \"Bob\"\n },\n \"last_name\": {\n \"type\": \"string\",\n \"value\": \"Seger\"\n }\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Table_ArrayNest(t *testing.T) { input := "[[albums]]\nname = \"Born to Run\"\n\n [[albums.songs]]\n name = \"Jungleland\"\n\n [[albums.songs]]\n name = \"Meeting Across the River\"\n\n[[albums]]\nname = \"Born in the USA\"\n \n [[albums.songs]]\n name = \"Glory Days\"\n\n [[albums.songs]]\n name = \"Dancing in the Dark\"\n" jsonRef := "{\n \"albums\": [\n {\n \"name\": {\n \"type\": \"string\",\n \"value\": \"Born to Run\"\n },\n \"songs\": [\n {\n \"name\": {\n \"type\": \"string\",\n \"value\": \"Jungleland\"\n }\n },\n {\n \"name\": {\n \"type\": \"string\",\n \"value\": \"Meeting Across the River\"\n }\n }\n ]\n },\n {\n \"name\": {\n \"type\": \"string\",\n \"value\": \"Born in the USA\"\n },\n \"songs\": [\n {\n \"name\": {\n \"type\": \"string\",\n \"value\": \"Glory Days\"\n }\n },\n {\n \"name\": {\n \"type\": \"string\",\n \"value\": \"Dancing in the Dark\"\n }\n }\n ]\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Table_ArrayOne(t *testing.T) { input := "[[people]]\nfirst_name = \"Bruce\"\nlast_name = \"Springsteen\"\n" jsonRef := "{\n \"people\": [\n {\n \"first_name\": {\n \"type\": \"string\",\n \"value\": \"Bruce\"\n },\n \"last_name\": {\n \"type\": \"string\",\n \"value\": \"Springsteen\"\n }\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Table_ArrayTableArray(t *testing.T) { input := "[[a]]\n [[a.b]]\n [a.b.c]\n d = \"val0\"\n [[a.b]]\n [a.b.c]\n d = \"val1\"\n" jsonRef := "{\n \"a\": [\n {\n \"b\": [\n {\n \"c\": {\n \"d\": {\n \"type\": \"string\",\n \"value\": \"val0\"\n }\n }\n },\n {\n \"c\": {\n \"d\": {\n \"type\": \"string\",\n \"value\": \"val1\"\n }\n }\n }\n ]\n }\n ]\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Table_ArrayWithinDotted(t *testing.T) { input := "[fruit]\napple.color = \"red\"\n\n[[fruit.apple.seeds]]\nsize = 2\n" jsonRef := "{\n \"fruit\": {\n \"apple\": {\n \"color\": {\n \"type\": \"string\",\n \"value\": \"red\"\n },\n \"seeds\": [\n {\n \"size\": {\n \"type\": \"integer\",\n \"value\": \"2\"\n }\n }\n ]\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Table_EmptyName(t *testing.T) { input := "['']\nx = 1\n\n[\"\".a]\nx = 2\n\n[a.'']\nx = 3\n" jsonRef := "{\n \"\": {\n \"a\": {\n \"x\": {\n \"type\": \"integer\",\n \"value\": \"2\"\n }\n },\n \"x\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n },\n \"a\": {\n \"\": {\n \"x\": {\n \"type\": \"integer\",\n \"value\": \"3\"\n }\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Table_Empty(t *testing.T) { input := "[a]\n" jsonRef := "{\n \"a\": {}\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Table_Keyword(t *testing.T) { input := "[true]\n\n[false]\n\n[inf]\n\n[nan]\n\n\n" jsonRef := "{\n \"false\": {},\n \"inf\": {},\n \"nan\": {},\n \"true\": {}\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Table_Names(t *testing.T) { input := "[a.b.c]\n[a.\"b.c\"]\n[a.'d.e']\n[a.' x ']\n[ d.e.f ]\n[ g . h . i ]\n[ j . \"ʞ\" . 'l' ]\n\n[x.1.2]\n" jsonRef := "{\n \"a\": {\n \" x \": {},\n \"b\": {\n \"c\": {}\n },\n \"b.c\": {},\n \"d.e\": {}\n },\n \"d\": {\n \"e\": {\n \"f\": {}\n }\n },\n \"g\": {\n \"h\": {\n \"i\": {}\n }\n },\n \"j\": {\n \"ʞ\": {\n \"l\": {}\n }\n },\n \"x\": {\n \"1\": {\n \"2\": {}\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Table_NoEol(t *testing.T) { input := "[table]\n" jsonRef := "{\n \"table\": {}\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Table_SubEmpty(t *testing.T) { input := "[a]\n[a.b]\n" jsonRef := "{\n \"a\": {\n \"b\": {}\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Table_Sub(t *testing.T) { input := "[a]\nkey = 1\n\n# a.extend is a key inside the \"a\" table.\n[a.extend]\nkey = 2\n\n[a.extend.more]\nkey = 3\n" jsonRef := "{\n \"a\": {\n \"extend\": {\n \"key\": {\n \"type\": \"integer\",\n \"value\": \"2\"\n },\n \"more\": {\n \"key\": {\n \"type\": \"integer\",\n \"value\": \"3\"\n }\n }\n },\n \"key\": {\n \"type\": \"integer\",\n \"value\": \"1\"\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Table_Whitespace(t *testing.T) { input := "[\"valid key\"]\n" jsonRef := "{\n \"valid key\": {}\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Table_WithLiteralString(t *testing.T) { input := "['a']\n[a.'\"b\"']\n[a.'\"b\"'.c]\nanswer = 42 \n" jsonRef := "{\n \"a\": {\n \"\\\"b\\\"\": {\n \"c\": {\n \"answer\": {\n \"type\": \"integer\",\n \"value\": \"42\"\n }\n }\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Table_WithPound(t *testing.T) { input := "[\"key#group\"]\nanswer = 42\n" jsonRef := "{\n \"key#group\": {\n \"answer\": {\n \"type\": \"integer\",\n \"value\": \"42\"\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Table_WithSingleQuotes(t *testing.T) { input := "['a']\n[a.'b']\n[a.'b'.c]\nanswer = 42 \n" jsonRef := "{\n \"a\": {\n \"b\": {\n \"c\": {\n \"answer\": {\n \"type\": \"integer\",\n \"value\": \"42\"\n }\n }\n }\n }\n}\n" testgenValid(t, input, jsonRef) } func TestTOMLTest_Valid_Table_WithoutSuper(t *testing.T) { input := "# [x] you\n# [x.y] don't\n# [x.y.z] need these\n[x.y.z.w] # for this to work\n[x] # defining a super-table afterwards is ok\n" jsonRef := "{\n \"x\": {\n \"y\": {\n \"z\": {\n \"w\": {}\n }\n }\n }\n}\n" testgenValid(t, input, jsonRef) } go-toml-2.1.1/types.go000066400000000000000000000006721453566013500146010ustar00rootroot00000000000000package toml import ( "encoding" "reflect" "time" ) var timeType = reflect.TypeOf((*time.Time)(nil)).Elem() var textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() var textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() var mapStringInterfaceType = reflect.TypeOf(map[string]interface{}(nil)) var sliceInterfaceType = reflect.TypeOf([]interface{}(nil)) var stringType = reflect.TypeOf("") go-toml-2.1.1/unmarshaler.go000066400000000000000000000751611453566013500157630ustar00rootroot00000000000000package toml import ( "encoding" "errors" "fmt" "io" "io/ioutil" "math" "reflect" "strings" "sync/atomic" "time" "github.com/pelletier/go-toml/v2/internal/danger" "github.com/pelletier/go-toml/v2/internal/tracker" "github.com/pelletier/go-toml/v2/unstable" ) // Unmarshal deserializes a TOML document into a Go value. // // It is a shortcut for Decoder.Decode() with the default options. func Unmarshal(data []byte, v interface{}) error { p := unstable.Parser{} p.Reset(data) d := decoder{p: &p} return d.FromParser(v) } // Decoder reads and decode a TOML document from an input stream. type Decoder struct { // input r io.Reader // global settings strict bool } // NewDecoder creates a new Decoder that will read from r. func NewDecoder(r io.Reader) *Decoder { return &Decoder{r: r} } // DisallowUnknownFields causes the Decoder to return an error when the // destination is a struct and the input contains a key that does not match a // non-ignored field. // // In that case, the Decoder returns a StrictMissingError that can be used to // retrieve the individual errors as well as generate a human readable // description of the missing fields. func (d *Decoder) DisallowUnknownFields() *Decoder { d.strict = true return d } // Decode the whole content of r into v. // // By default, values in the document that don't exist in the target Go value // are ignored. See Decoder.DisallowUnknownFields() to change this behavior. // // When a TOML local date, time, or date-time is decoded into a time.Time, its // value is represented in time.Local timezone. Otherwise the appropriate Local* // structure is used. For time values, precision up to the nanosecond is // supported by truncating extra digits. // // Empty tables decoded in an interface{} create an empty initialized // map[string]interface{}. // // Types implementing the encoding.TextUnmarshaler interface are decoded from a // TOML string. // // When decoding a number, go-toml will return an error if the number is out of // bounds for the target type (which includes negative numbers when decoding // into an unsigned int). // // If an error occurs while decoding the content of the document, this function // returns a toml.DecodeError, providing context about the issue. When using // strict mode and a field is missing, a `toml.StrictMissingError` is // returned. In any other case, this function returns a standard Go error. // // # Type mapping // // List of supported TOML types and their associated accepted Go types: // // String -> string // Integer -> uint*, int*, depending on size // Float -> float*, depending on size // Boolean -> bool // Offset Date-Time -> time.Time // Local Date-time -> LocalDateTime, time.Time // Local Date -> LocalDate, time.Time // Local Time -> LocalTime, time.Time // Array -> slice and array, depending on elements types // Table -> map and struct // Inline Table -> same as Table // Array of Tables -> same as Array and Table func (d *Decoder) Decode(v interface{}) error { b, err := ioutil.ReadAll(d.r) if err != nil { return fmt.Errorf("toml: %w", err) } p := unstable.Parser{} p.Reset(b) dec := decoder{ p: &p, strict: strict{ Enabled: d.strict, }, } return dec.FromParser(v) } type decoder struct { // Which parser instance in use for this decoding session. p *unstable.Parser // Flag indicating that the current expression is stashed. // If set to true, calling nextExpr will not actually pull a new expression // but turn off the flag instead. stashedExpr bool // Skip expressions until a table is found. This is set to true when a // table could not be created (missing field in map), so all KV expressions // need to be skipped. skipUntilTable bool // Tracks position in Go arrays. // This is used when decoding [[array tables]] into Go arrays. Given array // tables are separate TOML expression, we need to keep track of where we // are at in the Go array, as we can't just introspect its size. arrayIndexes map[reflect.Value]int // Tracks keys that have been seen, with which type. seen tracker.SeenTracker // Strict mode strict strict // Current context for the error. errorContext *errorContext } type errorContext struct { Struct reflect.Type Field []int } func (d *decoder) typeMismatchError(toml string, target reflect.Type) error { return fmt.Errorf("toml: %s", d.typeMismatchString(toml, target)) } func (d *decoder) typeMismatchString(toml string, target reflect.Type) string { if d.errorContext != nil && d.errorContext.Struct != nil { ctx := d.errorContext f := ctx.Struct.FieldByIndex(ctx.Field) return fmt.Sprintf("cannot decode TOML %s into struct field %s.%s of type %s", toml, ctx.Struct, f.Name, f.Type) } return fmt.Sprintf("cannot decode TOML %s into a Go value of type %s", toml, target) } func (d *decoder) expr() *unstable.Node { return d.p.Expression() } func (d *decoder) nextExpr() bool { if d.stashedExpr { d.stashedExpr = false return true } return d.p.NextExpression() } func (d *decoder) stashExpr() { d.stashedExpr = true } func (d *decoder) arrayIndex(shouldAppend bool, v reflect.Value) int { if d.arrayIndexes == nil { d.arrayIndexes = make(map[reflect.Value]int, 1) } idx, ok := d.arrayIndexes[v] if !ok { d.arrayIndexes[v] = 0 } else if shouldAppend { idx++ d.arrayIndexes[v] = idx } return idx } func (d *decoder) FromParser(v interface{}) error { r := reflect.ValueOf(v) if r.Kind() != reflect.Ptr { return fmt.Errorf("toml: decoding can only be performed into a pointer, not %s", r.Kind()) } if r.IsNil() { return fmt.Errorf("toml: decoding pointer target cannot be nil") } r = r.Elem() if r.Kind() == reflect.Interface && r.IsNil() { newMap := map[string]interface{}{} r.Set(reflect.ValueOf(newMap)) } err := d.fromParser(r) if err == nil { return d.strict.Error(d.p.Data()) } var e *unstable.ParserError if errors.As(err, &e) { return wrapDecodeError(d.p.Data(), e) } return err } func (d *decoder) fromParser(root reflect.Value) error { for d.nextExpr() { err := d.handleRootExpression(d.expr(), root) if err != nil { return err } } return d.p.Error() } /* Rules for the unmarshal code: - The stack is used to keep track of which values need to be set where. - handle* functions <=> switch on a given unstable.Kind. - unmarshalX* functions need to unmarshal a node of kind X. - An "object" is either a struct or a map. */ func (d *decoder) handleRootExpression(expr *unstable.Node, v reflect.Value) error { var x reflect.Value var err error if !(d.skipUntilTable && expr.Kind == unstable.KeyValue) { err = d.seen.CheckExpression(expr) if err != nil { return err } } switch expr.Kind { case unstable.KeyValue: if d.skipUntilTable { return nil } x, err = d.handleKeyValue(expr, v) case unstable.Table: d.skipUntilTable = false d.strict.EnterTable(expr) x, err = d.handleTable(expr.Key(), v) case unstable.ArrayTable: d.skipUntilTable = false d.strict.EnterArrayTable(expr) x, err = d.handleArrayTable(expr.Key(), v) default: panic(fmt.Errorf("parser should not permit expression of kind %s at document root", expr.Kind)) } if d.skipUntilTable { if expr.Kind == unstable.Table || expr.Kind == unstable.ArrayTable { d.strict.MissingTable(expr) } } else if err == nil && x.IsValid() { v.Set(x) } return err } func (d *decoder) handleArrayTable(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { if key.Next() { return d.handleArrayTablePart(key, v) } return d.handleKeyValues(v) } func (d *decoder) handleArrayTableCollectionLast(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { switch v.Kind() { case reflect.Interface: elem := v.Elem() if !elem.IsValid() { elem = reflect.New(sliceInterfaceType).Elem() elem.Set(reflect.MakeSlice(sliceInterfaceType, 0, 16)) } else if elem.Kind() == reflect.Slice { if elem.Type() != sliceInterfaceType { elem = reflect.New(sliceInterfaceType).Elem() elem.Set(reflect.MakeSlice(sliceInterfaceType, 0, 16)) } else if !elem.CanSet() { nelem := reflect.New(sliceInterfaceType).Elem() nelem.Set(reflect.MakeSlice(sliceInterfaceType, elem.Len(), elem.Cap())) reflect.Copy(nelem, elem) elem = nelem } } return d.handleArrayTableCollectionLast(key, elem) case reflect.Ptr: elem := v.Elem() if !elem.IsValid() { ptr := reflect.New(v.Type().Elem()) v.Set(ptr) elem = ptr.Elem() } elem, err := d.handleArrayTableCollectionLast(key, elem) if err != nil { return reflect.Value{}, err } v.Elem().Set(elem) return v, nil case reflect.Slice: elemType := v.Type().Elem() var elem reflect.Value if elemType.Kind() == reflect.Interface { elem = makeMapStringInterface() } else { elem = reflect.New(elemType).Elem() } elem2, err := d.handleArrayTable(key, elem) if err != nil { return reflect.Value{}, err } if elem2.IsValid() { elem = elem2 } return reflect.Append(v, elem), nil case reflect.Array: idx := d.arrayIndex(true, v) if idx >= v.Len() { return v, fmt.Errorf("%s at position %d", d.typeMismatchError("array table", v.Type()), idx) } elem := v.Index(idx) _, err := d.handleArrayTable(key, elem) return v, err default: return reflect.Value{}, d.typeMismatchError("array table", v.Type()) } } // When parsing an array table expression, each part of the key needs to be // evaluated like a normal key, but if it returns a collection, it also needs to // point to the last element of the collection. Unless it is the last part of // the key, then it needs to create a new element at the end. func (d *decoder) handleArrayTableCollection(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { if key.IsLast() { return d.handleArrayTableCollectionLast(key, v) } switch v.Kind() { case reflect.Ptr: elem := v.Elem() if !elem.IsValid() { ptr := reflect.New(v.Type().Elem()) v.Set(ptr) elem = ptr.Elem() } elem, err := d.handleArrayTableCollection(key, elem) if err != nil { return reflect.Value{}, err } if elem.IsValid() { v.Elem().Set(elem) } return v, nil case reflect.Slice: elem := v.Index(v.Len() - 1) x, err := d.handleArrayTable(key, elem) if err != nil || d.skipUntilTable { return reflect.Value{}, err } if x.IsValid() { elem.Set(x) } return v, err case reflect.Array: idx := d.arrayIndex(false, v) if idx >= v.Len() { return v, fmt.Errorf("%s at position %d", d.typeMismatchError("array table", v.Type()), idx) } elem := v.Index(idx) _, err := d.handleArrayTable(key, elem) return v, err } return d.handleArrayTable(key, v) } func (d *decoder) handleKeyPart(key unstable.Iterator, v reflect.Value, nextFn handlerFn, makeFn valueMakerFn) (reflect.Value, error) { var rv reflect.Value // First, dispatch over v to make sure it is a valid object. // There is no guarantee over what it could be. switch v.Kind() { case reflect.Ptr: elem := v.Elem() if !elem.IsValid() { v.Set(reflect.New(v.Type().Elem())) } elem = v.Elem() return d.handleKeyPart(key, elem, nextFn, makeFn) case reflect.Map: vt := v.Type() // Create the key for the map element. Convert to key type. mk, err := d.keyFromData(vt.Key(), key.Node().Data) if err != nil { return reflect.Value{}, err } // If the map does not exist, create it. if v.IsNil() { vt := v.Type() v = reflect.MakeMap(vt) rv = v } mv := v.MapIndex(mk) set := false if !mv.IsValid() { // If there is no value in the map, create a new one according to // the map type. If the element type is interface, create either a // map[string]interface{} or a []interface{} depending on whether // this is the last part of the array table key. t := vt.Elem() if t.Kind() == reflect.Interface { mv = makeFn() } else { mv = reflect.New(t).Elem() } set = true } else if mv.Kind() == reflect.Interface { mv = mv.Elem() if !mv.IsValid() { mv = makeFn() } set = true } else if !mv.CanAddr() { vt := v.Type() t := vt.Elem() oldmv := mv mv = reflect.New(t).Elem() mv.Set(oldmv) set = true } x, err := nextFn(key, mv) if err != nil { return reflect.Value{}, err } if x.IsValid() { mv = x set = true } if set { v.SetMapIndex(mk, mv) } case reflect.Struct: path, found := structFieldPath(v, string(key.Node().Data)) if !found { d.skipUntilTable = true return reflect.Value{}, nil } if d.errorContext == nil { d.errorContext = new(errorContext) } t := v.Type() d.errorContext.Struct = t d.errorContext.Field = path f := fieldByIndex(v, path) x, err := nextFn(key, f) if err != nil || d.skipUntilTable { return reflect.Value{}, err } if x.IsValid() { f.Set(x) } d.errorContext.Field = nil d.errorContext.Struct = nil case reflect.Interface: if v.Elem().IsValid() { v = v.Elem() } else { v = makeMapStringInterface() } x, err := d.handleKeyPart(key, v, nextFn, makeFn) if err != nil { return reflect.Value{}, err } if x.IsValid() { v = x } rv = v default: panic(fmt.Errorf("unhandled part: %s", v.Kind())) } return rv, nil } // HandleArrayTablePart navigates the Go structure v using the key v. It is // only used for the prefix (non-last) parts of an array-table. When // encountering a collection, it should go to the last element. func (d *decoder) handleArrayTablePart(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { var makeFn valueMakerFn if key.IsLast() { makeFn = makeSliceInterface } else { makeFn = makeMapStringInterface } return d.handleKeyPart(key, v, d.handleArrayTableCollection, makeFn) } // HandleTable returns a reference when it has checked the next expression but // cannot handle it. func (d *decoder) handleTable(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { if v.Kind() == reflect.Slice { if v.Len() == 0 { return reflect.Value{}, unstable.NewParserError(key.Node().Data, "cannot store a table in a slice") } elem := v.Index(v.Len() - 1) x, err := d.handleTable(key, elem) if err != nil { return reflect.Value{}, err } if x.IsValid() { elem.Set(x) } return reflect.Value{}, nil } if key.Next() { // Still scoping the key return d.handleTablePart(key, v) } // Done scoping the key. // Now handle all the key-value expressions in this table. return d.handleKeyValues(v) } // Handle root expressions until the end of the document or the next // non-key-value. func (d *decoder) handleKeyValues(v reflect.Value) (reflect.Value, error) { var rv reflect.Value for d.nextExpr() { expr := d.expr() if expr.Kind != unstable.KeyValue { // Stash the expression so that fromParser can just loop and use // the right handler. // We could just recurse ourselves here, but at least this gives a // chance to pop the stack a bit. d.stashExpr() break } err := d.seen.CheckExpression(expr) if err != nil { return reflect.Value{}, err } x, err := d.handleKeyValue(expr, v) if err != nil { return reflect.Value{}, err } if x.IsValid() { v = x rv = x } } return rv, nil } type ( handlerFn func(key unstable.Iterator, v reflect.Value) (reflect.Value, error) valueMakerFn func() reflect.Value ) func makeMapStringInterface() reflect.Value { return reflect.MakeMap(mapStringInterfaceType) } func makeSliceInterface() reflect.Value { return reflect.MakeSlice(sliceInterfaceType, 0, 16) } func (d *decoder) handleTablePart(key unstable.Iterator, v reflect.Value) (reflect.Value, error) { return d.handleKeyPart(key, v, d.handleTable, makeMapStringInterface) } func (d *decoder) tryTextUnmarshaler(node *unstable.Node, v reflect.Value) (bool, error) { // Special case for time, because we allow to unmarshal to it from // different kind of AST nodes. if v.Type() == timeType { return false, nil } if v.CanAddr() && v.Addr().Type().Implements(textUnmarshalerType) { err := v.Addr().Interface().(encoding.TextUnmarshaler).UnmarshalText(node.Data) if err != nil { return false, unstable.NewParserError(d.p.Raw(node.Raw), "%w", err) } return true, nil } return false, nil } func (d *decoder) handleValue(value *unstable.Node, v reflect.Value) error { for v.Kind() == reflect.Ptr { v = initAndDereferencePointer(v) } ok, err := d.tryTextUnmarshaler(value, v) if ok || err != nil { return err } switch value.Kind { case unstable.String: return d.unmarshalString(value, v) case unstable.Integer: return d.unmarshalInteger(value, v) case unstable.Float: return d.unmarshalFloat(value, v) case unstable.Bool: return d.unmarshalBool(value, v) case unstable.DateTime: return d.unmarshalDateTime(value, v) case unstable.LocalDate: return d.unmarshalLocalDate(value, v) case unstable.LocalTime: return d.unmarshalLocalTime(value, v) case unstable.LocalDateTime: return d.unmarshalLocalDateTime(value, v) case unstable.InlineTable: return d.unmarshalInlineTable(value, v) case unstable.Array: return d.unmarshalArray(value, v) default: panic(fmt.Errorf("handleValue not implemented for %s", value.Kind)) } } func (d *decoder) unmarshalArray(array *unstable.Node, v reflect.Value) error { switch v.Kind() { case reflect.Slice: if v.IsNil() { v.Set(reflect.MakeSlice(v.Type(), 0, 16)) } else { v.SetLen(0) } case reflect.Array: // arrays are always initialized case reflect.Interface: elem := v.Elem() if !elem.IsValid() { elem = reflect.New(sliceInterfaceType).Elem() elem.Set(reflect.MakeSlice(sliceInterfaceType, 0, 16)) } else if elem.Kind() == reflect.Slice { if elem.Type() != sliceInterfaceType { elem = reflect.New(sliceInterfaceType).Elem() elem.Set(reflect.MakeSlice(sliceInterfaceType, 0, 16)) } else if !elem.CanSet() { nelem := reflect.New(sliceInterfaceType).Elem() nelem.Set(reflect.MakeSlice(sliceInterfaceType, elem.Len(), elem.Cap())) reflect.Copy(nelem, elem) elem = nelem } } err := d.unmarshalArray(array, elem) if err != nil { return err } v.Set(elem) return nil default: // TODO: use newDecodeError, but first the parser needs to fill // array.Data. return d.typeMismatchError("array", v.Type()) } elemType := v.Type().Elem() it := array.Children() idx := 0 for it.Next() { n := it.Node() // TODO: optimize if v.Kind() == reflect.Slice { elem := reflect.New(elemType).Elem() err := d.handleValue(n, elem) if err != nil { return err } v.Set(reflect.Append(v, elem)) } else { // array if idx >= v.Len() { return nil } elem := v.Index(idx) err := d.handleValue(n, elem) if err != nil { return err } idx++ } } return nil } func (d *decoder) unmarshalInlineTable(itable *unstable.Node, v reflect.Value) error { // Make sure v is an initialized object. switch v.Kind() { case reflect.Map: if v.IsNil() { v.Set(reflect.MakeMap(v.Type())) } case reflect.Struct: // structs are always initialized. case reflect.Interface: elem := v.Elem() if !elem.IsValid() { elem = makeMapStringInterface() v.Set(elem) } return d.unmarshalInlineTable(itable, elem) default: return unstable.NewParserError(d.p.Raw(itable.Raw), "cannot store inline table in Go type %s", v.Kind()) } it := itable.Children() for it.Next() { n := it.Node() x, err := d.handleKeyValue(n, v) if err != nil { return err } if x.IsValid() { v = x } } return nil } func (d *decoder) unmarshalDateTime(value *unstable.Node, v reflect.Value) error { dt, err := parseDateTime(value.Data) if err != nil { return err } v.Set(reflect.ValueOf(dt)) return nil } func (d *decoder) unmarshalLocalDate(value *unstable.Node, v reflect.Value) error { ld, err := parseLocalDate(value.Data) if err != nil { return err } if v.Type() == timeType { cast := ld.AsTime(time.Local) v.Set(reflect.ValueOf(cast)) return nil } v.Set(reflect.ValueOf(ld)) return nil } func (d *decoder) unmarshalLocalTime(value *unstable.Node, v reflect.Value) error { lt, rest, err := parseLocalTime(value.Data) if err != nil { return err } if len(rest) > 0 { return unstable.NewParserError(rest, "extra characters at the end of a local time") } v.Set(reflect.ValueOf(lt)) return nil } func (d *decoder) unmarshalLocalDateTime(value *unstable.Node, v reflect.Value) error { ldt, rest, err := parseLocalDateTime(value.Data) if err != nil { return err } if len(rest) > 0 { return unstable.NewParserError(rest, "extra characters at the end of a local date time") } if v.Type() == timeType { cast := ldt.AsTime(time.Local) v.Set(reflect.ValueOf(cast)) return nil } v.Set(reflect.ValueOf(ldt)) return nil } func (d *decoder) unmarshalBool(value *unstable.Node, v reflect.Value) error { b := value.Data[0] == 't' switch v.Kind() { case reflect.Bool: v.SetBool(b) case reflect.Interface: v.Set(reflect.ValueOf(b)) default: return unstable.NewParserError(value.Data, "cannot assign boolean to a %t", b) } return nil } func (d *decoder) unmarshalFloat(value *unstable.Node, v reflect.Value) error { f, err := parseFloat(value.Data) if err != nil { return err } switch v.Kind() { case reflect.Float64: v.SetFloat(f) case reflect.Float32: if f > math.MaxFloat32 { return unstable.NewParserError(value.Data, "number %f does not fit in a float32", f) } v.SetFloat(f) case reflect.Interface: v.Set(reflect.ValueOf(f)) default: return unstable.NewParserError(value.Data, "float cannot be assigned to %s", v.Kind()) } return nil } const ( maxInt = int64(^uint(0) >> 1) minInt = -maxInt - 1 ) // Maximum value of uint for decoding. Currently the decoder parses the integer // into an int64. As a result, on architectures where uint is 64 bits, the // effective maximum uint we can decode is the maximum of int64. On // architectures where uint is 32 bits, the maximum value we can decode is // lower: the maximum of uint32. I didn't find a way to figure out this value at // compile time, so it is computed during initialization. var maxUint int64 = math.MaxInt64 func init() { m := uint64(^uint(0)) if m < uint64(maxUint) { maxUint = int64(m) } } func (d *decoder) unmarshalInteger(value *unstable.Node, v reflect.Value) error { kind := v.Kind() if kind == reflect.Float32 || kind == reflect.Float64 { return d.unmarshalFloat(value, v) } i, err := parseInteger(value.Data) if err != nil { return err } var r reflect.Value switch kind { case reflect.Int64: v.SetInt(i) return nil case reflect.Int32: if i < math.MinInt32 || i > math.MaxInt32 { return fmt.Errorf("toml: number %d does not fit in an int32", i) } r = reflect.ValueOf(int32(i)) case reflect.Int16: if i < math.MinInt16 || i > math.MaxInt16 { return fmt.Errorf("toml: number %d does not fit in an int16", i) } r = reflect.ValueOf(int16(i)) case reflect.Int8: if i < math.MinInt8 || i > math.MaxInt8 { return fmt.Errorf("toml: number %d does not fit in an int8", i) } r = reflect.ValueOf(int8(i)) case reflect.Int: if i < minInt || i > maxInt { return fmt.Errorf("toml: number %d does not fit in an int", i) } r = reflect.ValueOf(int(i)) case reflect.Uint64: if i < 0 { return fmt.Errorf("toml: negative number %d does not fit in an uint64", i) } r = reflect.ValueOf(uint64(i)) case reflect.Uint32: if i < 0 || i > math.MaxUint32 { return fmt.Errorf("toml: negative number %d does not fit in an uint32", i) } r = reflect.ValueOf(uint32(i)) case reflect.Uint16: if i < 0 || i > math.MaxUint16 { return fmt.Errorf("toml: negative number %d does not fit in an uint16", i) } r = reflect.ValueOf(uint16(i)) case reflect.Uint8: if i < 0 || i > math.MaxUint8 { return fmt.Errorf("toml: negative number %d does not fit in an uint8", i) } r = reflect.ValueOf(uint8(i)) case reflect.Uint: if i < 0 || i > maxUint { return fmt.Errorf("toml: negative number %d does not fit in an uint", i) } r = reflect.ValueOf(uint(i)) case reflect.Interface: r = reflect.ValueOf(i) default: return unstable.NewParserError(d.p.Raw(value.Raw), d.typeMismatchString("integer", v.Type())) } if !r.Type().AssignableTo(v.Type()) { r = r.Convert(v.Type()) } v.Set(r) return nil } func (d *decoder) unmarshalString(value *unstable.Node, v reflect.Value) error { switch v.Kind() { case reflect.String: v.SetString(string(value.Data)) case reflect.Interface: v.Set(reflect.ValueOf(string(value.Data))) default: return unstable.NewParserError(d.p.Raw(value.Raw), d.typeMismatchString("string", v.Type())) } return nil } func (d *decoder) handleKeyValue(expr *unstable.Node, v reflect.Value) (reflect.Value, error) { d.strict.EnterKeyValue(expr) v, err := d.handleKeyValueInner(expr.Key(), expr.Value(), v) if d.skipUntilTable { d.strict.MissingField(expr) d.skipUntilTable = false } d.strict.ExitKeyValue(expr) return v, err } func (d *decoder) handleKeyValueInner(key unstable.Iterator, value *unstable.Node, v reflect.Value) (reflect.Value, error) { if key.Next() { // Still scoping the key return d.handleKeyValuePart(key, value, v) } // Done scoping the key. // v is whatever Go value we need to fill. return reflect.Value{}, d.handleValue(value, v) } func (d *decoder) keyFromData(keyType reflect.Type, data []byte) (reflect.Value, error) { switch { case stringType.AssignableTo(keyType): return reflect.ValueOf(string(data)), nil case stringType.ConvertibleTo(keyType): return reflect.ValueOf(string(data)).Convert(keyType), nil case keyType.Implements(textUnmarshalerType): mk := reflect.New(keyType.Elem()) if err := mk.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil { return reflect.Value{}, fmt.Errorf("toml: error unmarshalling key type %s from text: %w", stringType, err) } return mk, nil case reflect.PtrTo(keyType).Implements(textUnmarshalerType): mk := reflect.New(keyType) if err := mk.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil { return reflect.Value{}, fmt.Errorf("toml: error unmarshalling key type %s from text: %w", stringType, err) } return mk.Elem(), nil } return reflect.Value{}, fmt.Errorf("toml: cannot convert map key of type %s to expected type %s", stringType, keyType) } func (d *decoder) handleKeyValuePart(key unstable.Iterator, value *unstable.Node, v reflect.Value) (reflect.Value, error) { // contains the replacement for v var rv reflect.Value // First, dispatch over v to make sure it is a valid object. // There is no guarantee over what it could be. switch v.Kind() { case reflect.Map: vt := v.Type() mk, err := d.keyFromData(vt.Key(), key.Node().Data) if err != nil { return reflect.Value{}, err } // If the map does not exist, create it. if v.IsNil() { v = reflect.MakeMap(vt) rv = v } mv := v.MapIndex(mk) set := false if !mv.IsValid() || key.IsLast() { set = true mv = reflect.New(v.Type().Elem()).Elem() } nv, err := d.handleKeyValueInner(key, value, mv) if err != nil { return reflect.Value{}, err } if nv.IsValid() { mv = nv set = true } if set { v.SetMapIndex(mk, mv) } case reflect.Struct: path, found := structFieldPath(v, string(key.Node().Data)) if !found { d.skipUntilTable = true break } if d.errorContext == nil { d.errorContext = new(errorContext) } t := v.Type() d.errorContext.Struct = t d.errorContext.Field = path f := fieldByIndex(v, path) if !f.CanAddr() { // If the field is not addressable, need to take a slower path and // make a copy of the struct itself to a new location. nvp := reflect.New(v.Type()) nvp.Elem().Set(v) v = nvp.Elem() _, err := d.handleKeyValuePart(key, value, v) if err != nil { return reflect.Value{}, err } return nvp.Elem(), nil } x, err := d.handleKeyValueInner(key, value, f) if err != nil { return reflect.Value{}, err } if x.IsValid() { f.Set(x) } d.errorContext.Struct = nil d.errorContext.Field = nil case reflect.Interface: v = v.Elem() // Following encoding/json: decoding an object into an // interface{}, it needs to always hold a // map[string]interface{}. This is for the types to be // consistent whether a previous value was set or not. if !v.IsValid() || v.Type() != mapStringInterfaceType { v = makeMapStringInterface() } x, err := d.handleKeyValuePart(key, value, v) if err != nil { return reflect.Value{}, err } if x.IsValid() { v = x } rv = v case reflect.Ptr: elem := v.Elem() if !elem.IsValid() { ptr := reflect.New(v.Type().Elem()) v.Set(ptr) rv = v elem = ptr.Elem() } elem2, err := d.handleKeyValuePart(key, value, elem) if err != nil { return reflect.Value{}, err } if elem2.IsValid() { elem = elem2 } v.Elem().Set(elem) default: return reflect.Value{}, fmt.Errorf("unhandled kv part: %s", v.Kind()) } return rv, nil } func initAndDereferencePointer(v reflect.Value) reflect.Value { var elem reflect.Value if v.IsNil() { ptr := reflect.New(v.Type().Elem()) v.Set(ptr) } elem = v.Elem() return elem } // Same as reflect.Value.FieldByIndex, but creates pointers if needed. func fieldByIndex(v reflect.Value, path []int) reflect.Value { for _, x := range path { v = v.Field(x) if v.Kind() == reflect.Ptr { if v.IsNil() { v.Set(reflect.New(v.Type().Elem())) } v = v.Elem() } } return v } type fieldPathsMap = map[string][]int var globalFieldPathsCache atomic.Value // map[danger.TypeID]fieldPathsMap func structFieldPath(v reflect.Value, name string) ([]int, bool) { t := v.Type() cache, _ := globalFieldPathsCache.Load().(map[danger.TypeID]fieldPathsMap) fieldPaths, ok := cache[danger.MakeTypeID(t)] if !ok { fieldPaths = map[string][]int{} forEachField(t, nil, func(name string, path []int) { fieldPaths[name] = path // extra copy for the case-insensitive match fieldPaths[strings.ToLower(name)] = path }) newCache := make(map[danger.TypeID]fieldPathsMap, len(cache)+1) newCache[danger.MakeTypeID(t)] = fieldPaths for k, v := range cache { newCache[k] = v } globalFieldPathsCache.Store(newCache) } path, ok := fieldPaths[name] if !ok { path, ok = fieldPaths[strings.ToLower(name)] } return path, ok } func forEachField(t reflect.Type, path []int, do func(name string, path []int)) { n := t.NumField() for i := 0; i < n; i++ { f := t.Field(i) if !f.Anonymous && f.PkgPath != "" { // only consider exported fields. continue } fieldPath := append(path, i) fieldPath = fieldPath[:len(fieldPath):len(fieldPath)] name := f.Tag.Get("toml") if name == "-" { continue } if i := strings.IndexByte(name, ','); i >= 0 { name = name[:i] } if f.Anonymous && name == "" { t2 := f.Type if t2.Kind() == reflect.Ptr { t2 = t2.Elem() } if t2.Kind() == reflect.Struct { forEachField(t2, fieldPath, do) } continue } if name == "" { name = f.Name } do(name, fieldPath) } } go-toml-2.1.1/unmarshaler_test.go000066400000000000000000002152711453566013500170200ustar00rootroot00000000000000package toml_test import ( "bytes" "encoding/json" "errors" "fmt" "math" "strconv" "strings" "testing" "time" "github.com/pelletier/go-toml/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) type unmarshalTextKey struct { A string B string } func (k *unmarshalTextKey) UnmarshalText(text []byte) error { parts := strings.Split(string(text), "-") if len(parts) != 2 { return fmt.Errorf("invalid text key: %s", text) } k.A = parts[0] k.B = parts[1] return nil } type unmarshalBadTextKey struct{} func (k *unmarshalBadTextKey) UnmarshalText(text []byte) error { return fmt.Errorf("error") } func ExampleDecoder_DisallowUnknownFields() { type S struct { Key1 string Key3 string } doc := ` key1 = "value1" key2 = "value2" key3 = "value3" ` r := strings.NewReader(doc) d := toml.NewDecoder(r) d.DisallowUnknownFields() s := S{} err := d.Decode(&s) fmt.Println(err.Error()) var details *toml.StrictMissingError if !errors.As(err, &details) { panic(fmt.Sprintf("err should have been a *toml.StrictMissingError, but got %s (%T)", err, err)) } fmt.Println(details.String()) // Output: // strict mode: fields in the document are missing in the target struct // 2| key1 = "value1" // 3| key2 = "value2" // | ~~~~ missing field // 4| key3 = "value3" } func ExampleUnmarshal() { type MyConfig struct { Version int Name string Tags []string } doc := ` version = 2 name = "go-toml" tags = ["go", "toml"] ` var cfg MyConfig err := toml.Unmarshal([]byte(doc), &cfg) if err != nil { panic(err) } fmt.Println("version:", cfg.Version) fmt.Println("name:", cfg.Name) fmt.Println("tags:", cfg.Tags) // Output: // version: 2 // name: go-toml // tags: [go toml] } type badReader struct{} func (r *badReader) Read([]byte) (int, error) { return 0, fmt.Errorf("testing error") } func TestDecodeReaderError(t *testing.T) { r := &badReader{} dec := toml.NewDecoder(r) m := map[string]interface{}{} err := dec.Decode(&m) require.Error(t, err) } // nolint:funlen func TestUnmarshal_Integers(t *testing.T) { examples := []struct { desc string input string expected int64 err bool }{ { desc: "integer just digits", input: `1234`, expected: 1234, }, { desc: "integer zero", input: `0`, expected: 0, }, { desc: "integer sign", input: `+99`, expected: 99, }, { desc: "integer decimal underscore", input: `123_456`, expected: 123456, }, { desc: "integer hex uppercase", input: `0xDEADBEEF`, expected: 0xDEADBEEF, }, { desc: "integer hex lowercase", input: `0xdead_beef`, expected: 0xDEADBEEF, }, { desc: "integer octal", input: `0o01234567`, expected: 0o01234567, }, { desc: "integer binary", input: `0b11010110`, expected: 0b11010110, }, { desc: "double underscore", input: "12__3", err: true, }, { desc: "starts with underscore", input: "_1", err: true, }, { desc: "ends with underscore", input: "1_", err: true, }, } type doc struct { A int64 } for _, e := range examples { e := e t.Run(e.desc, func(t *testing.T) { doc := doc{} err := toml.Unmarshal([]byte(`A = `+e.input), &doc) if e.err { require.Error(t, err) } else { require.NoError(t, err) assert.Equal(t, e.expected, doc.A) } }) } } //nolint:funlen func TestUnmarshal_Floats(t *testing.T) { examples := []struct { desc string input string expected float64 testFn func(t *testing.T, v float64) err bool }{ { desc: "float pi", input: `3.1415`, expected: 3.1415, }, { desc: "float negative", input: `-0.01`, expected: -0.01, }, { desc: "float signed exponent", input: `5e+22`, expected: 5e+22, }, { desc: "float exponent lowercase", input: `1e06`, expected: 1e06, }, { desc: "float exponent uppercase", input: `-2E-2`, expected: -2e-2, }, { desc: "float exponent zero", input: `0e0`, expected: 0.0, }, { desc: "float upper exponent zero", input: `0E0`, expected: 0.0, }, { desc: "float zero without decimals", input: `0`, expected: 0.0, }, { desc: "float fractional with exponent", input: `6.626e-34`, expected: 6.626e-34, }, { desc: "float underscores", input: `224_617.445_991_228`, expected: 224_617.445_991_228, }, { desc: "inf", input: `inf`, expected: math.Inf(+1), }, { desc: "inf negative", input: `-inf`, expected: math.Inf(-1), }, { desc: "inf positive", input: `+inf`, expected: math.Inf(+1), }, { desc: "nan", input: `nan`, testFn: func(t *testing.T, v float64) { t.Helper() assert.True(t, math.IsNaN(v)) }, }, { desc: "nan negative", input: `-nan`, testFn: func(t *testing.T, v float64) { t.Helper() assert.True(t, math.IsNaN(v)) }, }, { desc: "nan positive", input: `+nan`, testFn: func(t *testing.T, v float64) { t.Helper() assert.True(t, math.IsNaN(v)) }, }, { desc: "underscore after integer part", input: `1_e2`, err: true, }, { desc: "underscore after integer part", input: `1.0_e2`, err: true, }, { desc: "leading zero in positive float", input: `+0_0.0`, err: true, }, } type doc struct { A float64 } for _, e := range examples { e := e t.Run(e.desc, func(t *testing.T) { doc := doc{} err := toml.Unmarshal([]byte(`A = `+e.input), &doc) if e.err { require.Error(t, err) } else { require.NoError(t, err) if e.testFn != nil { e.testFn(t, doc.A) } else { assert.Equal(t, e.expected, doc.A) } } }) } } //nolint:funlen func TestUnmarshal(t *testing.T) { type test struct { target interface{} expected interface{} err bool assert func(t *testing.T, test test) } examples := []struct { skip bool desc string input string gen func() test }{ { desc: "kv string", input: `A = "foo"`, gen: func() test { type doc struct { A string } return test{ target: &doc{}, expected: &doc{A: "foo"}, } }, }, { desc: "kv literal string", input: `A = 'foo 🙂 '`, gen: func() test { type doc struct { A string } return test{ target: &doc{}, expected: &doc{A: "foo 🙂 "}, } }, }, { desc: "kv text key", input: `a-1 = "foo"`, gen: func() test { type doc = map[unmarshalTextKey]string return test{ target: &doc{}, expected: &doc{{A: "a", B: "1"}: "foo"}, } }, }, { desc: "table text key", input: `["a-1"] foo = "bar"`, gen: func() test { type doc = map[unmarshalTextKey]map[string]string return test{ target: &doc{}, expected: &doc{{A: "a", B: "1"}: map[string]string{"foo": "bar"}}, } }, }, { desc: "kv ptr text key", input: `a-1 = "foo"`, gen: func() test { type doc = map[*unmarshalTextKey]string return test{ target: &doc{}, expected: &doc{{A: "a", B: "1"}: "foo"}, assert: func(t *testing.T, test test) { // Despite the documentation: // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). // assert.Equal does not work properly with maps with pointer keys // https://github.com/stretchr/testify/issues/1143 expected := make(map[unmarshalTextKey]string) for k, v := range *(test.expected.(*doc)) { expected[*k] = v } got := make(map[unmarshalTextKey]string) for k, v := range *(test.target.(*doc)) { got[*k] = v } assert.Equal(t, expected, got) }, } }, }, { desc: "kv bad text key", input: `a-1 = "foo"`, gen: func() test { type doc = map[unmarshalBadTextKey]string return test{ target: &doc{}, err: true, } }, }, { desc: "kv bad ptr text key", input: `a-1 = "foo"`, gen: func() test { type doc = map[*unmarshalBadTextKey]string return test{ target: &doc{}, err: true, } }, }, { desc: "table bad text key", input: `["a-1"] foo = "bar"`, gen: func() test { type doc = map[unmarshalBadTextKey]map[string]string return test{ target: &doc{}, err: true, } }, }, { desc: "time.time with negative zone", input: `a = 1979-05-27T00:32:00-07:00 `, // space intentional gen: func() test { var v map[string]time.Time return test{ target: &v, expected: &map[string]time.Time{ "a": time.Date(1979, 5, 27, 0, 32, 0, 0, time.FixedZone("", -7*3600)), }, } }, }, { desc: "time.time with positive zone", input: `a = 1979-05-27T00:32:00+07:00`, gen: func() test { var v map[string]time.Time return test{ target: &v, expected: &map[string]time.Time{ "a": time.Date(1979, 5, 27, 0, 32, 0, 0, time.FixedZone("", 7*3600)), }, } }, }, { desc: "time.time with zone and fractional", input: `a = 1979-05-27T00:32:00.999999-07:00`, gen: func() test { var v map[string]time.Time return test{ target: &v, expected: &map[string]time.Time{ "a": time.Date(1979, 5, 27, 0, 32, 0, 999999000, time.FixedZone("", -7*3600)), }, } }, }, { desc: "local datetime into time.Time", input: `a = 1979-05-27T00:32:00`, gen: func() test { type doc struct { A time.Time } return test{ target: &doc{}, expected: &doc{ A: time.Date(1979, 5, 27, 0, 32, 0, 0, time.Local), }, } }, }, { desc: "local datetime into interface", input: `a = 1979-05-27T00:32:00`, gen: func() test { type doc struct { A interface{} } return test{ target: &doc{}, expected: &doc{ A: toml.LocalDateTime{ toml.LocalDate{1979, 5, 27}, toml.LocalTime{0, 32, 0, 0, 0}, }, }, } }, }, { desc: "local date into interface", input: `a = 1979-05-27`, gen: func() test { type doc struct { A interface{} } return test{ target: &doc{}, expected: &doc{ A: toml.LocalDate{1979, 5, 27}, }, } }, }, { desc: "local leap-day date into interface", input: `a = 2020-02-29`, gen: func() test { type doc struct { A interface{} } return test{ target: &doc{}, expected: &doc{ A: toml.LocalDate{2020, 2, 29}, }, } }, }, { desc: "local-time with nano second", input: `a = 12:08:05.666666666`, gen: func() test { var v map[string]interface{} return test{ target: &v, expected: &map[string]interface{}{ "a": toml.LocalTime{Hour: 12, Minute: 8, Second: 5, Nanosecond: 666666666, Precision: 9}, }, } }, }, { desc: "local-time", input: `a = 12:08:05`, gen: func() test { var v map[string]interface{} return test{ target: &v, expected: &map[string]interface{}{ "a": toml.LocalTime{Hour: 12, Minute: 8, Second: 5}, }, } }, }, { desc: "local-time missing digit", input: `a = 12:08:0`, gen: func() test { var v map[string]interface{} return test{ target: &v, err: true, } }, }, { desc: "local-time extra digit", input: `a = 12:08:000`, gen: func() test { var v map[string]interface{} return test{ target: &v, err: true, } }, }, { desc: "issue 475 - space between dots in key", input: `fruit. color = "yellow" fruit . flavor = "banana"`, gen: func() test { m := map[string]interface{}{} return test{ target: &m, expected: &map[string]interface{}{ "fruit": map[string]interface{}{ "color": "yellow", "flavor": "banana", }, }, } }, }, { desc: "issue 427 - quotation marks in key", input: `'"a"' = 1 "\"b\"" = 2`, gen: func() test { m := map[string]interface{}{} return test{ target: &m, expected: &map[string]interface{}{ `"a"`: int64(1), `"b"`: int64(2), }, } }, }, { desc: "issue 739 - table redefinition", input: ` [foo.bar.baz] wibble = 'wobble' [foo] [foo.bar] huey = 'dewey' `, gen: func() test { m := map[string]interface{}{} return test{ target: &m, expected: &map[string]interface{}{ `foo`: map[string]interface{}{ "bar": map[string]interface{}{ "huey": "dewey", "baz": map[string]interface{}{ "wibble": "wobble", }, }, }, }, } }, }, { desc: "multiline basic string", input: `A = """\ Test"""`, gen: func() test { type doc struct { A string } return test{ target: &doc{}, expected: &doc{A: "Test"}, } }, }, { desc: "multiline literal string with windows newline", input: "A = '''\r\nTest'''", gen: func() test { type doc struct { A string } return test{ target: &doc{}, expected: &doc{A: "Test"}, } }, }, { desc: "multiline basic string with windows newline", input: "A = \"\"\"\r\nTe\r\nst\"\"\"", gen: func() test { type doc struct { A string } return test{ target: &doc{}, expected: &doc{A: "Te\r\nst"}, } }, }, { desc: "multiline basic string escapes", input: `A = """ \\\b\f\n\r\t\uffff\U0001D11E"""`, gen: func() test { type doc struct { A string } return test{ target: &doc{}, expected: &doc{A: "\\\b\f\n\r\t\uffff\U0001D11E"}, } }, }, { desc: "basic string escapes", input: `A = "\\\b\f\n\r\t\uffff\U0001D11E"`, gen: func() test { type doc struct { A string } return test{ target: &doc{}, expected: &doc{A: "\\\b\f\n\r\t\uffff\U0001D11E"}, } }, }, { desc: "spaces around dotted keys", input: "a . b = 1", gen: func() test { return test{ target: &map[string]map[string]interface{}{}, expected: &map[string]map[string]interface{}{"a": {"b": int64(1)}}, } }, }, { desc: "kv bool true", input: `A = true`, gen: func() test { type doc struct { A bool } return test{ target: &doc{}, expected: &doc{A: true}, } }, }, { desc: "kv bool false", input: `A = false`, gen: func() test { type doc struct { A bool } return test{ target: &doc{A: true}, expected: &doc{A: false}, } }, }, { desc: "string array", input: `A = ["foo", "bar"]`, gen: func() test { type doc struct { A []string } return test{ target: &doc{}, expected: &doc{A: []string{"foo", "bar"}}, } }, }, { desc: "long string array into []string", input: `A = ["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17"]`, gen: func() test { type doc struct { A []string } return test{ target: &doc{}, expected: &doc{A: []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17"}}, } }, }, { desc: "long string array into []interface{}", input: `A = ["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14", "15","16","17"]`, gen: func() test { type doc struct { A []interface{} } return test{ target: &doc{}, expected: &doc{A: []interface{}{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17"}}, } }, }, { desc: "standard table", input: `[A] B = "data"`, gen: func() test { type A struct { B string } type doc struct { A A } return test{ target: &doc{}, expected: &doc{A: A{B: "data"}}, } }, }, { desc: "standard empty table", input: `[A]`, gen: func() test { var v map[string]interface{} return test{ target: &v, expected: &map[string]interface{}{`A`: map[string]interface{}{}}, } }, }, { desc: "inline table", input: `Name = {First = "hello", Last = "world"}`, gen: func() test { type name struct { First string Last string } type doc struct { Name name } return test{ target: &doc{}, expected: &doc{Name: name{ First: "hello", Last: "world", }}, } }, }, { desc: "inline empty table", input: `A = {}`, gen: func() test { var v map[string]interface{} return test{ target: &v, expected: &map[string]interface{}{`A`: map[string]interface{}{}}, } }, }, { desc: "inline table inside array", input: `Names = [{First = "hello", Last = "world"}, {First = "ab", Last = "cd"}]`, gen: func() test { type name struct { First string Last string } type doc struct { Names []name } return test{ target: &doc{}, expected: &doc{ Names: []name{ { First: "hello", Last: "world", }, { First: "ab", Last: "cd", }, }, }, } }, }, { desc: "into map[string]interface{}", input: `A = "foo"`, gen: func() test { doc := map[string]interface{}{} return test{ target: &doc, expected: &map[string]interface{}{ "A": "foo", }, } }, }, { desc: "multi keys of different types into map[string]interface{}", input: `A = "foo" B = 42`, gen: func() test { doc := map[string]interface{}{} return test{ target: &doc, expected: &map[string]interface{}{ "A": "foo", "B": int64(42), }, } }, }, { desc: "slice in a map[string]interface{}", input: `A = ["foo", "bar"]`, gen: func() test { doc := map[string]interface{}{} return test{ target: &doc, expected: &map[string]interface{}{ "A": []interface{}{"foo", "bar"}, }, } }, }, { desc: "string into map[string]string", input: `A = "foo"`, gen: func() test { doc := map[string]string{} return test{ target: &doc, expected: &map[string]string{ "A": "foo", }, } }, }, { desc: "float64 into map[string]string", input: `A = 42.0`, gen: func() test { doc := map[string]string{} return test{ target: &doc, err: true, } }, }, { desc: "one-level one-element array table", input: `[[First]] Second = "hello"`, gen: func() test { type First struct { Second string } type Doc struct { First []First } return test{ target: &Doc{}, expected: &Doc{ First: []First{ { Second: "hello", }, }, }, } }, }, { desc: "one-level multi-element array table", input: `[[Products]] Name = "Hammer" Sku = 738594937 [[Products]] # empty table within the array [[Products]] Name = "Nail" Sku = 284758393 Color = "gray"`, gen: func() test { type Product struct { Name string Sku int64 Color string } type Doc struct { Products []Product } return test{ target: &Doc{}, expected: &Doc{ Products: []Product{ {Name: "Hammer", Sku: 738594937}, {}, {Name: "Nail", Sku: 284758393, Color: "gray"}, }, }, } }, }, { desc: "one-level multi-element array table to map", input: `[[Products]] Name = "Hammer" Sku = 738594937 [[Products]] # empty table within the array [[Products]] Name = "Nail" Sku = 284758393 Color = "gray"`, gen: func() test { return test{ target: &map[string]interface{}{}, expected: &map[string]interface{}{ "Products": []interface{}{ map[string]interface{}{ "Name": "Hammer", "Sku": int64(738594937), }, map[string]interface{}{}, map[string]interface{}{ "Name": "Nail", "Sku": int64(284758393), "Color": "gray", }, }, }, } }, }, { desc: "sub-table in array table", input: `[[Fruits]] Name = "apple" [Fruits.Physical] # subtable Color = "red" Shape = "round"`, gen: func() test { return test{ target: &map[string]interface{}{}, expected: &map[string]interface{}{ "Fruits": []interface{}{ map[string]interface{}{ "Name": "apple", "Physical": map[string]interface{}{ "Color": "red", "Shape": "round", }, }, }, }, } }, }, { desc: "multiple sub-table in array tables", input: `[[Fruits]] Name = "apple" [[Fruits.Varieties]] # nested array of tables Name = "red delicious" [[Fruits.Varieties]] Name = "granny smith" [[Fruits]] Name = "banana" [[Fruits.Varieties]] Name = "plantain"`, gen: func() test { return test{ target: &map[string]interface{}{}, expected: &map[string]interface{}{ "Fruits": []interface{}{ map[string]interface{}{ "Name": "apple", "Varieties": []interface{}{ map[string]interface{}{ "Name": "red delicious", }, map[string]interface{}{ "Name": "granny smith", }, }, }, map[string]interface{}{ "Name": "banana", "Varieties": []interface{}{ map[string]interface{}{ "Name": "plantain", }, }, }, }, }, } }, }, { desc: "multiple sub-table in array tables into structs", input: `[[Fruits]] Name = "apple" [[Fruits.Varieties]] # nested array of tables Name = "red delicious" [[Fruits.Varieties]] Name = "granny smith" [[Fruits]] Name = "banana" [[Fruits.Varieties]] Name = "plantain"`, gen: func() test { type Variety struct { Name string } type Fruit struct { Name string Varieties []Variety } type doc struct { Fruits []Fruit } return test{ target: &doc{}, expected: &doc{ Fruits: []Fruit{ { Name: "apple", Varieties: []Variety{ {Name: "red delicious"}, {Name: "granny smith"}, }, }, { Name: "banana", Varieties: []Variety{ {Name: "plantain"}, }, }, }, }, } }, }, { desc: "array table into interface in struct", input: `[[foo]] bar = "hello"`, gen: func() test { type doc struct { Foo interface{} } return test{ target: &doc{}, expected: &doc{ Foo: []interface{}{ map[string]interface{}{ "bar": "hello", }, }, }, } }, }, { desc: "array table into interface in struct already initialized with right type", input: `[[foo]] bar = "hello"`, gen: func() test { type doc struct { Foo interface{} } return test{ target: &doc{ Foo: []interface{}{}, }, expected: &doc{ Foo: []interface{}{ map[string]interface{}{ "bar": "hello", }, }, }, } }, }, { desc: "array table into interface in struct already initialized with wrong type", input: `[[foo]] bar = "hello"`, gen: func() test { type doc struct { Foo interface{} } return test{ target: &doc{ Foo: []string{}, }, expected: &doc{ Foo: []interface{}{ map[string]interface{}{ "bar": "hello", }, }, }, } }, }, { desc: "array table into maps with pointer on last key", input: `[[foo]] bar = "hello"`, gen: func() test { type doc struct { Foo **[]interface{} } x := &[]interface{}{ map[string]interface{}{ "bar": "hello", }, } return test{ target: &doc{}, expected: &doc{ Foo: &x, }, } }, }, { desc: "array table into maps with pointer on intermediate key", input: `[[foo.foo2]] bar = "hello"`, gen: func() test { type doc struct { Foo **map[string]interface{} } x := &map[string]interface{}{ "foo2": []interface{}{ map[string]interface{}{ "bar": "hello", }, }, } return test{ target: &doc{}, expected: &doc{ Foo: &x, }, } }, }, { desc: "array table into maps with pointer on last key with invalid leaf type", input: `[[foo]] bar = "hello"`, gen: func() test { type doc struct { Foo **[]map[string]int } return test{ target: &doc{}, err: true, } }, }, { desc: "unexported struct fields are ignored", input: `foo = "bar"`, gen: func() test { type doc struct { foo string } return test{ target: &doc{}, expected: &doc{}, } }, }, { desc: "array table into nil ptr", input: `[[foo]] bar = "hello"`, gen: func() test { type doc struct { Foo *[]interface{} } return test{ target: &doc{}, expected: &doc{ Foo: &[]interface{}{ map[string]interface{}{ "bar": "hello", }, }, }, } }, }, { desc: "array table into nil ptr of invalid type", input: `[[foo]] bar = "hello"`, gen: func() test { type doc struct { Foo *string } return test{ target: &doc{}, err: true, } }, }, { desc: "array table with intermediate ptr", input: `[[foo.bar]] bar = "hello"`, gen: func() test { type doc struct { Foo *map[string]interface{} } return test{ target: &doc{}, expected: &doc{ Foo: &map[string]interface{}{ "bar": []interface{}{ map[string]interface{}{ "bar": "hello", }, }, }, }, } }, }, { desc: "unmarshal array into interface that contains a slice", input: `a = [1,2,3]`, gen: func() test { type doc struct { A interface{} } return test{ target: &doc{ A: []string{}, }, expected: &doc{ A: []interface{}{ int64(1), int64(2), int64(3), }, }, } }, }, { desc: "unmarshal array into interface that contains a []interface{}", input: `a = [1,2,3]`, gen: func() test { type doc struct { A interface{} } return test{ target: &doc{ A: []interface{}{}, }, expected: &doc{ A: []interface{}{ int64(1), int64(2), int64(3), }, }, } }, }, { desc: "unmarshal key into map with existing value", input: `a = "new"`, gen: func() test { return test{ target: &map[string]interface{}{"a": "old"}, expected: &map[string]interface{}{"a": "new"}, } }, }, { desc: "unmarshal key into map with existing value", input: `a.b = "new"`, gen: func() test { type doc struct { A interface{} } return test{ target: &doc{}, expected: &doc{ A: map[string]interface{}{ "b": "new", }, }, } }, }, { desc: "unmarshal array into struct field with existing array", input: `a = [1,2]`, gen: func() test { type doc struct { A []int } return test{ target: &doc{}, expected: &doc{ A: []int{1, 2}, }, } }, }, { desc: "unmarshal inline table into map", input: `a = {b="hello"}`, gen: func() test { type doc struct { A map[string]interface{} } return test{ target: &doc{}, expected: &doc{ A: map[string]interface{}{ "b": "hello", }, }, } }, }, { desc: "unmarshal inline table into map of incorrect type", input: `a = {b="hello"}`, gen: func() test { type doc struct { A map[string]int } return test{ target: &doc{}, err: true, } }, }, { desc: "slice pointer in slice pointer", input: `A = ["Hello"]`, gen: func() test { type doc struct { A *[]*string } hello := "Hello" return test{ target: &doc{}, expected: &doc{ A: &[]*string{&hello}, }, } }, }, { desc: "interface holding a string", input: `A = "Hello"`, gen: func() test { type doc struct { A interface{} } return test{ target: &doc{}, expected: &doc{ A: "Hello", }, } }, }, { desc: "map of bools", input: `A = true`, gen: func() test { return test{ target: &map[string]bool{}, expected: &map[string]bool{"A": true}, } }, }, { desc: "map of int64", input: `A = 42`, gen: func() test { return test{ target: &map[string]int64{}, expected: &map[string]int64{"A": 42}, } }, }, { desc: "map of float64", input: `A = 4.2`, gen: func() test { return test{ target: &map[string]float64{}, expected: &map[string]float64{"A": 4.2}, } }, }, { desc: "array of int in map", input: `A = [1,2,3]`, gen: func() test { return test{ target: &map[string][3]int{}, expected: &map[string][3]int{"A": {1, 2, 3}}, } }, }, { desc: "array of int in map with too many elements", input: `A = [1,2,3,4,5]`, gen: func() test { return test{ target: &map[string][3]int{}, expected: &map[string][3]int{"A": {1, 2, 3}}, } }, }, { desc: "array of int in map with invalid element", input: `A = [1,2,false]`, gen: func() test { return test{ target: &map[string][3]int{}, err: true, } }, }, { desc: "nested arrays", input: ` [[A]] [[A.B]] C = 1 [[A]] [[A.B]] C = 2`, gen: func() test { type leaf struct { C int } type inner struct { B [2]leaf } type s struct { A [2]inner } return test{ target: &s{}, expected: &s{A: [2]inner{ {B: [2]leaf{ {C: 1}, }}, {B: [2]leaf{ {C: 2}, }}, }}, } }, }, { desc: "nested arrays too many", input: ` [[A]] [[A.B]] C = 1 [[A.B]] C = 2`, gen: func() test { type leaf struct { C int } type inner struct { B [1]leaf } type s struct { A [1]inner } return test{ target: &s{}, err: true, } }, }, { desc: "empty array table in interface{}", input: `[[products]]`, gen: func() test { return test{ target: &map[string]interface{}{}, expected: &map[string]interface{}{ "products": []interface{}{ map[string]interface{}{}, }, }, } }, }, { desc: "into map with invalid key type", input: `A = "hello"`, gen: func() test { return test{ target: &map[int]string{}, err: true, } }, }, { desc: "empty map into map with invalid key type", input: ``, gen: func() test { return test{ target: &map[int]string{}, expected: &map[int]string{}, } }, }, { desc: "into map with convertible key type", input: `A = "hello"`, gen: func() test { type foo string return test{ target: &map[foo]string{}, expected: &map[foo]string{ "A": "hello", }, } }, }, { desc: "array of int in struct", input: `A = [1,2,3]`, gen: func() test { type s struct { A [3]int } return test{ target: &s{}, expected: &s{A: [3]int{1, 2, 3}}, } }, }, { desc: "array of int in struct", input: `[A] b = 42`, gen: func() test { type s struct { A *map[string]interface{} } return test{ target: &s{}, expected: &s{A: &map[string]interface{}{"b": int64(42)}}, } }, }, { desc: "assign bool to float", input: `A = true`, gen: func() test { return test{ target: &map[string]float64{}, err: true, } }, }, { desc: "interface holding a struct", input: `[A] B = "After"`, gen: func() test { type inner struct { B interface{} } type doc struct { A interface{} } return test{ target: &doc{ A: inner{ B: "Before", }, }, expected: &doc{ A: map[string]interface{}{ "B": "After", }, }, } }, }, { desc: "array of structs with table arrays", input: `[[A]] B = "one" [[A]] B = "two"`, gen: func() test { type inner struct { B string } type doc struct { A [4]inner } return test{ target: &doc{}, expected: &doc{ A: [4]inner{ {B: "one"}, {B: "two"}, }, }, } }, }, { desc: "windows line endings", input: "A = 1\r\n\r\nB = 2", gen: func() test { doc := map[string]interface{}{} return test{ target: &doc, expected: &map[string]interface{}{ "A": int64(1), "B": int64(2), }, } }, }, { desc: "dangling CR", input: "A = 1\r", gen: func() test { doc := map[string]interface{}{} return test{ target: &doc, err: true, } }, }, { desc: "missing NL after CR", input: "A = 1\rB = 2", gen: func() test { doc := map[string]interface{}{} return test{ target: &doc, err: true, } }, }, { desc: "no newline (#526)", input: `a = 1z = 2`, gen: func() test { m := map[string]interface{}{} return test{ target: &m, err: true, } }, }, { desc: "mismatch types int to string", input: `A = 42`, gen: func() test { type S struct { A string } return test{ target: &S{}, err: true, } }, }, { desc: "mismatch types array of int to interface with non-slice", input: `A = [42]`, gen: func() test { type S struct { A string } return test{ target: &S{}, err: true, } }, }, { desc: "comment with CRLF", input: "# foo\r\na=2", gen: func() test { doc := map[string]interface{}{} return test{ target: &doc, expected: &map[string]interface{}{"a": int64(2)}, } }, }, { desc: "comment that looks like a date", input: "a=19#9-", gen: func() test { doc := map[string]interface{}{} return test{ target: &doc, expected: &map[string]interface{}{"a": int64(19)}, } }, }, { desc: "comment that looks like a date", input: "a=199#-", gen: func() test { doc := map[string]interface{}{} return test{ target: &doc, expected: &map[string]interface{}{"a": int64(199)}, } }, }, { desc: "kv that points to a slice", input: "a.b.c = 'foo'", gen: func() test { doc := map[string][]string{} return test{ target: &doc, err: true, } }, }, { desc: "kv that points to a pointer to a slice", input: "a.b.c = 'foo'", gen: func() test { doc := map[string]*[]string{} return test{ target: &doc, err: true, } }, }, } for _, e := range examples { e := e t.Run(e.desc, func(t *testing.T) { if e.skip { t.Skip() } test := e.gen() if test.err && test.expected != nil { panic("invalid test: cannot expect both an error and a value") } err := toml.Unmarshal([]byte(e.input), test.target) if test.err { if err == nil { t.Log("=>", test.target) } require.Error(t, err) } else { require.NoError(t, err) if test.assert != nil { test.assert(t, test) } else { assert.Equal(t, test.expected, test.target) } } }) } } func TestUnmarshalOverflows(t *testing.T) { examples := []struct { t interface{} errors []string }{ { t: &map[string]int32{}, errors: []string{`-2147483649`, `2147483649`}, }, { t: &map[string]int16{}, errors: []string{`-2147483649`, `2147483649`}, }, { t: &map[string]int8{}, errors: []string{`-2147483649`, `2147483649`}, }, { t: &map[string]int{}, errors: []string{`-19223372036854775808`, `9223372036854775808`}, }, { t: &map[string]uint64{}, errors: []string{`-1`, `18446744073709551616`}, }, { t: &map[string]uint32{}, errors: []string{`-1`, `18446744073709551616`}, }, { t: &map[string]uint16{}, errors: []string{`-1`, `18446744073709551616`}, }, { t: &map[string]uint8{}, errors: []string{`-1`, `18446744073709551616`}, }, { t: &map[string]uint{}, errors: []string{`-1`, `18446744073709551616`}, }, } for _, e := range examples { e := e for _, v := range e.errors { v := v t.Run(fmt.Sprintf("%T %s", e.t, v), func(t *testing.T) { doc := "A = " + v err := toml.Unmarshal([]byte(doc), e.t) t.Log("input:", doc) require.Error(t, err) }) } t.Run(fmt.Sprintf("%T ok", e.t), func(t *testing.T) { doc := "A = 1" err := toml.Unmarshal([]byte(doc), e.t) t.Log("input:", doc) require.NoError(t, err) }) } } func TestUnmarshalErrors(t *testing.T) { type mystruct struct { Bar string } data := `bar = 42` s := mystruct{} err := toml.Unmarshal([]byte(data), &s) require.Error(t, err) require.Equal(t, "toml: cannot decode TOML integer into struct field toml_test.mystruct.Bar of type string", err.Error()) } func TestUnmarshalStringInvalidStructField(t *testing.T) { type Server struct { Path string Port int } type Cfg struct { Server Server } var cfg Cfg data := `[server] path = "/my/path" port = "bad" ` file := strings.NewReader(data) err := toml.NewDecoder(file).Decode(&cfg) require.Error(t, err) x := err.(*toml.DecodeError) require.Equal(t, "toml: cannot decode TOML string into struct field toml_test.Server.Port of type int", x.Error()) expected := `1| [server] 2| path = "/my/path" 3| port = "bad" | ~~~~~ cannot decode TOML string into struct field toml_test.Server.Port of type int` require.Equal(t, expected, x.String()) } func TestUnmarshalIntegerInvalidStructField(t *testing.T) { type Server struct { Path string Port int } type Cfg struct { Server Server } var cfg Cfg data := `[server] path = 100 port = 50 ` file := strings.NewReader(data) err := toml.NewDecoder(file).Decode(&cfg) require.Error(t, err) x := err.(*toml.DecodeError) require.Equal(t, "toml: cannot decode TOML integer into struct field toml_test.Server.Path of type string", x.Error()) expected := `1| [server] 2| path = 100 | ~~~ cannot decode TOML integer into struct field toml_test.Server.Path of type string 3| port = 50` require.Equal(t, expected, x.String()) } func TestUnmarshalInvalidTarget(t *testing.T) { x := "foo" err := toml.Unmarshal([]byte{}, x) require.Error(t, err) var m *map[string]interface{} err = toml.Unmarshal([]byte{}, m) require.Error(t, err) } func TestUnmarshalFloat32(t *testing.T) { t.Run("fits", func(t *testing.T) { doc := "A = 1.2" err := toml.Unmarshal([]byte(doc), &map[string]float32{}) require.NoError(t, err) }) t.Run("overflows", func(t *testing.T) { doc := "A = 4.40282346638528859811704183484516925440e+38" err := toml.Unmarshal([]byte(doc), &map[string]float32{}) require.Error(t, err) }) } func TestDecoderStrict(t *testing.T) { examples := []struct { desc string input string expected string target interface{} }{ { desc: "multiple missing root keys", input: ` key1 = "value1" key2 = "missing2" key3 = "missing3" key4 = "value4" `, expected: `2| key1 = "value1" 3| key2 = "missing2" | ~~~~ missing field 4| key3 = "missing3" 5| key4 = "value4" --- 2| key1 = "value1" 3| key2 = "missing2" 4| key3 = "missing3" | ~~~~ missing field 5| key4 = "value4"`, target: &struct { Key1 string Key4 string }{}, }, { desc: "multi-part key", input: `a.short.key="foo"`, expected: `1| a.short.key="foo" | ~~~~~~~~~~~ missing field`, }, { desc: "missing table", input: ` [foo] bar = 42 `, expected: `2| [foo] | ~~~ missing table 3| bar = 42`, }, { desc: "missing array table", input: ` [[foo]] bar = 42`, expected: `2| [[foo]] | ~~~ missing table 3| bar = 42`, }, } for _, e := range examples { e := e t.Run(e.desc, func(t *testing.T) { t.Run("strict", func(t *testing.T) { r := strings.NewReader(e.input) d := toml.NewDecoder(r) d.DisallowUnknownFields() x := e.target if x == nil { x = &struct{}{} } err := d.Decode(x) var tsm *toml.StrictMissingError if errors.As(err, &tsm) { assert.Equal(t, e.expected, tsm.String()) } else { t.Fatalf("err should have been a *toml.StrictMissingError, but got %s (%T)", err, err) } }) t.Run("default", func(t *testing.T) { r := strings.NewReader(e.input) d := toml.NewDecoder(r) x := e.target if x == nil { x = &struct{}{} } err := d.Decode(x) require.NoError(t, err) }) }) } } func TestIssue252(t *testing.T) { type config struct { Val1 string `toml:"val1"` Val2 string `toml:"val2"` } configFile := []byte( ` val1 = "test1" `) cfg := &config{ Val2: "test2", } err := toml.Unmarshal(configFile, cfg) require.NoError(t, err) require.Equal(t, "test2", cfg.Val2) } func TestIssue287(t *testing.T) { b := `y=[[{}]]` v := map[string]interface{}{} err := toml.Unmarshal([]byte(b), &v) require.NoError(t, err) expected := map[string]interface{}{ "y": []interface{}{ []interface{}{ map[string]interface{}{}, }, }, } require.Equal(t, expected, v) } type ( Map458 map[string]interface{} Slice458 []interface{} ) func (m Map458) A(s string) Slice458 { return m[s].([]interface{}) } func TestIssue458(t *testing.T) { s := []byte(`[[package]] dependencies = ["regex"] name = "decode" version = "0.1.0"`) m := Map458{} err := toml.Unmarshal(s, &m) require.NoError(t, err) a := m.A("package") expected := Slice458{ map[string]interface{}{ "dependencies": []interface{}{"regex"}, "name": "decode", "version": "0.1.0", }, } assert.Equal(t, expected, a) } type Integer484 struct { Value int } func (i Integer484) MarshalText() ([]byte, error) { return []byte(strconv.Itoa(i.Value)), nil } func (i *Integer484) UnmarshalText(data []byte) error { conv, err := strconv.Atoi(string(data)) if err != nil { return fmt.Errorf("UnmarshalText: %w", err) } i.Value = conv return nil } type Config484 struct { Integers []Integer484 `toml:"integers"` } func TestIssue484(t *testing.T) { raw := []byte(`integers = ["1","2","3","100"]`) var cfg Config484 err := toml.Unmarshal(raw, &cfg) require.NoError(t, err) assert.Equal(t, Config484{ Integers: []Integer484{{1}, {2}, {3}, {100}}, }, cfg) } func TestIssue494(t *testing.T) { data := ` foo = 2021-04-08 bar = 2021-04-08 ` type s struct { Foo time.Time `toml:"foo"` Bar time.Time `toml:"bar"` } ss := new(s) err := toml.Unmarshal([]byte(data), ss) require.NoError(t, err) } func TestIssue508(t *testing.T) { type head struct { Title string `toml:"title"` } type text struct { head } b := []byte(`title = "This is a title"`) t1 := text{} err := toml.Unmarshal(b, &t1) require.NoError(t, err) require.Equal(t, "This is a title", t1.head.Title) } func TestIssue507(t *testing.T) { data := []byte{'0', '=', '\n', '0', 'a', 'm', 'e'} m := map[string]interface{}{} err := toml.Unmarshal(data, &m) require.Error(t, err) } type uuid [16]byte func (u *uuid) UnmarshalText(text []byte) (err error) { // Note: the original reported issue had a more complex implementation // of this function. But the important part is to verify that a // non-struct type implementing UnmarshalText works with the unmarshal // process. placeholder := bytes.Repeat([]byte{0xAA}, 16) copy(u[:], placeholder) return nil } func TestIssue564(t *testing.T) { type Config struct { ID uuid } var config Config err := toml.Unmarshal([]byte(`id = "0818a52b97b94768941ba1172c76cf6c"`), &config) require.NoError(t, err) require.Equal(t, uuid{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}, config.ID) } func TestIssue575(t *testing.T) { b := []byte(` [pkg.cargo] version = "0.55.0 (5ae8d74b3 2021-06-22)" git_commit_hash = "a178d0322ce20e33eac124758e837cbd80a6f633" [pkg.cargo.target.aarch64-apple-darwin] available = true url = "https://static.rust-lang.org/dist/2021-07-29/cargo-1.54.0-aarch64-apple-darwin.tar.gz" hash = "7bac3901d8eb6a4191ffeebe75b29c78bcb270158ec901addb31f588d965d35d" xz_url = "https://static.rust-lang.org/dist/2021-07-29/cargo-1.54.0-aarch64-apple-darwin.tar.xz" xz_hash = "5207644fd6379f3e5b8ae60016b854efa55a381b0c363bff7f9b2f25bfccc430" [pkg.cargo.target.aarch64-pc-windows-msvc] available = true url = "https://static.rust-lang.org/dist/2021-07-29/cargo-1.54.0-aarch64-pc-windows-msvc.tar.gz" hash = "eb8ccd9b1f6312b06dc749c17896fa4e9c163661c273dcb61cd7a48376227f6d" xz_url = "https://static.rust-lang.org/dist/2021-07-29/cargo-1.54.0-aarch64-pc-windows-msvc.tar.xz" xz_hash = "1a48f723fea1f17d786ce6eadd9d00914d38062d28fd9c455ed3c3801905b388" `) type target struct { XZ_URL string } type pkg struct { Target map[string]target } type doc struct { Pkg map[string]pkg } var dist doc err := toml.Unmarshal(b, &dist) require.NoError(t, err) expected := doc{ Pkg: map[string]pkg{ "cargo": { Target: map[string]target{ "aarch64-apple-darwin": { XZ_URL: "https://static.rust-lang.org/dist/2021-07-29/cargo-1.54.0-aarch64-apple-darwin.tar.xz", }, "aarch64-pc-windows-msvc": { XZ_URL: "https://static.rust-lang.org/dist/2021-07-29/cargo-1.54.0-aarch64-pc-windows-msvc.tar.xz", }, }, }, }, } require.Equal(t, expected, dist) } func TestIssue579(t *testing.T) { var v interface{} err := toml.Unmarshal([]byte(`[foo`), &v) require.Error(t, err) } func TestIssue581(t *testing.T) { var v interface{} err := toml.Unmarshal([]byte(`P=[#`), &v) require.Error(t, err) } func TestIssue585(t *testing.T) { var v interface{} err := toml.Unmarshal([]byte(`a=1979-05127T 0`), &v) require.Error(t, err) } func TestIssue586(t *testing.T) { var v interface{} err := toml.Unmarshal([]byte(`a={ `), &v) require.Error(t, err) } func TestIssue588(t *testing.T) { var v interface{} err := toml.Unmarshal([]byte(`a=[1#`), &v) require.Error(t, err) } // Support lowercase 'T' and 'Z' func TestIssue600(t *testing.T) { var v interface{} err := toml.Unmarshal([]byte(`a=1979-05-27t00:32:00z`), &v) require.NoError(t, err) } func TestIssue596(t *testing.T) { var v interface{} err := toml.Unmarshal([]byte(`a=1979-05-27T90:+2:99`), &v) require.Error(t, err) } func TestIssue602(t *testing.T) { var v interface{} err := toml.Unmarshal([]byte(""), &v) require.NoError(t, err) var expected interface{} = map[string]interface{}{} require.Equal(t, expected, v) } func TestIssue623(t *testing.T) { definition := struct { Things []string }{} values := `[things] foo = "bar"` err := toml.Unmarshal([]byte(values), &definition) require.Error(t, err) } func TestIssue631(t *testing.T) { v := map[string]interface{}{} err := toml.Unmarshal([]byte("\"\\b\u007f\"= 2"), &v) require.Error(t, err) } func TestIssue658(t *testing.T) { var v map[string]interface{} err := toml.Unmarshal([]byte("e={b=1,b=4}"), &v) require.Error(t, err) } func TestIssue662(t *testing.T) { var v map[string]interface{} err := toml.Unmarshal([]byte("a=[{b=1,b=2}]"), &v) require.Error(t, err) } func TestIssue666(t *testing.T) { var v map[string]interface{} err := toml.Unmarshal([]byte("a={}\na={}"), &v) require.Error(t, err) } func TestIssue677(t *testing.T) { doc := ` [Build] Name = "publication build" [[Build.Dependencies]] Name = "command" Program = "hugo" ` type _tomlJob struct { Dependencies []map[string]interface{} } type tomlParser struct { Build *_tomlJob } p := tomlParser{} err := toml.Unmarshal([]byte(doc), &p) require.NoError(t, err) expected := tomlParser{ Build: &_tomlJob{ Dependencies: []map[string]interface{}{ { "Name": "command", "Program": "hugo", }, }, }, } require.Equal(t, expected, p) } func TestIssue701(t *testing.T) { // Expected behavior: // Return an error since a cannot be modified. From the TOML spec: // // > Inline tables are fully self-contained and define all // keys and sub-tables within them. Keys and sub-tables cannot // be added outside the braces. docs := []string{ ` a={} [a.b] z=0 `, ` a={} [[a.b]] z=0 `, } for _, doc := range docs { var v interface{} err := toml.Unmarshal([]byte(doc), &v) assert.Error(t, err) } } func TestIssue703(t *testing.T) { var v interface{} err := toml.Unmarshal([]byte("[a]\nx.y=0\n[a.x]"), &v) require.Error(t, err) } func TestIssue708(t *testing.T) { v := map[string]string{} err := toml.Unmarshal([]byte("0=\"\"\"\\\r\n\"\"\""), &v) require.NoError(t, err) require.Equal(t, map[string]string{"0": ""}, v) } func TestIssue710(t *testing.T) { v := map[string]toml.LocalTime{} err := toml.Unmarshal([]byte(`0=00:00:00.0000000000`), &v) require.NoError(t, err) require.Equal(t, map[string]toml.LocalTime{"0": {Precision: 9}}, v) v1 := map[string]toml.LocalTime{} err = toml.Unmarshal([]byte(`0=00:00:00.0000000001`), &v1) require.NoError(t, err) require.Equal(t, map[string]toml.LocalTime{"0": {Precision: 9}}, v1) v2 := map[string]toml.LocalTime{} err = toml.Unmarshal([]byte(`0=00:00:00.1111111119`), &v2) require.NoError(t, err) require.Equal(t, map[string]toml.LocalTime{"0": {Nanosecond: 111111111, Precision: 9}}, v2) } func TestIssue715(t *testing.T) { var v interface{} err := toml.Unmarshal([]byte("0=+"), &v) require.Error(t, err) err = toml.Unmarshal([]byte("0=-"), &v) require.Error(t, err) err = toml.Unmarshal([]byte("0=+A"), &v) require.Error(t, err) } func TestIssue714(t *testing.T) { var v interface{} err := toml.Unmarshal([]byte("0."), &v) require.Error(t, err) err = toml.Unmarshal([]byte("0={0=0,"), &v) require.Error(t, err) } func TestIssue772(t *testing.T) { type FileHandling struct { FilePattern string `toml:"pattern"` } type Config struct { FileHandling `toml:"filehandling"` } var defaultConfigFile = []byte(` [filehandling] pattern = "reach-masterdev-"`) config := Config{} err := toml.Unmarshal(defaultConfigFile, &config) require.NoError(t, err) require.Equal(t, "reach-masterdev-", config.FileHandling.FilePattern) } func TestIssue774(t *testing.T) { type ScpData struct { Host string `json:"host"` } type GenConfig struct { SCP []ScpData `toml:"scp" comment:"Array of Secure Copy Configurations"` } c := &GenConfig{} c.SCP = []ScpData{{Host: "main.domain.com"}} b, err := toml.Marshal(c) require.NoError(t, err) expected := `# Array of Secure Copy Configurations [[scp]] Host = 'main.domain.com' ` require.Equal(t, expected, string(b)) } func TestIssue799(t *testing.T) { const testTOML = ` # notice the double brackets [[test]] answer = 42 ` var s struct { // should be []map[string]int Test map[string]int `toml:"test"` } err := toml.Unmarshal([]byte(testTOML), &s) require.Error(t, err) } func TestIssue807(t *testing.T) { type A struct { Name string `toml:"name"` } type M struct { *A } var m M err := toml.Unmarshal([]byte(`name = 'foo'`), &m) require.NoError(t, err) require.Equal(t, "foo", m.Name) } func TestIssue850(t *testing.T) { data := make(map[string]string) err := toml.Unmarshal([]byte("foo = {}"), &data) require.Error(t, err) } func TestIssue851(t *testing.T) { type Target struct { Params map[string]string `toml:"params"` } content := "params = {a=\"1\",b=\"2\"}" var target Target err := toml.Unmarshal([]byte(content), &target) require.NoError(t, err) require.Equal(t, map[string]string{"a": "1", "b": "2"}, target.Params) err = toml.Unmarshal([]byte(content), &target) require.NoError(t, err) require.Equal(t, map[string]string{"a": "1", "b": "2"}, target.Params) } func TestIssue866(t *testing.T) { type Pipeline struct { Mapping map[string]struct { Req [][]string `toml:"req"` Res [][]string `toml:"res"` } `toml:"mapping"` } type Pipelines struct { PipelineMapping map[string]*Pipeline `toml:"pipelines"` } var badToml = ` [pipelines.register] mapping.inst.req = [ ["param1", "value1"], ] mapping.inst.res = [ ["param2", "value2"], ] ` pipelines := new(Pipelines) if err := toml.NewDecoder(bytes.NewBufferString(badToml)).DisallowUnknownFields().Decode(pipelines); err != nil { t.Fatal(err) } if pipelines.PipelineMapping["register"].Mapping["inst"].Req[0][0] != "param1" { t.Fatal("unmarshal failed with mismatch value") } var goodTooToml = ` [pipelines.register] mapping.inst.req = [ ["param1", "value1"], ] ` pipelines = new(Pipelines) if err := toml.NewDecoder(bytes.NewBufferString(goodTooToml)).DisallowUnknownFields().Decode(pipelines); err != nil { t.Fatal(err) } if pipelines.PipelineMapping["register"].Mapping["inst"].Req[0][0] != "param1" { t.Fatal("unmarshal failed with mismatch value") } var goodToml = ` [pipelines.register.mapping.inst] req = [ ["param1", "value1"], ] res = [ ["param2", "value2"], ] ` pipelines = new(Pipelines) if err := toml.NewDecoder(bytes.NewBufferString(goodToml)).DisallowUnknownFields().Decode(pipelines); err != nil { t.Fatal(err) } if pipelines.PipelineMapping["register"].Mapping["inst"].Req[0][0] != "param1" { t.Fatal("unmarshal failed with mismatch value") } } func TestIssue915(t *testing.T) { type blah struct { A string `toml:"a"` } type config struct { Fizz string `toml:"fizz"` blah `toml:"blah"` } b := []byte(` fizz = "abc" blah.a = "def"`) var cfg config err := toml.Unmarshal(b, &cfg) require.NoError(t, err) require.Equal(t, "abc", cfg.Fizz) require.Equal(t, "def", cfg.blah.A) require.Equal(t, "def", cfg.A) } func TestUnmarshalDecodeErrors(t *testing.T) { examples := []struct { desc string data string msg string }{ { desc: "local date with invalid digit", data: `a = 20x1-05-21`, }, { desc: "local time with fractional", data: `a = 11:22:33.x`, }, { desc: "wrong time offset separator", data: `a = 1979-05-27T00:32:00.-07:00`, }, { desc: "wrong time offset separator", data: `a = 1979-05-27T00:32:00Z07:00`, }, { desc: "float with double _", data: `flt8 = 224_617.445_991__228`, }, { desc: "float with double .", data: `flt8 = 1..2`, }, { desc: "number with plus sign and leading underscore", data: `a = +_0`, }, { desc: "number with negative sign and leading underscore", data: `a = -_0`, }, { desc: "exponent with plus sign and leading underscore", data: `a = 0e+_0`, }, { desc: "exponent with negative sign and leading underscore", data: `a = 0e-_0`, }, { desc: "int with wrong base", data: `a = 0f2`, }, { desc: "int hex with double underscore", data: `a = 0xFFF__FFF`, }, { desc: "int hex very large", data: `a = 0xFFFFFFFFFFFFFFFFF`, }, { desc: "int oct with double underscore", data: `a = 0o777__77`, }, { desc: "int oct very large", data: `a = 0o77777777777777777777777`, }, { desc: "int bin with double underscore", data: `a = 0b111__111`, }, { desc: "int bin very large", data: `a = 0b11111111111111111111111111111111111111111111111111111111111111111111111111111`, }, { desc: "int dec very large", data: `a = 999999999999999999999999`, }, { desc: "literal string with new lines", data: `a = 'hello world'`, msg: `literal strings cannot have new lines`, }, { desc: "unterminated literal string", data: `a = 'hello`, msg: `unterminated literal string`, }, { desc: "unterminated multiline literal string", data: `a = '''hello`, msg: `multiline literal string not terminated by '''`, }, { desc: "basic string with new lines", data: `a = "hello "`, msg: `basic strings cannot have new lines`, }, { desc: "basic string with unfinished escape", data: `a = "hello \`, msg: `need a character after \`, }, { desc: "basic unfinished multiline string", data: `a = """hello`, msg: `multiline basic string not terminated by """`, }, { desc: "basic unfinished escape in multiline string", data: `a = """hello \`, msg: `need a character after \`, }, { desc: "malformed local date", data: `a = 2021-033-0`, msg: `dates are expected to have the format YYYY-MM-DD`, }, { desc: "malformed tz", data: `a = 2021-03-30 21:31:00+1`, msg: `invalid date-time timezone`, }, { desc: "malformed tz first char", data: `a = 2021-03-30 21:31:00:1`, msg: `extra characters at the end of a local date time`, }, { desc: "bad char between hours and minutes", data: `a = 2021-03-30 213:1:00`, msg: `expecting colon between hours and minutes`, }, { desc: "bad char between minutes and seconds", data: `a = 2021-03-30 21:312:0`, msg: `expecting colon between minutes and seconds`, }, { desc: "invalid hour value", data: `a=1979-05-27T90:+2:99`, msg: `hour cannot be greater 23`, }, { desc: "invalid minutes value", data: `a=1979-05-27T23:+2:99`, msg: `expected digit (0-9)`, }, { desc: "invalid seconds value", data: `a=1979-05-27T12:45:99`, msg: `seconds cannot be greater 60`, }, { desc: `binary with invalid digit`, data: `a = 0bf`, }, { desc: `invalid i in dec`, data: `a = 0i`, }, { desc: `invalid n in dec`, data: `a = 0n`, }, { desc: `invalid unquoted key`, data: `a`, }, { desc: "dt with tz has no time", data: `a = 2021-03-30TZ`, }, { desc: "invalid end of array table", data: `[[a}`, }, { desc: "invalid end of array table two", data: `[[a]}`, }, { desc: "eof after equal", data: `a =`, }, { desc: "invalid true boolean", data: `a = trois`, }, { desc: "invalid false boolean", data: `a = faux`, }, { desc: "inline table with incorrect separator", data: `a = {b=1;}`, }, { desc: "inline table with invalid value", data: `a = {b=faux}`, }, { desc: `incomplete array after whitespace`, data: `a = [ `, }, { desc: `array with comma first`, data: `a = [ ,]`, }, { desc: `array staring with incomplete newline`, data: "a = [\r]", }, { desc: `array with incomplete newline after comma`, data: "a = [1,\r]", }, { desc: `array with incomplete newline after value`, data: "a = [1\r]", }, { desc: `invalid unicode in basic multiline string`, data: `A = """\u123"""`, }, { desc: `invalid long unicode in basic multiline string`, data: `A = """\U0001D11"""`, }, { desc: `invalid unicode in basic string`, data: `A = "\u123"`, }, { desc: `invalid long unicode in basic string`, data: `A = "\U0001D11"`, }, { desc: `invalid escape char basic multiline string`, data: `A = """\z"""`, }, { desc: `invalid inf`, data: `A = ick`, }, { desc: `invalid nan`, data: `A = non`, }, { desc: `invalid character in comment in array`, data: "A = [#\x00\n]", }, { desc: "invalid utf8 character in long string with no escape sequence", data: "a = \"aaaa\x80aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"", }, { desc: "invalid ascii character in long string with no escape sequence", data: "a = \"aaaa\x00aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"", }, { desc: "unfinished 2-byte utf8 character in string with no escape sequence", data: "a = \"aaaa\xC2\"", }, { desc: "unfinished 3-byte utf8 character in string with no escape sequence", data: "a = \"aaaa\xE2\x00\x00\"", }, { desc: "invalid 3rd byte of 3-byte utf8 character in string with no escape sequence", data: "a = \"aaaa\xE2\x80\x00\"", }, { desc: "invalid 4th byte of 4-byte utf8 character in string with no escape sequence", data: "a = \"aaaa\xF2\x81\x81\x00\"", }, { desc: "unfinished 2-byte utf8 character in literal string", data: "a = 'aaa\xC2'", }, { desc: "unfinished 3-byte utf8 character in literal string", data: "a = 'aaaa\xE2\x00\x00'", }, { desc: "invalid 3rd byte of 3-byte utf8 character in literal string", data: "a = 'aaaa\xE2\x80\x00'", }, { desc: "invalid 4th byte of 4-byte utf8 character in literal string", data: "a = 'aaaa\xF2\x81\x81\x00'", }, { desc: "invalid start utf8 character in literal string", data: "a = '\x80'", }, { desc: "utf8 character with not enough bytes before end in literal string", data: "a = '\xEF'", }, { desc: "basic string with newline after the first escape code", data: "a = \"\\t\n\"", }, { desc: "basic string with unfinished escape sequence after the first escape code", data: "a = \"\\t\\", }, { desc: "basic string with unfinished after the first escape code", data: "a = \"\\t", }, { desc: "multiline basic string with unfinished escape sequence after the first escape code", data: "a = \"\"\"\\t\\", }, { desc: `impossible date-day`, data: `A = 2021-03-40T23:59:00`, msg: `impossible date`, }, { desc: `leap day in non-leap year`, data: `A = 2021-02-29T23:59:00`, msg: `impossible date`, }, { desc: `missing minute digit`, data: `a=17:4::01`, }, { desc: `invalid space in year`, data: `i=19 7-12-21T10:32:00`, }, { desc: `missing nanoseconds digits`, data: `a=17:45:56.`, }, { desc: `minutes over 60`, data: `a=17:99:00`, }, { desc: `invalid second`, data: `a=17:00::0`, }, { desc: `invalid hour`, data: `a=1::00:00`, }, { desc: `invalid month`, data: `a=2021-0--29`, }, { desc: `zero is an invalid day`, data: `a=2021-11-00`, }, { desc: `zero is an invalid month`, data: `a=2021-00-11`, }, { desc: `invalid number of seconds digits with trailing digit`, data: `a=0000-01-01 00:00:000000Z3`, }, { desc: `invalid zone offset hours`, data: `a=0000-01-01 00:00:00+24:00`, }, { desc: `invalid zone offset minutes`, data: `a=0000-01-01 00:00:00+00:60`, }, { desc: `invalid character in zone offset hours`, data: `a=0000-01-01 00:00:00+0Z:00`, }, { desc: `invalid character in zone offset minutes`, data: `a=0000-01-01 00:00:00+00:0Z`, }, { desc: `invalid number of seconds`, data: `a=0000-01-01 00:00:00+27000`, }, { desc: `carriage return inside basic key`, data: "\"\r\"=42", }, { desc: `carriage return inside literal key`, data: "'\r'=42", }, { desc: `carriage return inside basic string`, data: "A = \"\r\"", }, { desc: `carriage return inside basic multiline string`, data: "a=\"\"\"\r\"\"\"", }, { desc: `carriage return at the trail of basic multiline string`, data: "a=\"\"\"\r", }, { desc: `carriage return inside literal string`, data: "A = '\r'", }, { desc: `carriage return inside multiline literal string`, data: "a='''\r'''", }, { desc: `carriage return at trail of multiline literal string`, data: "a='''\r", }, { desc: `carriage return in comment`, data: "# this is a test\ra=1", }, { desc: `backspace in comment`, data: "# this is a test\ba=1", }, } for _, e := range examples { e := e t.Run(e.desc, func(t *testing.T) { m := map[string]interface{}{} err := toml.Unmarshal([]byte(e.data), &m) require.Error(t, err) var de *toml.DecodeError if !errors.As(err, &de) { t.Fatalf("err should have been a *toml.DecodeError, but got %s (%T)", err, err) } if e.msg != "" { t.Log("\n" + de.String()) require.Equal(t, "toml: "+e.msg, de.Error()) } }) } } func TestOmitEmpty(t *testing.T) { type inner struct { private string Skip string `toml:"-"` V string } type elem struct { Foo string `toml:",omitempty"` Bar string `toml:",omitempty"` Inner inner `toml:",omitempty"` } type doc struct { X []elem `toml:",inline"` } d := doc{X: []elem{elem{ Foo: "test", Inner: inner{ V: "alue", }, }}} b, err := toml.Marshal(d) require.NoError(t, err) require.Equal(t, "X = [{Foo = 'test', Inner = {V = 'alue'}}]\n", string(b)) } func TestUnmarshalTags(t *testing.T) { type doc struct { Dash string `toml:"-,"` Ignore string `toml:"-"` A string `toml:"hello"` B string `toml:"comma,omitempty"` } data := ` '-' = "dash" Ignore = 'me' hello = 'content' comma = 'ok' ` d := doc{} expected := doc{ Dash: "dash", Ignore: "", A: "content", B: "ok", } err := toml.Unmarshal([]byte(data), &d) require.NoError(t, err) require.Equal(t, expected, d) } func TestASCIIControlCharacters(t *testing.T) { invalidCharacters := []byte{0x7F} for c := byte(0x0); c <= 0x08; c++ { invalidCharacters = append(invalidCharacters, c) } for c := byte(0x0B); c <= 0x0C; c++ { invalidCharacters = append(invalidCharacters, c) } for c := byte(0x0E); c <= 0x1F; c++ { invalidCharacters = append(invalidCharacters, c) } type stringType struct { Delimiter string CanEscape bool } stringTypes := map[string]stringType{ "basic": {Delimiter: "\"", CanEscape: true}, "basicMultiline": {Delimiter: "\"\"\"", CanEscape: true}, "literal": {Delimiter: "'", CanEscape: false}, "literalMultiline": {Delimiter: "'''", CanEscape: false}, } checkError := func(t *testing.T, input []byte) { t.Helper() m := map[string]interface{}{} err := toml.Unmarshal(input, &m) require.Error(t, err) var de *toml.DecodeError if !errors.As(err, &de) { t.Fatalf("err should have been a *toml.DecodeError, but got %s (%T)", err, err) } } for name, st := range stringTypes { t.Run(name, func(t *testing.T) { for _, c := range invalidCharacters { name := fmt.Sprintf("%2X", c) t.Run(name, func(t *testing.T) { data := []byte("A = " + st.Delimiter + string(c) + st.Delimiter) checkError(t, data) if st.CanEscape { t.Run("withEscapeBefore", func(t *testing.T) { data := []byte("A = " + st.Delimiter + "\\t" + string(c) + st.Delimiter) checkError(t, data) }) t.Run("withEscapeAfter", func(t *testing.T) { data := []byte("A = " + st.Delimiter + string(c) + "\\t" + st.Delimiter) checkError(t, data) }) } }) } }) } } //nolint:funlen func TestLocalDateTime(t *testing.T) { examples := []struct { desc string input string prec int }{ { desc: "9 digits zero nanoseconds", input: "2006-01-02T15:04:05.000000000", prec: 9, }, { desc: "9 digits", input: "2006-01-02T15:04:05.123456789", prec: 9, }, { desc: "8 digits", input: "2006-01-02T15:04:05.12345678", prec: 8, }, { desc: "7 digits", input: "2006-01-02T15:04:05.1234567", prec: 7, }, { desc: "6 digits", input: "2006-01-02T15:04:05.123456", prec: 6, }, { desc: "5 digits", input: "2006-01-02T15:04:05.12345", prec: 5, }, { desc: "4 digits", input: "2006-01-02T15:04:05.1234", prec: 4, }, { desc: "3 digits", input: "2006-01-02T15:04:05.123", prec: 3, }, { desc: "2 digits", input: "2006-01-02T15:04:05.12", prec: 2, }, { desc: "1 digit", input: "2006-01-02T15:04:05.1", prec: 1, }, { desc: "0 digit", input: "2006-01-02T15:04:05", }, } for _, e := range examples { e := e t.Run(e.desc, func(t *testing.T) { t.Log("input:", e.input) doc := `a = ` + e.input m := map[string]toml.LocalDateTime{} err := toml.Unmarshal([]byte(doc), &m) require.NoError(t, err) actual := m["a"] golang, err := time.Parse("2006-01-02T15:04:05.999999999", e.input) require.NoError(t, err) expected := toml.LocalDateTime{ toml.LocalDate{golang.Year(), int(golang.Month()), golang.Day()}, toml.LocalTime{golang.Hour(), golang.Minute(), golang.Second(), golang.Nanosecond(), e.prec}, } require.Equal(t, expected, actual) }) } } func TestUnmarshal_RecursiveTable(t *testing.T) { type Foo struct { I int F *Foo } examples := []struct { desc string input string expected string err bool }{ { desc: "simplest", input: ` I=1 `, expected: `{"I":1,"F":null}`, }, { desc: "depth 1", input: ` I=1 [F] I=2 `, expected: `{"I":1,"F":{"I":2,"F":null}}`, }, { desc: "depth 3", input: ` I=1 [F] I=2 [F.F] I=3 `, expected: `{"I":1,"F":{"I":2,"F":{"I":3,"F":null}}}`, }, { desc: "depth 4", input: ` I=1 [F] I=2 [F.F] I=3 [F.F.F] I=4 `, expected: `{"I":1,"F":{"I":2,"F":{"I":3,"F":{"I":4,"F":null}}}}`, }, { desc: "skip mid step", input: ` I=1 [F.F] I=7 `, expected: `{"I":1,"F":{"I":0,"F":{"I":7,"F":null}}}`, }, } for _, ex := range examples { e := ex t.Run(e.desc, func(t *testing.T) { foo := Foo{} err := toml.Unmarshal([]byte(e.input), &foo) if e.err { require.Error(t, err) } else { require.NoError(t, err) j, err := json.Marshal(foo) require.NoError(t, err) assert.Equal(t, e.expected, string(j)) } }) } } func TestUnmarshal_RecursiveTableArray(t *testing.T) { type Foo struct { I int F []*Foo } examples := []struct { desc string input string expected string err bool }{ { desc: "simplest", input: ` I=1 F=[] `, expected: `{"I":1,"F":[]}`, }, { desc: "depth 1", input: ` I=1 [[F]] I=2 F=[] `, expected: `{"I":1,"F":[{"I":2,"F":[]}]}`, }, { desc: "depth 2", input: ` I=1 [[F]] I=2 [[F.F]] I=3 F=[] `, expected: `{"I":1,"F":[{"I":2,"F":[{"I":3,"F":[]}]}]}`, }, { desc: "depth 3", input: ` I=1 [[F]] I=2 [[F.F]] I=3 [[F.F.F]] I=4 F=[] `, expected: `{"I":1,"F":[{"I":2,"F":[{"I":3,"F":[{"I":4,"F":[]}]}]}]}`, }, { desc: "depth 4", input: ` I=1 [[F]] I=2 [[F.F]] I=3 [[F.F.F]] I=4 [[F.F.F.F]] I=5 F=[] `, expected: `{"I":1,"F":[{"I":2,"F":[{"I":3,"F":[{"I":4,"F":[{"I":5,"F":[]}]}]}]}]}`, }, } for _, ex := range examples { e := ex t.Run(e.desc, func(t *testing.T) { foo := Foo{} err := toml.Unmarshal([]byte(e.input), &foo) if e.err { require.Error(t, err) } else { require.NoError(t, err) j, err := json.Marshal(foo) require.NoError(t, err) assert.Equal(t, e.expected, string(j)) } }) } } func TestUnmarshalEmbedNonString(t *testing.T) { type Foo []byte type doc struct { Foo } d := doc{} err := toml.Unmarshal([]byte(`foo = 'bar'`), &d) require.NoError(t, err) require.Nil(t, d.Foo) } func TestUnmarshal_Nil(t *testing.T) { type Foo struct { Foo *Foo `toml:"foo,omitempty"` Bar *Foo `toml:"bar,omitempty"` } examples := []struct { desc string input string expected string err bool }{ { desc: "empty", input: ``, expected: ``, }, { desc: "simplest", input: ` [foo] [foo.foo] `, expected: "[foo]\n[foo.foo]\n", }, } for _, ex := range examples { e := ex t.Run(e.desc, func(t *testing.T) { foo := Foo{} err := toml.Unmarshal([]byte(e.input), &foo) if e.err { require.Error(t, err) } else { require.NoError(t, err) j, err := toml.Marshal(foo) require.NoError(t, err) assert.Equal(t, e.expected, string(j)) } }) } } go-toml-2.1.1/unstable/000077500000000000000000000000001453566013500147165ustar00rootroot00000000000000go-toml-2.1.1/unstable/ast.go000066400000000000000000000071001453566013500160320ustar00rootroot00000000000000package unstable import ( "fmt" "unsafe" "github.com/pelletier/go-toml/v2/internal/danger" ) // Iterator over a sequence of nodes. // // Starts uninitialized, you need to call Next() first. // // For example: // // it := n.Children() // for it.Next() { // n := it.Node() // // do something with n // } type Iterator struct { started bool node *Node } // Next moves the iterator forward and returns true if points to a // node, false otherwise. func (c *Iterator) Next() bool { if !c.started { c.started = true } else if c.node.Valid() { c.node = c.node.Next() } return c.node.Valid() } // IsLast returns true if the current node of the iterator is the last // one. Subsequent calls to Next() will return false. func (c *Iterator) IsLast() bool { return c.node.next == 0 } // Node returns a pointer to the node pointed at by the iterator. func (c *Iterator) Node() *Node { return c.node } // Node in a TOML expression AST. // // Depending on Kind, its sequence of children should be interpreted // differently. // // - Array have one child per element in the array. // - InlineTable have one child per key-value in the table (each of kind // InlineTable). // - KeyValue have at least two children. The first one is the value. The rest // make a potentially dotted key. // - Table and ArrayTable's children represent a dotted key (same as // KeyValue, but without the first node being the value). // // When relevant, Raw describes the range of bytes this node is referring to in // the input document. Use Parser.Raw() to retrieve the actual bytes. type Node struct { Kind Kind Raw Range // Raw bytes from the input. Data []byte // Node value (either allocated or referencing the input). // References to other nodes, as offsets in the backing array // from this node. References can go backward, so those can be // negative. next int // 0 if last element child int // 0 if no child } // Range of bytes in the document. type Range struct { Offset uint32 Length uint32 } // Next returns a pointer to the next node, or nil if there is no next node. func (n *Node) Next() *Node { if n.next == 0 { return nil } ptr := unsafe.Pointer(n) size := unsafe.Sizeof(Node{}) return (*Node)(danger.Stride(ptr, size, n.next)) } // Child returns a pointer to the first child node of this node. Other children // can be accessed calling Next on the first child. Returns an nil if this Node // has no child. func (n *Node) Child() *Node { if n.child == 0 { return nil } ptr := unsafe.Pointer(n) size := unsafe.Sizeof(Node{}) return (*Node)(danger.Stride(ptr, size, n.child)) } // Valid returns true if the node's kind is set (not to Invalid). func (n *Node) Valid() bool { return n != nil } // Key returns the children nodes making the Key on a supported node. Panics // otherwise. They are guaranteed to be all be of the Kind Key. A simple key // would return just one element. func (n *Node) Key() Iterator { switch n.Kind { case KeyValue: value := n.Child() if !value.Valid() { panic(fmt.Errorf("KeyValue should have at least two children")) } return Iterator{node: value.Next()} case Table, ArrayTable: return Iterator{node: n.Child()} default: panic(fmt.Errorf("Key() is not supported on a %s", n.Kind)) } } // Value returns a pointer to the value node of a KeyValue. // Guaranteed to be non-nil. Panics if not called on a KeyValue node, // or if the Children are malformed. func (n *Node) Value() *Node { return n.Child() } // Children returns an iterator over a node's children. func (n *Node) Children() Iterator { return Iterator{node: n.Child()} } go-toml-2.1.1/unstable/benchmark_test.go000066400000000000000000000032651453566013500202440ustar00rootroot00000000000000package unstable import ( "bytes" "testing" ) var valid10Ascii = []byte("1234567890") var valid10Utf8 = []byte("日本語a") var valid1kUtf8 = bytes.Repeat([]byte("0123456789日本語日本語日本語日abcdefghijklmnopqrstuvwx"), 16) var valid1MUtf8 = bytes.Repeat(valid1kUtf8, 1024) var valid1kAscii = bytes.Repeat([]byte("012345678998jhjklasDJKLAAdjdfjsdklfjdslkabcdefghijklmnopqrstuvwx"), 16) var valid1MAscii = bytes.Repeat(valid1kAscii, 1024) func BenchmarkScanComments(b *testing.B) { wrap := func(x []byte) []byte { return []byte("# " + string(x) + "\n") } inputs := map[string][]byte{ "10Valid": wrap(valid10Ascii), "1kValid": wrap(valid1kAscii), "1MValid": wrap(valid1MAscii), "10ValidUtf8": wrap(valid10Utf8), "1kValidUtf8": wrap(valid1kUtf8), "1MValidUtf8": wrap(valid1MUtf8), } for name, input := range inputs { b.Run(name, func(b *testing.B) { b.SetBytes(int64(len(input))) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { scanComment(input) } }) } } func BenchmarkParseLiteralStringValid(b *testing.B) { wrap := func(x []byte) []byte { return []byte("'" + string(x) + "'") } inputs := map[string][]byte{ "10Valid": wrap(valid10Ascii), "1kValid": wrap(valid1kAscii), "1MValid": wrap(valid1MAscii), "10ValidUtf8": wrap(valid10Utf8), "1kValidUtf8": wrap(valid1kUtf8), "1MValidUtf8": wrap(valid1MUtf8), } for name, input := range inputs { b.Run(name, func(b *testing.B) { p := Parser{} b.SetBytes(int64(len(input))) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { _, _, _, err := p.parseLiteralString(input) if err != nil { panic(err) } } }) } } go-toml-2.1.1/unstable/builder.go000066400000000000000000000025261453566013500167000ustar00rootroot00000000000000package unstable // root contains a full AST. // // It is immutable once constructed with Builder. type root struct { nodes []Node } // Iterator over the top level nodes. func (r *root) Iterator() Iterator { it := Iterator{} if len(r.nodes) > 0 { it.node = &r.nodes[0] } return it } func (r *root) at(idx reference) *Node { return &r.nodes[idx] } type reference int const invalidReference reference = -1 func (r reference) Valid() bool { return r != invalidReference } type builder struct { tree root lastIdx int } func (b *builder) Tree() *root { return &b.tree } func (b *builder) NodeAt(ref reference) *Node { return b.tree.at(ref) } func (b *builder) Reset() { b.tree.nodes = b.tree.nodes[:0] b.lastIdx = 0 } func (b *builder) Push(n Node) reference { b.lastIdx = len(b.tree.nodes) b.tree.nodes = append(b.tree.nodes, n) return reference(b.lastIdx) } func (b *builder) PushAndChain(n Node) reference { newIdx := len(b.tree.nodes) b.tree.nodes = append(b.tree.nodes, n) if b.lastIdx >= 0 { b.tree.nodes[b.lastIdx].next = newIdx - b.lastIdx } b.lastIdx = newIdx return reference(b.lastIdx) } func (b *builder) AttachChild(parent reference, child reference) { b.tree.nodes[parent].child = int(child) - int(parent) } func (b *builder) Chain(from reference, to reference) { b.tree.nodes[from].next = int(to) - int(from) } go-toml-2.1.1/unstable/doc.go000066400000000000000000000001621453566013500160110ustar00rootroot00000000000000// Package unstable provides APIs that do not meet the backward compatibility // guarantees yet. package unstable go-toml-2.1.1/unstable/kind.go000066400000000000000000000020571453566013500161760ustar00rootroot00000000000000package unstable import "fmt" // Kind represents the type of TOML structure contained in a given Node. type Kind int const ( // Meta Invalid Kind = iota Comment Key // Top level structures Table ArrayTable KeyValue // Containers values Array InlineTable // Values String Bool Float Integer LocalDate LocalTime LocalDateTime DateTime ) // String implementation of fmt.Stringer. func (k Kind) String() string { switch k { case Invalid: return "Invalid" case Comment: return "Comment" case Key: return "Key" case Table: return "Table" case ArrayTable: return "ArrayTable" case KeyValue: return "KeyValue" case Array: return "Array" case InlineTable: return "InlineTable" case String: return "String" case Bool: return "Bool" case Float: return "Float" case Integer: return "Integer" case LocalDate: return "LocalDate" case LocalTime: return "LocalTime" case LocalDateTime: return "LocalDateTime" case DateTime: return "DateTime" } panic(fmt.Errorf("Kind.String() not implemented for '%d'", k)) } go-toml-2.1.1/unstable/parser.go000066400000000000000000000647371453566013500165620ustar00rootroot00000000000000package unstable import ( "bytes" "fmt" "unicode" "github.com/pelletier/go-toml/v2/internal/characters" "github.com/pelletier/go-toml/v2/internal/danger" ) // ParserError describes an error relative to the content of the document. // // It cannot outlive the instance of Parser it refers to, and may cause panics // if the parser is reset. type ParserError struct { Highlight []byte Message string Key []string // optional } // Error is the implementation of the error interface. func (e *ParserError) Error() string { return e.Message } // NewParserError is a convenience function to create a ParserError // // Warning: Highlight needs to be a subslice of Parser.data, so only slices // returned by Parser.Raw are valid candidates. func NewParserError(highlight []byte, format string, args ...interface{}) error { return &ParserError{ Highlight: highlight, Message: fmt.Errorf(format, args...).Error(), } } // Parser scans over a TOML-encoded document and generates an iterative AST. // // To prime the Parser, first reset it with the contents of a TOML document. // Then, process all top-level expressions sequentially. See Example. // // Don't forget to check Error() after you're done parsing. // // Each top-level expression needs to be fully processed before calling // NextExpression() again. Otherwise, calls to various Node methods may panic if // the parser has moved on the next expression. // // For performance reasons, go-toml doesn't make a copy of the input bytes to // the parser. Make sure to copy all the bytes you need to outlive the slice // given to the parser. type Parser struct { data []byte builder builder ref reference left []byte err error first bool KeepComments bool } // Data returns the slice provided to the last call to Reset. func (p *Parser) Data() []byte { return p.data } // Range returns a range description that corresponds to a given slice of the // input. If the argument is not a subslice of the parser input, this function // panics. func (p *Parser) Range(b []byte) Range { return Range{ Offset: uint32(danger.SubsliceOffset(p.data, b)), Length: uint32(len(b)), } } // Raw returns the slice corresponding to the bytes in the given range. func (p *Parser) Raw(raw Range) []byte { return p.data[raw.Offset : raw.Offset+raw.Length] } // Reset brings the parser to its initial state for a given input. It wipes an // reuses internal storage to reduce allocation. func (p *Parser) Reset(b []byte) { p.builder.Reset() p.ref = invalidReference p.data = b p.left = b p.err = nil p.first = true } // NextExpression parses the next top-level expression. If an expression was // successfully parsed, it returns true. If the parser is at the end of the // document or an error occurred, it returns false. // // Retrieve the parsed expression with Expression(). func (p *Parser) NextExpression() bool { if len(p.left) == 0 || p.err != nil { return false } p.builder.Reset() p.ref = invalidReference for { if len(p.left) == 0 || p.err != nil { return false } if !p.first { p.left, p.err = p.parseNewline(p.left) } if len(p.left) == 0 || p.err != nil { return false } p.ref, p.left, p.err = p.parseExpression(p.left) if p.err != nil { return false } p.first = false if p.ref.Valid() { return true } } } // Expression returns a pointer to the node representing the last successfully // parsed expression. func (p *Parser) Expression() *Node { return p.builder.NodeAt(p.ref) } // Error returns any error that has occurred during parsing. func (p *Parser) Error() error { return p.err } // Position describes a position in the input. type Position struct { // Number of bytes from the beginning of the input. Offset int // Line number, starting at 1. Line int // Column number, starting at 1. Column int } // Shape describes the position of a range in the input. type Shape struct { Start Position End Position } func (p *Parser) position(b []byte) Position { offset := danger.SubsliceOffset(p.data, b) lead := p.data[:offset] return Position{ Offset: offset, Line: bytes.Count(lead, []byte{'\n'}) + 1, Column: len(lead) - bytes.LastIndex(lead, []byte{'\n'}), } } // Shape returns the shape of the given range in the input. Will // panic if the range is not a subslice of the input. func (p *Parser) Shape(r Range) Shape { raw := p.Raw(r) return Shape{ Start: p.position(raw), End: p.position(raw[r.Length:]), } } func (p *Parser) parseNewline(b []byte) ([]byte, error) { if b[0] == '\n' { return b[1:], nil } if b[0] == '\r' { _, rest, err := scanWindowsNewline(b) return rest, err } return nil, NewParserError(b[0:1], "expected newline but got %#U", b[0]) } func (p *Parser) parseComment(b []byte) (reference, []byte, error) { ref := invalidReference data, rest, err := scanComment(b) if p.KeepComments && err == nil { ref = p.builder.Push(Node{ Kind: Comment, Raw: p.Range(data), Data: data, }) } return ref, rest, err } func (p *Parser) parseExpression(b []byte) (reference, []byte, error) { // expression = ws [ comment ] // expression =/ ws keyval ws [ comment ] // expression =/ ws table ws [ comment ] ref := invalidReference b = p.parseWhitespace(b) if len(b) == 0 { return ref, b, nil } if b[0] == '#' { ref, rest, err := p.parseComment(b) return ref, rest, err } if b[0] == '\n' || b[0] == '\r' { return ref, b, nil } var err error if b[0] == '[' { ref, b, err = p.parseTable(b) } else { ref, b, err = p.parseKeyval(b) } if err != nil { return ref, nil, err } b = p.parseWhitespace(b) if len(b) > 0 && b[0] == '#' { cref, rest, err := p.parseComment(b) if cref != invalidReference { p.builder.Chain(ref, cref) } return ref, rest, err } return ref, b, nil } func (p *Parser) parseTable(b []byte) (reference, []byte, error) { // table = std-table / array-table if len(b) > 1 && b[1] == '[' { return p.parseArrayTable(b) } return p.parseStdTable(b) } func (p *Parser) parseArrayTable(b []byte) (reference, []byte, error) { // array-table = array-table-open key array-table-close // array-table-open = %x5B.5B ws ; [[ Double left square bracket // array-table-close = ws %x5D.5D ; ]] Double right square bracket ref := p.builder.Push(Node{ Kind: ArrayTable, }) b = b[2:] b = p.parseWhitespace(b) k, b, err := p.parseKey(b) if err != nil { return ref, nil, err } p.builder.AttachChild(ref, k) b = p.parseWhitespace(b) b, err = expect(']', b) if err != nil { return ref, nil, err } b, err = expect(']', b) return ref, b, err } func (p *Parser) parseStdTable(b []byte) (reference, []byte, error) { // std-table = std-table-open key std-table-close // std-table-open = %x5B ws ; [ Left square bracket // std-table-close = ws %x5D ; ] Right square bracket ref := p.builder.Push(Node{ Kind: Table, }) b = b[1:] b = p.parseWhitespace(b) key, b, err := p.parseKey(b) if err != nil { return ref, nil, err } p.builder.AttachChild(ref, key) b = p.parseWhitespace(b) b, err = expect(']', b) return ref, b, err } func (p *Parser) parseKeyval(b []byte) (reference, []byte, error) { // keyval = key keyval-sep val ref := p.builder.Push(Node{ Kind: KeyValue, }) key, b, err := p.parseKey(b) if err != nil { return invalidReference, nil, err } // keyval-sep = ws %x3D ws ; = b = p.parseWhitespace(b) if len(b) == 0 { return invalidReference, nil, NewParserError(b, "expected = after a key, but the document ends there") } b, err = expect('=', b) if err != nil { return invalidReference, nil, err } b = p.parseWhitespace(b) valRef, b, err := p.parseVal(b) if err != nil { return ref, b, err } p.builder.Chain(valRef, key) p.builder.AttachChild(ref, valRef) return ref, b, err } //nolint:cyclop,funlen func (p *Parser) parseVal(b []byte) (reference, []byte, error) { // val = string / boolean / array / inline-table / date-time / float / integer ref := invalidReference if len(b) == 0 { return ref, nil, NewParserError(b, "expected value, not eof") } var err error c := b[0] switch c { case '"': var raw []byte var v []byte if scanFollowsMultilineBasicStringDelimiter(b) { raw, v, b, err = p.parseMultilineBasicString(b) } else { raw, v, b, err = p.parseBasicString(b) } if err == nil { ref = p.builder.Push(Node{ Kind: String, Raw: p.Range(raw), Data: v, }) } return ref, b, err case '\'': var raw []byte var v []byte if scanFollowsMultilineLiteralStringDelimiter(b) { raw, v, b, err = p.parseMultilineLiteralString(b) } else { raw, v, b, err = p.parseLiteralString(b) } if err == nil { ref = p.builder.Push(Node{ Kind: String, Raw: p.Range(raw), Data: v, }) } return ref, b, err case 't': if !scanFollowsTrue(b) { return ref, nil, NewParserError(atmost(b, 4), "expected 'true'") } ref = p.builder.Push(Node{ Kind: Bool, Data: b[:4], }) return ref, b[4:], nil case 'f': if !scanFollowsFalse(b) { return ref, nil, NewParserError(atmost(b, 5), "expected 'false'") } ref = p.builder.Push(Node{ Kind: Bool, Data: b[:5], }) return ref, b[5:], nil case '[': return p.parseValArray(b) case '{': return p.parseInlineTable(b) default: return p.parseIntOrFloatOrDateTime(b) } } func atmost(b []byte, n int) []byte { if n >= len(b) { return b } return b[:n] } func (p *Parser) parseLiteralString(b []byte) ([]byte, []byte, []byte, error) { v, rest, err := scanLiteralString(b) if err != nil { return nil, nil, nil, err } return v, v[1 : len(v)-1], rest, nil } func (p *Parser) parseInlineTable(b []byte) (reference, []byte, error) { // inline-table = inline-table-open [ inline-table-keyvals ] inline-table-close // inline-table-open = %x7B ws ; { // inline-table-close = ws %x7D ; } // inline-table-sep = ws %x2C ws ; , Comma // inline-table-keyvals = keyval [ inline-table-sep inline-table-keyvals ] parent := p.builder.Push(Node{ Kind: InlineTable, Raw: p.Range(b[:1]), }) first := true var child reference b = b[1:] var err error for len(b) > 0 { previousB := b b = p.parseWhitespace(b) if len(b) == 0 { return parent, nil, NewParserError(previousB[:1], "inline table is incomplete") } if b[0] == '}' { break } if !first { b, err = expect(',', b) if err != nil { return parent, nil, err } b = p.parseWhitespace(b) } var kv reference kv, b, err = p.parseKeyval(b) if err != nil { return parent, nil, err } if first { p.builder.AttachChild(parent, kv) } else { p.builder.Chain(child, kv) } child = kv first = false } rest, err := expect('}', b) return parent, rest, err } //nolint:funlen,cyclop func (p *Parser) parseValArray(b []byte) (reference, []byte, error) { // array = array-open [ array-values ] ws-comment-newline array-close // array-open = %x5B ; [ // array-close = %x5D ; ] // array-values = ws-comment-newline val ws-comment-newline array-sep array-values // array-values =/ ws-comment-newline val ws-comment-newline [ array-sep ] // array-sep = %x2C ; , Comma // ws-comment-newline = *( wschar / [ comment ] newline ) arrayStart := b b = b[1:] parent := p.builder.Push(Node{ Kind: Array, }) // First indicates whether the parser is looking for the first element // (non-comment) of the array. first := true lastChild := invalidReference addChild := func(valueRef reference) { if lastChild == invalidReference { p.builder.AttachChild(parent, valueRef) } else { p.builder.Chain(lastChild, valueRef) } lastChild = valueRef } var err error for len(b) > 0 { cref := invalidReference cref, b, err = p.parseOptionalWhitespaceCommentNewline(b) if err != nil { return parent, nil, err } if cref != invalidReference { addChild(cref) } if len(b) == 0 { return parent, nil, NewParserError(arrayStart[:1], "array is incomplete") } if b[0] == ']' { break } if b[0] == ',' { if first { return parent, nil, NewParserError(b[0:1], "array cannot start with comma") } b = b[1:] cref, b, err = p.parseOptionalWhitespaceCommentNewline(b) if err != nil { return parent, nil, err } if cref != invalidReference { addChild(cref) } } else if !first { return parent, nil, NewParserError(b[0:1], "array elements must be separated by commas") } // TOML allows trailing commas in arrays. if len(b) > 0 && b[0] == ']' { break } var valueRef reference valueRef, b, err = p.parseVal(b) if err != nil { return parent, nil, err } addChild(valueRef) cref, b, err = p.parseOptionalWhitespaceCommentNewline(b) if err != nil { return parent, nil, err } if cref != invalidReference { addChild(cref) } first = false } rest, err := expect(']', b) return parent, rest, err } func (p *Parser) parseOptionalWhitespaceCommentNewline(b []byte) (reference, []byte, error) { rootCommentRef := invalidReference latestCommentRef := invalidReference addComment := func(ref reference) { if rootCommentRef == invalidReference { rootCommentRef = ref } else if latestCommentRef == invalidReference { p.builder.AttachChild(rootCommentRef, ref) latestCommentRef = ref } else { p.builder.Chain(latestCommentRef, ref) latestCommentRef = ref } } for len(b) > 0 { var err error b = p.parseWhitespace(b) if len(b) > 0 && b[0] == '#' { var ref reference ref, b, err = p.parseComment(b) if err != nil { return invalidReference, nil, err } if ref != invalidReference { addComment(ref) } } if len(b) == 0 { break } if b[0] == '\n' || b[0] == '\r' { b, err = p.parseNewline(b) if err != nil { return invalidReference, nil, err } } else { break } } return rootCommentRef, b, nil } func (p *Parser) parseMultilineLiteralString(b []byte) ([]byte, []byte, []byte, error) { token, rest, err := scanMultilineLiteralString(b) if err != nil { return nil, nil, nil, err } i := 3 // skip the immediate new line if token[i] == '\n' { i++ } else if token[i] == '\r' && token[i+1] == '\n' { i += 2 } return token, token[i : len(token)-3], rest, err } //nolint:funlen,gocognit,cyclop func (p *Parser) parseMultilineBasicString(b []byte) ([]byte, []byte, []byte, error) { // ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body // ml-basic-string-delim // ml-basic-string-delim = 3quotation-mark // ml-basic-body = *mlb-content *( mlb-quotes 1*mlb-content ) [ mlb-quotes ] // // mlb-content = mlb-char / newline / mlb-escaped-nl // mlb-char = mlb-unescaped / escaped // mlb-quotes = 1*2quotation-mark // mlb-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii // mlb-escaped-nl = escape ws newline *( wschar / newline ) token, escaped, rest, err := scanMultilineBasicString(b) if err != nil { return nil, nil, nil, err } i := 3 // skip the immediate new line if token[i] == '\n' { i++ } else if token[i] == '\r' && token[i+1] == '\n' { i += 2 } // fast path startIdx := i endIdx := len(token) - len(`"""`) if !escaped { str := token[startIdx:endIdx] verr := characters.Utf8TomlValidAlreadyEscaped(str) if verr.Zero() { return token, str, rest, nil } return nil, nil, nil, NewParserError(str[verr.Index:verr.Index+verr.Size], "invalid UTF-8") } var builder bytes.Buffer // The scanner ensures that the token starts and ends with quotes and that // escapes are balanced. for i < len(token)-3 { c := token[i] //nolint:nestif if c == '\\' { // When the last non-whitespace character on a line is an unescaped \, // it will be trimmed along with all whitespace (including newlines) up // to the next non-whitespace character or closing delimiter. isLastNonWhitespaceOnLine := false j := 1 findEOLLoop: for ; j < len(token)-3-i; j++ { switch token[i+j] { case ' ', '\t': continue case '\r': if token[i+j+1] == '\n' { continue } case '\n': isLastNonWhitespaceOnLine = true } break findEOLLoop } if isLastNonWhitespaceOnLine { i += j for ; i < len(token)-3; i++ { c := token[i] if !(c == '\n' || c == '\r' || c == ' ' || c == '\t') { i-- break } } i++ continue } // handle escaping i++ c = token[i] switch c { case '"', '\\': builder.WriteByte(c) case 'b': builder.WriteByte('\b') case 'f': builder.WriteByte('\f') case 'n': builder.WriteByte('\n') case 'r': builder.WriteByte('\r') case 't': builder.WriteByte('\t') case 'e': builder.WriteByte(0x1B) case 'u': x, err := hexToRune(atmost(token[i+1:], 4), 4) if err != nil { return nil, nil, nil, err } builder.WriteRune(x) i += 4 case 'U': x, err := hexToRune(atmost(token[i+1:], 8), 8) if err != nil { return nil, nil, nil, err } builder.WriteRune(x) i += 8 default: return nil, nil, nil, NewParserError(token[i:i+1], "invalid escaped character %#U", c) } i++ } else { size := characters.Utf8ValidNext(token[i:]) if size == 0 { return nil, nil, nil, NewParserError(token[i:i+1], "invalid character %#U", c) } builder.Write(token[i : i+size]) i += size } } return token, builder.Bytes(), rest, nil } func (p *Parser) parseKey(b []byte) (reference, []byte, error) { // key = simple-key / dotted-key // simple-key = quoted-key / unquoted-key // // unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _ // quoted-key = basic-string / literal-string // dotted-key = simple-key 1*( dot-sep simple-key ) // // dot-sep = ws %x2E ws ; . Period raw, key, b, err := p.parseSimpleKey(b) if err != nil { return invalidReference, nil, err } ref := p.builder.Push(Node{ Kind: Key, Raw: p.Range(raw), Data: key, }) for { b = p.parseWhitespace(b) if len(b) > 0 && b[0] == '.' { b = p.parseWhitespace(b[1:]) raw, key, b, err = p.parseSimpleKey(b) if err != nil { return ref, nil, err } p.builder.PushAndChain(Node{ Kind: Key, Raw: p.Range(raw), Data: key, }) } else { break } } return ref, b, nil } func (p *Parser) parseSimpleKey(b []byte) (raw, key, rest []byte, err error) { if len(b) == 0 { return nil, nil, nil, NewParserError(b, "expected key but found none") } // simple-key = quoted-key / unquoted-key // unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _ // quoted-key = basic-string / literal-string switch { case b[0] == '\'': return p.parseLiteralString(b) case b[0] == '"': return p.parseBasicString(b) case isUnquotedKeyChar(b[0]): key, rest = scanUnquotedKey(b) return key, key, rest, nil default: return nil, nil, nil, NewParserError(b[0:1], "invalid character at start of key: %c", b[0]) } } //nolint:funlen,cyclop func (p *Parser) parseBasicString(b []byte) ([]byte, []byte, []byte, error) { // basic-string = quotation-mark *basic-char quotation-mark // quotation-mark = %x22 ; " // basic-char = basic-unescaped / escaped // basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii // escaped = escape escape-seq-char // escape-seq-char = %x22 ; " quotation mark U+0022 // escape-seq-char =/ %x5C ; \ reverse solidus U+005C // escape-seq-char =/ %x62 ; b backspace U+0008 // escape-seq-char =/ %x66 ; f form feed U+000C // escape-seq-char =/ %x6E ; n line feed U+000A // escape-seq-char =/ %x72 ; r carriage return U+000D // escape-seq-char =/ %x74 ; t tab U+0009 // escape-seq-char =/ %x75 4HEXDIG ; uXXXX U+XXXX // escape-seq-char =/ %x55 8HEXDIG ; UXXXXXXXX U+XXXXXXXX token, escaped, rest, err := scanBasicString(b) if err != nil { return nil, nil, nil, err } startIdx := len(`"`) endIdx := len(token) - len(`"`) // Fast path. If there is no escape sequence, the string should just be // an UTF-8 encoded string, which is the same as Go. In that case, // validate the string and return a direct reference to the buffer. if !escaped { str := token[startIdx:endIdx] verr := characters.Utf8TomlValidAlreadyEscaped(str) if verr.Zero() { return token, str, rest, nil } return nil, nil, nil, NewParserError(str[verr.Index:verr.Index+verr.Size], "invalid UTF-8") } i := startIdx var builder bytes.Buffer // The scanner ensures that the token starts and ends with quotes and that // escapes are balanced. for i < len(token)-1 { c := token[i] if c == '\\' { i++ c = token[i] switch c { case '"', '\\': builder.WriteByte(c) case 'b': builder.WriteByte('\b') case 'f': builder.WriteByte('\f') case 'n': builder.WriteByte('\n') case 'r': builder.WriteByte('\r') case 't': builder.WriteByte('\t') case 'e': builder.WriteByte(0x1B) case 'u': x, err := hexToRune(token[i+1:len(token)-1], 4) if err != nil { return nil, nil, nil, err } builder.WriteRune(x) i += 4 case 'U': x, err := hexToRune(token[i+1:len(token)-1], 8) if err != nil { return nil, nil, nil, err } builder.WriteRune(x) i += 8 default: return nil, nil, nil, NewParserError(token[i:i+1], "invalid escaped character %#U", c) } i++ } else { size := characters.Utf8ValidNext(token[i:]) if size == 0 { return nil, nil, nil, NewParserError(token[i:i+1], "invalid character %#U", c) } builder.Write(token[i : i+size]) i += size } } return token, builder.Bytes(), rest, nil } func hexToRune(b []byte, length int) (rune, error) { if len(b) < length { return -1, NewParserError(b, "unicode point needs %d character, not %d", length, len(b)) } b = b[:length] var r uint32 for i, c := range b { d := uint32(0) switch { case '0' <= c && c <= '9': d = uint32(c - '0') case 'a' <= c && c <= 'f': d = uint32(c - 'a' + 10) case 'A' <= c && c <= 'F': d = uint32(c - 'A' + 10) default: return -1, NewParserError(b[i:i+1], "non-hex character") } r = r*16 + d } if r > unicode.MaxRune || 0xD800 <= r && r < 0xE000 { return -1, NewParserError(b, "escape sequence is invalid Unicode code point") } return rune(r), nil } func (p *Parser) parseWhitespace(b []byte) []byte { // ws = *wschar // wschar = %x20 ; Space // wschar =/ %x09 ; Horizontal tab _, rest := scanWhitespace(b) return rest } //nolint:cyclop func (p *Parser) parseIntOrFloatOrDateTime(b []byte) (reference, []byte, error) { switch b[0] { case 'i': if !scanFollowsInf(b) { return invalidReference, nil, NewParserError(atmost(b, 3), "expected 'inf'") } return p.builder.Push(Node{ Kind: Float, Data: b[:3], Raw: p.Range(b[:3]), }), b[3:], nil case 'n': if !scanFollowsNan(b) { return invalidReference, nil, NewParserError(atmost(b, 3), "expected 'nan'") } return p.builder.Push(Node{ Kind: Float, Data: b[:3], Raw: p.Range(b[:3]), }), b[3:], nil case '+', '-': return p.scanIntOrFloat(b) } if len(b) < 3 { return p.scanIntOrFloat(b) } s := 5 if len(b) < s { s = len(b) } for idx, c := range b[:s] { if isDigit(c) { continue } if idx == 2 && c == ':' || (idx == 4 && c == '-') { return p.scanDateTime(b) } break } return p.scanIntOrFloat(b) } func (p *Parser) scanDateTime(b []byte) (reference, []byte, error) { // scans for contiguous characters in [0-9T:Z.+-], and up to one space if // followed by a digit. hasDate := false hasTime := false hasTz := false seenSpace := false i := 0 byteLoop: for ; i < len(b); i++ { c := b[i] switch { case isDigit(c): case c == '-': hasDate = true const minOffsetOfTz = 8 if i >= minOffsetOfTz { hasTz = true } case c == 'T' || c == 't' || c == ':' || c == '.': hasTime = true case c == '+' || c == '-' || c == 'Z' || c == 'z': hasTz = true case c == ' ': if !seenSpace && i+1 < len(b) && isDigit(b[i+1]) { i += 2 // Avoid reaching past the end of the document in case the time // is malformed. See TestIssue585. if i >= len(b) { i-- } seenSpace = true hasTime = true } else { break byteLoop } default: break byteLoop } } var kind Kind if hasTime { if hasDate { if hasTz { kind = DateTime } else { kind = LocalDateTime } } else { kind = LocalTime } } else { kind = LocalDate } return p.builder.Push(Node{ Kind: kind, Data: b[:i], }), b[i:], nil } //nolint:funlen,gocognit,cyclop func (p *Parser) scanIntOrFloat(b []byte) (reference, []byte, error) { i := 0 if len(b) > 2 && b[0] == '0' && b[1] != '.' && b[1] != 'e' && b[1] != 'E' { var isValidRune validRuneFn switch b[1] { case 'x': isValidRune = isValidHexRune case 'o': isValidRune = isValidOctalRune case 'b': isValidRune = isValidBinaryRune default: i++ } if isValidRune != nil { i += 2 for ; i < len(b); i++ { if !isValidRune(b[i]) { break } } } return p.builder.Push(Node{ Kind: Integer, Data: b[:i], Raw: p.Range(b[:i]), }), b[i:], nil } isFloat := false for ; i < len(b); i++ { c := b[i] if c >= '0' && c <= '9' || c == '+' || c == '-' || c == '_' { continue } if c == '.' || c == 'e' || c == 'E' { isFloat = true continue } if c == 'i' { if scanFollowsInf(b[i:]) { return p.builder.Push(Node{ Kind: Float, Data: b[:i+3], Raw: p.Range(b[:i+3]), }), b[i+3:], nil } return invalidReference, nil, NewParserError(b[i:i+1], "unexpected character 'i' while scanning for a number") } if c == 'n' { if scanFollowsNan(b[i:]) { return p.builder.Push(Node{ Kind: Float, Data: b[:i+3], Raw: p.Range(b[:i+3]), }), b[i+3:], nil } return invalidReference, nil, NewParserError(b[i:i+1], "unexpected character 'n' while scanning for a number") } break } if i == 0 { return invalidReference, b, NewParserError(b, "incomplete number") } kind := Integer if isFloat { kind = Float } return p.builder.Push(Node{ Kind: kind, Data: b[:i], Raw: p.Range(b[:i]), }), b[i:], nil } func isDigit(r byte) bool { return r >= '0' && r <= '9' } type validRuneFn func(r byte) bool func isValidHexRune(r byte) bool { return r >= 'a' && r <= 'f' || r >= 'A' && r <= 'F' || r >= '0' && r <= '9' || r == '_' } func isValidOctalRune(r byte) bool { return r >= '0' && r <= '7' || r == '_' } func isValidBinaryRune(r byte) bool { return r == '0' || r == '1' || r == '_' } func expect(x byte, b []byte) ([]byte, error) { if len(b) == 0 { return nil, NewParserError(b, "expected character %c but the document ended here", x) } if b[0] != x { return nil, NewParserError(b[0:1], "expected character %c", x) } return b[1:], nil } go-toml-2.1.1/unstable/parser_test.go000066400000000000000000000327021453566013500176040ustar00rootroot00000000000000package unstable import ( "fmt" "strconv" "strings" "testing" "github.com/stretchr/testify/require" ) func TestParser_AST_Numbers(t *testing.T) { examples := []struct { desc string input string kind Kind err bool }{ { desc: "integer just digits", input: `1234`, kind: Integer, }, { desc: "integer zero", input: `0`, kind: Integer, }, { desc: "integer sign", input: `+99`, kind: Integer, }, { desc: "integer hex uppercase", input: `0xDEADBEEF`, kind: Integer, }, { desc: "integer hex lowercase", input: `0xdead_beef`, kind: Integer, }, { desc: "integer octal", input: `0o01234567`, kind: Integer, }, { desc: "integer binary", input: `0b11010110`, kind: Integer, }, { desc: "float zero", input: `0.0`, kind: Float, }, { desc: "float positive zero", input: `+0.0`, kind: Float, }, { desc: "float negative zero", input: `-0.0`, kind: Float, }, { desc: "float pi", input: `3.1415`, kind: Float, }, { desc: "float negative", input: `-0.01`, kind: Float, }, { desc: "float signed exponent", input: `5e+22`, kind: Float, }, { desc: "float exponent lowercase", input: `1e06`, kind: Float, }, { desc: "float exponent uppercase", input: `-2E-2`, kind: Float, }, { desc: "float fractional with exponent", input: `6.626e-34`, kind: Float, }, { desc: "float underscores", input: `224_617.445_991_228`, kind: Float, }, { desc: "inf", input: `inf`, kind: Float, }, { desc: "inf negative", input: `-inf`, kind: Float, }, { desc: "inf positive", input: `+inf`, kind: Float, }, { desc: "nan", input: `nan`, kind: Float, }, { desc: "nan negative", input: `-nan`, kind: Float, }, { desc: "nan positive", input: `+nan`, kind: Float, }, } for _, e := range examples { e := e t.Run(e.desc, func(t *testing.T) { p := Parser{} p.Reset([]byte(`A = ` + e.input)) p.NextExpression() err := p.Error() if e.err { require.Error(t, err) } else { require.NoError(t, err) expected := astNode{ Kind: KeyValue, Children: []astNode{ {Kind: e.kind, Data: []byte(e.input)}, {Kind: Key, Data: []byte(`A`)}, }, } compareNode(t, expected, p.Expression()) } }) } } type ( astNode struct { Kind Kind Data []byte Children []astNode } ) func compareNode(t *testing.T, e astNode, n *Node) { t.Helper() require.Equal(t, e.Kind, n.Kind) require.Equal(t, e.Data, n.Data) compareIterator(t, e.Children, n.Children()) } func compareIterator(t *testing.T, expected []astNode, actual Iterator) { t.Helper() idx := 0 for actual.Next() { n := actual.Node() if idx >= len(expected) { t.Fatal("extra child in actual tree") } e := expected[idx] compareNode(t, e, n) idx++ } if idx < len(expected) { t.Fatal("missing children in actual", "idx =", idx, "expected =", len(expected)) } } //nolint:funlen func TestParser_AST(t *testing.T) { examples := []struct { desc string input string ast astNode err bool }{ { desc: "simple string assignment", input: `A = "hello"`, ast: astNode{ Kind: KeyValue, Children: []astNode{ { Kind: String, Data: []byte(`hello`), }, { Kind: Key, Data: []byte(`A`), }, }, }, }, { desc: "simple bool assignment", input: `A = true`, ast: astNode{ Kind: KeyValue, Children: []astNode{ { Kind: Bool, Data: []byte(`true`), }, { Kind: Key, Data: []byte(`A`), }, }, }, }, { desc: "array of strings", input: `A = ["hello", ["world", "again"]]`, ast: astNode{ Kind: KeyValue, Children: []astNode{ { Kind: Array, Children: []astNode{ { Kind: String, Data: []byte(`hello`), }, { Kind: Array, Children: []astNode{ { Kind: String, Data: []byte(`world`), }, { Kind: String, Data: []byte(`again`), }, }, }, }, }, { Kind: Key, Data: []byte(`A`), }, }, }, }, { desc: "array of arrays of strings", input: `A = ["hello", "world"]`, ast: astNode{ Kind: KeyValue, Children: []astNode{ { Kind: Array, Children: []astNode{ { Kind: String, Data: []byte(`hello`), }, { Kind: String, Data: []byte(`world`), }, }, }, { Kind: Key, Data: []byte(`A`), }, }, }, }, { desc: "inline table", input: `name = { first = "Tom", last = "Preston-Werner" }`, ast: astNode{ Kind: KeyValue, Children: []astNode{ { Kind: InlineTable, Children: []astNode{ { Kind: KeyValue, Children: []astNode{ {Kind: String, Data: []byte(`Tom`)}, {Kind: Key, Data: []byte(`first`)}, }, }, { Kind: KeyValue, Children: []astNode{ {Kind: String, Data: []byte(`Preston-Werner`)}, {Kind: Key, Data: []byte(`last`)}, }, }, }, }, { Kind: Key, Data: []byte(`name`), }, }, }, }, } for _, e := range examples { e := e t.Run(e.desc, func(t *testing.T) { p := Parser{} p.Reset([]byte(e.input)) p.NextExpression() err := p.Error() if e.err { require.Error(t, err) } else { require.NoError(t, err) compareNode(t, e.ast, p.Expression()) } }) } } func BenchmarkParseBasicStringWithUnicode(b *testing.B) { p := &Parser{} b.Run("4", func(b *testing.B) { input := []byte(`"\u1234\u5678\u9ABC\u1234\u5678\u9ABC"`) b.ReportAllocs() b.SetBytes(int64(len(input))) for i := 0; i < b.N; i++ { p.parseBasicString(input) } }) b.Run("8", func(b *testing.B) { input := []byte(`"\u12345678\u9ABCDEF0\u12345678\u9ABCDEF0"`) b.ReportAllocs() b.SetBytes(int64(len(input))) for i := 0; i < b.N; i++ { p.parseBasicString(input) } }) } func BenchmarkParseBasicStringsEasy(b *testing.B) { p := &Parser{} for _, size := range []int{1, 4, 8, 16, 21} { b.Run(strconv.Itoa(size), func(b *testing.B) { input := []byte(`"` + strings.Repeat("A", size) + `"`) b.ReportAllocs() b.SetBytes(int64(len(input))) for i := 0; i < b.N; i++ { p.parseBasicString(input) } }) } } func TestParser_AST_DateTimes(t *testing.T) { examples := []struct { desc string input string kind Kind err bool }{ { desc: "offset-date-time with delim 'T' and UTC offset", input: `2021-07-21T12:08:05Z`, kind: DateTime, }, { desc: "offset-date-time with space delim and +8hours offset", input: `2021-07-21 12:08:05+08:00`, kind: DateTime, }, { desc: "local-date-time with nano second", input: `2021-07-21T12:08:05.666666666`, kind: LocalDateTime, }, { desc: "local-date-time", input: `2021-07-21T12:08:05`, kind: LocalDateTime, }, { desc: "local-date", input: `2021-07-21`, kind: LocalDate, }, } for _, e := range examples { e := e t.Run(e.desc, func(t *testing.T) { p := Parser{} p.Reset([]byte(`A = ` + e.input)) p.NextExpression() err := p.Error() if e.err { require.Error(t, err) } else { require.NoError(t, err) expected := astNode{ Kind: KeyValue, Children: []astNode{ {Kind: e.kind, Data: []byte(e.input)}, {Kind: Key, Data: []byte(`A`)}, }, } compareNode(t, expected, p.Expression()) } }) } } // This example demonstrates how to parse a TOML document and preserving // comments. Comments are stored in the AST as Comment nodes. This example // displays the structure of the full AST generated by the parser using the // following structure: // // 1. Each root-level expression is separated by three dashes. // 2. Bytes associated to a node are displayed in square brackets. // 3. Siblings have the same indentation. // 4. Children of a node are indented one level. func ExampleParser_comments() { doc := `# Top of the document comment. # Optional, any amount of lines. # Above table. [table] # Next to table. # Above simple value. key = "value" # Next to simple value. # Below simple value. # Some comment alone. # Multiple comments, on multiple lines. # Above inline table. name = { first = "Tom", last = "Preston-Werner" } # Next to inline table. # Below inline table. # Above array. array = [ 1, 2, 3 ] # Next to one-line array. # Below array. # Above multi-line array. key5 = [ # Next to start of inline array. # Second line before array content. 1, # Next to first element. # After first element. # Before second element. 2, 3, # Next to last element # After last element. ] # Next to end of array. # Below multi-line array. # Before array table. [[products]] # Next to array table. # After array table. ` var printGeneric func(*Parser, int, *Node) printGeneric = func(p *Parser, indent int, e *Node) { if e == nil { return } s := p.Shape(e.Raw) x := fmt.Sprintf("%d:%d->%d:%d (%d->%d)", s.Start.Line, s.Start.Column, s.End.Line, s.End.Column, s.Start.Offset, s.End.Offset) fmt.Printf("%-25s | %s%s [%s]\n", x, strings.Repeat(" ", indent), e.Kind, e.Data) printGeneric(p, indent+1, e.Child()) printGeneric(p, indent, e.Next()) } printTree := func(p *Parser) { for p.NextExpression() { e := p.Expression() fmt.Println("---") printGeneric(p, 0, e) } if err := p.Error(); err != nil { panic(err) } } p := &Parser{ KeepComments: true, } p.Reset([]byte(doc)) printTree(p) // Output: // --- // 1:1->1:31 (0->30) | Comment [# Top of the document comment.] // --- // 2:1->2:33 (31->63) | Comment [# Optional, any amount of lines.] // --- // 4:1->4:15 (65->79) | Comment [# Above table.] // --- // 1:1->1:1 (0->0) | Table [] // 5:2->5:7 (81->86) | Key [table] // 5:9->5:25 (88->104) | Comment [# Next to table.] // --- // 6:1->6:22 (105->126) | Comment [# Above simple value.] // --- // 1:1->1:1 (0->0) | KeyValue [] // 7:7->7:14 (133->140) | String [value] // 7:1->7:4 (127->130) | Key [key] // 7:15->7:38 (141->164) | Comment [# Next to simple value.] // --- // 8:1->8:22 (165->186) | Comment [# Below simple value.] // --- // 10:1->10:22 (188->209) | Comment [# Some comment alone.] // --- // 12:1->12:40 (211->250) | Comment [# Multiple comments, on multiple lines.] // --- // 14:1->14:22 (252->273) | Comment [# Above inline table.] // --- // 1:1->1:1 (0->0) | KeyValue [] // 15:8->15:9 (281->282) | InlineTable [] // 1:1->1:1 (0->0) | KeyValue [] // 15:18->15:23 (291->296) | String [Tom] // 15:10->15:15 (283->288) | Key [first] // 1:1->1:1 (0->0) | KeyValue [] // 15:32->15:48 (305->321) | String [Preston-Werner] // 15:25->15:29 (298->302) | Key [last] // 15:1->15:5 (274->278) | Key [name] // 15:51->15:74 (324->347) | Comment [# Next to inline table.] // --- // 16:1->16:22 (348->369) | Comment [# Below inline table.] // --- // 18:1->18:15 (371->385) | Comment [# Above array.] // --- // 1:1->1:1 (0->0) | KeyValue [] // 1:1->1:1 (0->0) | Array [] // 19:11->19:12 (396->397) | Integer [1] // 19:14->19:15 (399->400) | Integer [2] // 19:17->19:18 (402->403) | Integer [3] // 19:1->19:6 (386->391) | Key [array] // 19:21->19:46 (406->431) | Comment [# Next to one-line array.] // --- // 20:1->20:15 (432->446) | Comment [# Below array.] // --- // 22:1->22:26 (448->473) | Comment [# Above multi-line array.] // --- // 1:1->1:1 (0->0) | KeyValue [] // 1:1->1:1 (0->0) | Array [] // 23:10->23:42 (483->515) | Comment [# Next to start of inline array.] // 24:3->24:38 (518->553) | Comment [# Second line before array content.] // 25:3->25:4 (556->557) | Integer [1] // 25:6->25:30 (559->583) | Comment [# Next to first element.] // 26:3->26:25 (586->608) | Comment [# After first element.] // 27:3->27:27 (611->635) | Comment [# Before second element.] // 28:3->28:4 (638->639) | Integer [2] // 29:3->29:4 (643->644) | Integer [3] // 29:6->29:28 (646->668) | Comment [# Next to last element] // 30:3->30:24 (671->692) | Comment [# After last element.] // 23:1->23:5 (474->478) | Key [key5] // 31:3->31:26 (695->718) | Comment [# Next to end of array.] // --- // 32:1->32:26 (719->744) | Comment [# Below multi-line array.] // --- // 34:1->34:22 (746->767) | Comment [# Before array table.] // --- // 1:1->1:1 (0->0) | ArrayTable [] // 35:3->35:11 (770->778) | Key [products] // 35:14->35:36 (781->803) | Comment [# Next to array table.] // --- // 36:1->36:21 (804->824) | Comment [# After array table.] } func ExampleParser() { doc := ` hello = "world" value = 42 ` p := Parser{} p.Reset([]byte(doc)) for p.NextExpression() { e := p.Expression() fmt.Printf("Expression: %s\n", e.Kind) value := e.Value() it := e.Key() k := it.Node() // shortcut: we know there is no dotted key in the example fmt.Printf("%s -> (%s) %s\n", k.Data, value.Kind, value.Data) } // Output: // Expression: KeyValue // hello -> (String) world // Expression: KeyValue // value -> (Integer) 42 } go-toml-2.1.1/unstable/scanner.go000066400000000000000000000157651453566013500167140ustar00rootroot00000000000000package unstable import "github.com/pelletier/go-toml/v2/internal/characters" func scanFollows(b []byte, pattern string) bool { n := len(pattern) return len(b) >= n && string(b[:n]) == pattern } func scanFollowsMultilineBasicStringDelimiter(b []byte) bool { return scanFollows(b, `"""`) } func scanFollowsMultilineLiteralStringDelimiter(b []byte) bool { return scanFollows(b, `'''`) } func scanFollowsTrue(b []byte) bool { return scanFollows(b, `true`) } func scanFollowsFalse(b []byte) bool { return scanFollows(b, `false`) } func scanFollowsInf(b []byte) bool { return scanFollows(b, `inf`) } func scanFollowsNan(b []byte) bool { return scanFollows(b, `nan`) } func scanUnquotedKey(b []byte) ([]byte, []byte) { // unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _ for i := 0; i < len(b); i++ { if !isUnquotedKeyChar(b[i]) { return b[:i], b[i:] } } return b, b[len(b):] } func isUnquotedKeyChar(r byte) bool { return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '-' || r == '_' } func scanLiteralString(b []byte) ([]byte, []byte, error) { // literal-string = apostrophe *literal-char apostrophe // apostrophe = %x27 ; ' apostrophe // literal-char = %x09 / %x20-26 / %x28-7E / non-ascii for i := 1; i < len(b); { switch b[i] { case '\'': return b[:i+1], b[i+1:], nil case '\n', '\r': return nil, nil, NewParserError(b[i:i+1], "literal strings cannot have new lines") } size := characters.Utf8ValidNext(b[i:]) if size == 0 { return nil, nil, NewParserError(b[i:i+1], "invalid character") } i += size } return nil, nil, NewParserError(b[len(b):], "unterminated literal string") } func scanMultilineLiteralString(b []byte) ([]byte, []byte, error) { // ml-literal-string = ml-literal-string-delim [ newline ] ml-literal-body // ml-literal-string-delim // ml-literal-string-delim = 3apostrophe // ml-literal-body = *mll-content *( mll-quotes 1*mll-content ) [ mll-quotes ] // // mll-content = mll-char / newline // mll-char = %x09 / %x20-26 / %x28-7E / non-ascii // mll-quotes = 1*2apostrophe for i := 3; i < len(b); { switch b[i] { case '\'': if scanFollowsMultilineLiteralStringDelimiter(b[i:]) { i += 3 // At that point we found 3 apostrophe, and i is the // index of the byte after the third one. The scanner // needs to be eager, because there can be an extra 2 // apostrophe that can be accepted at the end of the // string. if i >= len(b) || b[i] != '\'' { return b[:i], b[i:], nil } i++ if i >= len(b) || b[i] != '\'' { return b[:i], b[i:], nil } i++ if i < len(b) && b[i] == '\'' { return nil, nil, NewParserError(b[i-3:i+1], "''' not allowed in multiline literal string") } return b[:i], b[i:], nil } case '\r': if len(b) < i+2 { return nil, nil, NewParserError(b[len(b):], `need a \n after \r`) } if b[i+1] != '\n' { return nil, nil, NewParserError(b[i:i+2], `need a \n after \r`) } i += 2 // skip the \n continue } size := characters.Utf8ValidNext(b[i:]) if size == 0 { return nil, nil, NewParserError(b[i:i+1], "invalid character") } i += size } return nil, nil, NewParserError(b[len(b):], `multiline literal string not terminated by '''`) } func scanWindowsNewline(b []byte) ([]byte, []byte, error) { const lenCRLF = 2 if len(b) < lenCRLF { return nil, nil, NewParserError(b, "windows new line expected") } if b[1] != '\n' { return nil, nil, NewParserError(b, `windows new line should be \r\n`) } return b[:lenCRLF], b[lenCRLF:], nil } func scanWhitespace(b []byte) ([]byte, []byte) { for i := 0; i < len(b); i++ { switch b[i] { case ' ', '\t': continue default: return b[:i], b[i:] } } return b, b[len(b):] } func scanComment(b []byte) ([]byte, []byte, error) { // comment-start-symbol = %x23 ; # // non-ascii = %x80-D7FF / %xE000-10FFFF // non-eol = %x09 / %x20-7F / non-ascii // // comment = comment-start-symbol *non-eol for i := 1; i < len(b); { if b[i] == '\n' { return b[:i], b[i:], nil } if b[i] == '\r' { if i+1 < len(b) && b[i+1] == '\n' { return b[:i+1], b[i+1:], nil } return nil, nil, NewParserError(b[i:i+1], "invalid character in comment") } size := characters.Utf8ValidNext(b[i:]) if size == 0 { return nil, nil, NewParserError(b[i:i+1], "invalid character in comment") } i += size } return b, b[len(b):], nil } func scanBasicString(b []byte) ([]byte, bool, []byte, error) { // basic-string = quotation-mark *basic-char quotation-mark // quotation-mark = %x22 ; " // basic-char = basic-unescaped / escaped // basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii // escaped = escape escape-seq-char escaped := false i := 1 for ; i < len(b); i++ { switch b[i] { case '"': return b[:i+1], escaped, b[i+1:], nil case '\n', '\r': return nil, escaped, nil, NewParserError(b[i:i+1], "basic strings cannot have new lines") case '\\': if len(b) < i+2 { return nil, escaped, nil, NewParserError(b[i:i+1], "need a character after \\") } escaped = true i++ // skip the next character } } return nil, escaped, nil, NewParserError(b[len(b):], `basic string not terminated by "`) } func scanMultilineBasicString(b []byte) ([]byte, bool, []byte, error) { // ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body // ml-basic-string-delim // ml-basic-string-delim = 3quotation-mark // ml-basic-body = *mlb-content *( mlb-quotes 1*mlb-content ) [ mlb-quotes ] // // mlb-content = mlb-char / newline / mlb-escaped-nl // mlb-char = mlb-unescaped / escaped // mlb-quotes = 1*2quotation-mark // mlb-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii // mlb-escaped-nl = escape ws newline *( wschar / newline ) escaped := false i := 3 for ; i < len(b); i++ { switch b[i] { case '"': if scanFollowsMultilineBasicStringDelimiter(b[i:]) { i += 3 // At that point we found 3 apostrophe, and i is the // index of the byte after the third one. The scanner // needs to be eager, because there can be an extra 2 // apostrophe that can be accepted at the end of the // string. if i >= len(b) || b[i] != '"' { return b[:i], escaped, b[i:], nil } i++ if i >= len(b) || b[i] != '"' { return b[:i], escaped, b[i:], nil } i++ if i < len(b) && b[i] == '"' { return nil, escaped, nil, NewParserError(b[i-3:i+1], `""" not allowed in multiline basic string`) } return b[:i], escaped, b[i:], nil } case '\\': if len(b) < i+2 { return nil, escaped, nil, NewParserError(b[len(b):], "need a character after \\") } escaped = true i++ // skip the next character case '\r': if len(b) < i+2 { return nil, escaped, nil, NewParserError(b[len(b):], `need a \n after \r`) } if b[i+1] != '\n' { return nil, escaped, nil, NewParserError(b[i:i+2], `need a \n after \r`) } i++ // skip the \n } } return nil, escaped, nil, NewParserError(b[len(b):], `multiline basic string not terminated by """`) }