Compare commits

..

49 Commits
v1.1 ... main

Author SHA1 Message Date
Thomas Leonard
8f7f82d2c1
Merge pull request #795 from bdodrem/windows_read-stdin__issue_793_and_792
On Windows, fix stdin broken-pipe and blocking domain.  Issues #793 and #792.
2025-01-27 12:27:06 +00:00
bdodrem
d3cb04a67e eio_windows: fix blocking bug and pipe error
* fix blocking issue on Windows : issue #793.
      Adding await_readable before reading fd

* fix broken pipe exception : issue #792.
      Use Unix.read_bigarray instead of Unix_cstruct.read

* replace eio_windows_cstruct_stubs.c by Unix functions.
     Since OCaml 5.2, Unix.read_bigarray and Unix.write_bigarray can be used.
2025-01-27 12:04:08 +00:00
Thomas Leonard
c78db1a757
Merge pull request #794 from talex5/docs
Minor documentation improvements
2025-01-16 10:51:37 +00:00
Thomas Leonard
4a19b2eea9 Minor documentation improvements
- Remove reference to Meio until it's actually working with stable
  versions of Eio.
- Remove the "Please try porting your programs" request now that Eio is
  stable.
- Use a simpler and more typical example for the switches section.
- In the `Executor_pool` make the second example create the pool the
  same way as the first one (and shrink it a bit).
- Replace out-of-date retro-httpaf-bench example with cohttp-eio and
  capnp-rpc examples.
- In `Net.run_server`, suggest using `Executor_pool`.
2025-01-15 16:59:06 +00:00
Thomas Leonard
fdd2593e33
Merge pull request #789 from talex5/fix-benchmarks
Disable dune subst
2024-12-06 20:00:33 +00:00
Thomas Leonard
c39449a631 Disable dune subst
It was always unnecessary, but with dune 3.17.0 it's causing the
benchmarks to fail to install. The error is:

    Error: There is no dune-project file in the current directory, please add one
    with a (name <name>) field in it.
    Hint: 'dune subst' must be executed from the root of the project
2024-12-06 14:54:15 +00:00
Thomas Leonard
f195295e3d
Merge pull request #787 from talex5/freebsd-close
Ignore ECONNRESET on close
2024-12-06 09:02:12 +00:00
Thomas Leonard
17665689f5 Ignore ECONNRESET on close
FreeBSD returns ECONNRESET in certain (unclear) circumstances, but it
does still close the FD successfully. If you care about this case, you
should probably use `shutdown` instead and check for it there.

Python and Ruby at least both explicitly check for and ignore this error
too.
2024-12-02 11:57:55 +00:00
Thomas Leonard
f26d70d642
Merge pull request #784 from talex5/changelog
Update changelog
2024-11-22 11:25:01 +00:00
Thomas Leonard
730033c0f1 Update changelog 2024-11-22 10:14:28 +00:00
Thomas Leonard
cc0274dd2f
Merge pull request #783 from talex5/windows-conn-aborted
eio_windows: group ECONNABORTED with other connection reset errors
2024-11-22 09:12:14 +00:00
Thomas Leonard
b971fa1cca eio_windows: group ECONNABORTED with other connection reset errors
Windows returns this if the other end disconnects (seen while testing
with both ends on the same machine).
2024-11-21 15:00:52 +00:00
Thomas Leonard
2d9e24a67f
Merge pull request #782 from talex5/openbsd
Fix tests on OpenBSD
2024-11-21 10:46:45 +00:00
Thomas Leonard
ae0eaa2dfd Fix tests on OpenBSD
- Add test dependency on bash.
- Allow www instead of http as the service name for port 80.
2024-11-21 10:27:00 +00:00
Thomas Leonard
681400c18e
Merge pull request #780 from talex5/windows-getaddrinfo
eio_windows: work around problems in Unix.getaddrinfo
2024-11-21 10:00:18 +00:00
Thomas Leonard
b41d4d8a49 eio_windows: work around problems in Unix.getaddrinfo
OCaml's `Unix.getaddrinfo` on Windows doesn't set `ai_protocol` to
anything useful, so you can't tell which addresses are TCP and which are
UDP. So, do two separate queries.
2024-11-20 12:21:04 +00:00
Thomas Leonard
9b939abedd
Merge pull request #779 from talex5/fork-bt
Preserve backtraces in fork_daemon and fork_promise_exn
2024-11-16 10:33:37 +00:00
Thomas Leonard
61f738df51 Preserve backtraces in fork_daemon and fork_promise_exn
If the fiber fails, record the backtrace when failing the switch. `fork`
itself already did this, but `fork_daemon` and `fork_promise_exn`
didn't.
2024-11-16 10:19:20 +00:00
Onah_Anthony
eb8fe3ef82
Check if windows has_symlink for tests (#771)
Symlinking is privileged operation on Windows, so we check if the running user can make symlinks before running tests that require them.
2024-10-21 15:42:36 +01:00
Thomas Leonard
debe35621e
Merge pull request #769 from patricoferris/include-fork-action-h
Make fork_action.h a public_header
2024-10-19 11:26:23 +01:00
Patrick Ferris
dd13303a4a Make fork_action.h a public_header 2024-10-17 19:43:32 +01:00
Patrick Ferris
216ccff845
Merge pull request #765 from ocaml-multicore/ai-hacking-guidance
Add advice about using AI for code generation
2024-10-07 13:22:02 +01:00
Patrick Ferris
a65fd2e612
Add advice about using AI for code generation 2024-10-06 11:28:52 +01:00
Thomas Leonard
f8c9441852
Merge pull request #756 from talex5/win-unregister-fd
eio_windows: unregister FDs on cancel
2024-09-30 12:52:39 +01:00
Thomas Leonard
8364428147 eio_windows: unregister FDs on cancel
This was fixed in cc19aa160626f5b for `eio_posix`, but not for
`eio_windows`.

The error looks like:

    exception Unix.Unix_error(Unix.ENOTSOCK, "select", "")
2024-09-29 14:15:13 +01:00
Thomas Leonard
d2a3e21bc4
Merge pull request #755 from talex5/cleanup
Minor code cleanups
2024-09-18 16:45:45 +01:00
Thomas Leonard
424f5e8c96 Move Dla to eio.utils
It doesn't need to be in eio.core, as nothing else there depends on it.
2024-09-18 12:02:49 +01:00
Thomas Leonard
89019ddd3d Remove unused library
eio.core doesn't use cstruct.
2024-09-18 11:51:57 +01:00
Thomas Leonard
534f89cf0e
Merge pull request #754 from talex5/quiet-tsan
eio_linux: avoid triggering a TSan warning
2024-09-07 14:06:38 +01:00
Thomas Leonard
9ca440ac5c eio_linux: avoid triggering a TSan warning
TSan warns that setting `statx_works` races with users of it. This isn't
really a problem, because we always set it to the same value, but it's
distracting when looking for other bugs.

Reported by Anil Madhavapeddy in #751.
2024-09-06 17:23:32 +01:00
Thomas Leonard
e2dc1d79e5
Merge pull request #753 from talex5/cancel-alloc-fixed
eio_linux: allow alloc_fixed_or_wait to be cancelled
2024-09-06 17:23:14 +01:00
Thomas Leonard
0c414327d1 eio_linux: allow alloc_fixed_or_wait to be cancelled 2024-09-06 17:11:37 +01:00
Thomas Leonard
0f6b65dea2
Merge pull request #752 from talex5/linux-get-sched
eio_linux: refactor fixed buffer code
2024-09-05 11:07:08 +01:00
Thomas Leonard
cc2cd3da32 eio_linux: refactor fixed buffer code
Instead of having separate Alloc, Alloc_or_wait and Free effects,
the scheduler now provides a single Get effect to return itself,
and the actual work is now done in the calling fiber. This is cleaner,
and seems to be slightly faster too.

Note that `alloc_fixed_or_wait` is currently not cancellable (it wasn't
before either, but it's more obvious now).

It would be possible to use DLS to store the scheduler rather than using
an effect. However, the improvement in speed is minimal and there are
some complications with sys-threads, so probably better to wait for
OCaml to support thread-local-storage first.
2024-09-04 15:41:36 +01:00
Thomas Leonard
d47b5e29f7
Merge pull request #749 from talex5/trace-spawn
Record trace event when spawning processes
2024-08-23 15:16:55 +01:00
Thomas Leonard
c2d314b930 Record trace event when spawning processes
This can take quite a long time.
2024-08-23 15:03:43 +01:00
Thomas Leonard
33d4e01a30
Merge pull request #745 from talex5/fs-example
examples/fs: show how to read files while scanning
2024-06-28 11:03:33 +01:00
Thomas Leonard
c45978252a examples/fs: show how to read files while scanning 2024-06-22 16:41:21 +01:00
Thomas Leonard
37840760b1
Merge pull request #744 from talex5/unix-net-types
Eio_unix.Net: make some return types more polymorphic
2024-06-21 10:17:40 +01:00
Thomas Leonard
8cb86a703e Eio_unix.Net: make some return types more polymorphic
This allows e.g. using the result of the `import_socket_stream` as a
`stream_socket_ty` without needing a cast.
2024-06-20 09:37:40 +01:00
Thomas Leonard
12721820e4
Merge pull request #743 from talex5/win-fwd-slash
Eio.Path: always use "/" as separator
2024-06-19 17:13:42 +01:00
Thomas Leonard
32e501a198 Eio.Path: always use "/" as separator
path.mli says:

> In Eio, the directory separator is always "/", even on Windows.

However, `Path.(/)` used `Filename.concat` to create paths, which uses
the native separator ("\" on Windows).
2024-06-19 14:27:26 +01:00
Thomas Leonard
a21b507de8
Merge pull request #742 from talex5/win-openat-debug
eio_windows: improve openat error handling
2024-06-19 14:12:20 +01:00
Thomas Leonard
54df8fdd62 eio_windows: run the fs example in CI 2024-06-19 11:53:18 +01:00
Thomas Leonard
2cd09925d0 eio_windows: improve openat error handling 2024-06-19 11:45:19 +01:00
Thomas Leonard
642bdbef7e
Merge pull request #741 from copy/main
define struct clone_args for linux-lts versions that don't have it
2024-06-19 11:26:07 +01:00
Fabian
574afc103a define struct clone_args for linux-lts versions that don't have it 2024-06-18 19:12:52 +01:00
Thomas Leonard
17562f24fe
Merge pull request #739 from talex5/doc-seq
Add example to `Buf_read.seq` documentation
2024-06-17 09:27:07 +01:00
Thomas Leonard
8ef76f06bf Add example to Buf_read.seq documentation 2024-06-14 11:30:09 +01:00
55 changed files with 808 additions and 722 deletions

View File

@ -12,7 +12,7 @@ jobs:
os: os:
- macos-latest - macos-latest
ocaml-compiler: ocaml-compiler:
- 5.1.x - 5.2.x
local-packages: local-packages:
- eio eio_posix eio_main - eio eio_posix eio_main
@ -44,7 +44,7 @@ jobs:
with: with:
opam-pin: false opam-pin: false
opam-depext: false opam-depext: false
ocaml-compiler: ocaml.5.1.0,ocaml-option-mingw ocaml-compiler: ocaml.5.2.0,ocaml-option-mingw
opam-repositories: | opam-repositories: |
dra27: https://github.com/dra27/opam-repository.git#windows-5.0 dra27: https://github.com/dra27/opam-repository.git#windows-5.0
normal: https://github.com/ocaml/opam-repository.git normal: https://github.com/ocaml/opam-repository.git
@ -57,6 +57,7 @@ jobs:
- run: opam exec -- dune build - run: opam exec -- dune build
- run: opam exec -- dune runtest - run: opam exec -- dune runtest
- run: opam exec -- dune exec -- ./examples/net/main.exe - run: opam exec -- dune exec -- ./examples/net/main.exe
- run: opam exec -- dune exec -- ./examples/fs/main.exe
docker: docker:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

View File

@ -1,3 +1,60 @@
## v1.2
Changes:
- Make `fork_action.h` a public header (@patricoferris #769, reviewed by @talex5).
Allows other libraries to add new actions.
- Record trace event when spawning processes (@talex5 #749).
Spawning a subprocess can take a long time in some cases, so show it clearly in the traces.
- Eio_unix.Net: make some return types more polymorphic (@talex5 #744).
Bug fixes:
- Preserve backtraces in `fork_daemon` (@talex5 #779).
- Eio.Path: always use "/" as separator (@talex5 #743).
Linux backend:
- Allow `alloc_fixed_or_wait` to be cancelled (@talex5 #753).
- Avoid triggering a (harmless) TSan warning (@talex5 #754, reported by @avsm).
Windows backend:
- Unregister FDs on cancel (@talex5 #756).
Fixes `exception Unix.Unix_error(Unix.ENOTSOCK, "select", "")`.
- Work around problems in `Unix.getaddrinfo` (@talex5 #780).
Fixes e.g. `No addresses found for host name "127.0.0.1"`.
- Group `ECONNABORTED` with other connection reset errors (@talex5 #783).
- Check `has_symlink` for tests (@create2000 #771, reviewed by @patricoferris and @talex5).
- Improve `openat` error handling (@talex5 #742, reported by @kentookura).
Fixes `exception Unix.Unix_error(Unix.ENOENT, "openat", "")`.
Documentation:
- examples/fs: show how to read files while scanning (@talex5 #745).
- Add example to `Buf_read.seq` documentation (@talex5 #739, requested by @darrenldl and @rizo).
Build and test:
- Fix tests on OpenBSD (@talex5 #782).
- Add advice about using AI for code generation (@patricoferris #765, reviewed by @avsm and @talex5).
- Minor code cleanups (@talex5 #755).
- Define `struct clone_args` for linux-lts versions that don't have it (@copy #741, reviewed by @talex5).
- eio_linux: refactor fixed buffer code (@talex5 #752).
## v1.1 ## v1.1
New features: New features:

View File

@ -135,4 +135,15 @@ Try to avoid making unnecessary changes; this makes review harder and clutters u
`ocamlformat` may be useful to get badly messed up code to a baseline unformatted state, `ocamlformat` may be useful to get badly messed up code to a baseline unformatted state,
from which human formatting can be added where needed. from which human formatting can be added where needed.
## AI-generated Code
Contributing to Eio should not be done _solely_ using "AI tools" such as ChatGPT. This is for a few reasons:
1. **It obfuscates how you think**. Purely AI-generated code tells us little about how you think and the problems you might be having. This makes it harder to provide good feedback on PRs and issues.
2. **It is often more work to review**. Particularly for the OCaml ecosystem and libraries like Eio, it seems that these tools are not very good and generate a lot of believable code that is in actual fact completely wrong. PR comments and the code submitted with them can say completely different things.
3. **It is a grey area for licensing**. Models like ChatGPT have been trained on lots of code with different licenses and has been known to simply copy code as an answer to a prompt. We would like to avoid this headache as best we can.
Use AI tools, if you wish, to help you understand OCaml and Eio. Do not offload all of the work of a PR or a comment to these tools.
[dscheck]: https://github.com/ocaml-multicore/dscheck [dscheck]: https://github.com/ocaml-multicore/dscheck

View File

@ -79,9 +79,8 @@ Additionally, modern operating systems provide high-performance alternatives to
For example, Linux's io_uring system has applications write the operations they want to perform to a ring buffer, For example, Linux's io_uring system has applications write the operations they want to perform to a ring buffer,
which Linux handles asynchronously, and Eio can take advantage of this. which Linux handles asynchronously, and Eio can take advantage of this.
Please try porting your programs to use Eio and submit PRs or open issues when you find problems. You can always [fall back to using Lwt libraries](#lwt) to provide missing features if necessary.
Remember that you can always [fall back to using Lwt libraries](#lwt) to provide missing features if necessary. See [Awesome Multicore OCaml][] for links to other projects using Eio.
See [Awesome Multicore OCaml][] for links to work migrating other projects to Eio.
## Eio packages ## Eio packages
@ -232,12 +231,7 @@ The green segments show when each fiber is running.
Note that the output from `traceln` appears in the trace as well as on the console. Note that the output from `traceln` appears in the trace as well as on the console.
In the eio-trace window, scrolling with the mouse or touchpad will zoom in or out of the diagram. In the eio-trace window, scrolling with the mouse or touchpad will zoom in or out of the diagram.
There are various third-party tools that can also consume this data Third-party tools, such as [Olly][], can also consume this data.
(but may currently require patches to support the new system):
- [Meio][] (Monitoring for Eio) provides an interactive console-based UI for exploring running fibers.
- [Olly][] can save Perfetto traces and report statistics.
[examples/trace](./examples/trace/) shows how to consume the events manually. [examples/trace](./examples/trace/) shows how to consume the events manually.
## Cancellation ## Cancellation
@ -311,22 +305,23 @@ For example:
```ocaml ```ocaml
# Eio_main.run @@ fun _env -> # Eio_main.run @@ fun _env ->
Switch.run (fun sw -> Switch.run (fun sw ->
Fiber.fork ~sw for i = 1 to 3 do
(fun () -> for i = 1 to 3 do traceln "i = %d" i; Fiber.yield () done); Fiber.fork ~sw (fun () ->
traceln "First thread forked"; traceln "Job %d starting" i;
Fiber.fork ~sw Fiber.yield ();
(fun () -> for j = 1 to 3 do traceln "j = %d" j; Fiber.yield () done); traceln "%d done" i;
traceln "Second thread forked; top-level code is finished" );
); done;
traceln "All child fibers forked";
);
traceln "Switch is finished";; traceln "Switch is finished";;
+i = 1 +Job 1 starting
+First thread forked +Job 2 starting
+j = 1 +Job 3 starting
+Second thread forked; top-level code is finished +All child fibers forked
+i = 2 +1 done
+j = 2 +2 done
+i = 3 +3 done
+j = 3
+Switch is finished +Switch is finished
- : unit = () - : unit = ()
``` ```
@ -1015,12 +1010,8 @@ Usually you will only want one pool for an entire application, so the pool is ty
let () = let () =
Eio_main.run @@ fun env -> Eio_main.run @@ fun env ->
Switch.run @@ fun sw -> Switch.run @@ fun sw ->
let pool = let dm = Eio.Stdenv.domain_mgr env in
Eio.Executor_pool.create main ~pool:(Eio.Executor_pool.create ~sw ~domain_count:2 dm)
~sw (Eio.Stdenv.domain_mgr env)
~domain_count:4
in
main ~pool
``` ```
The pool starts its domain workers immediately upon creation. The pool starts its domain workers immediately upon creation.
@ -1034,11 +1025,7 @@ The total number of domains should not exceed `Domain.recommended_domain_count`
We can run the previous example using an Executor Pool like this: We can run the previous example using an Executor Pool like this:
```ocaml ```ocaml
let main ~domain_mgr = let main ~pool =
Switch.run @@ fun sw ->
let pool =
Eio.Executor_pool.create ~sw domain_mgr ~domain_count:4
in
let test n = let test n =
traceln "sum 1..%d = %d" n traceln "sum 1..%d = %d" n
(Eio.Executor_pool.submit_exn pool ~weight:1.0 (Eio.Executor_pool.submit_exn pool ~weight:1.0
@ -1052,7 +1039,9 @@ let main ~domain_mgr =
<!-- $MDX non-deterministic=output --> <!-- $MDX non-deterministic=output -->
```ocaml ```ocaml
# Eio_main.run @@ fun env -> # Eio_main.run @@ fun env ->
main ~domain_mgr:(Eio.Stdenv.domain_mgr env);; Switch.run @@ fun sw ->
let dm = Eio.Stdenv.domain_mgr env in
main ~pool:(Eio.Executor_pool.create ~sw ~domain_count:2 dm)
+Starting CPU-intensive task... +Starting CPU-intensive task...
+Starting CPU-intensive task... +Starting CPU-intensive task...
+Finished +Finished
@ -1632,7 +1621,8 @@ It can then be used like any other Eio flow:
## Example Applications ## Example Applications
- [gemini-eio][] is a simple Gemini browser. It shows how to integrate Eio with `ocaml-tls` and `notty`. - [gemini-eio][] is a simple Gemini browser. It shows how to integrate Eio with `ocaml-tls` and `notty`.
- [ocaml-multicore/retro-httpaf-bench](https://github.com/ocaml-multicore/retro-httpaf-bench) includes a simple HTTP server using Eio. It shows how to use Eio with `httpaf`, and how to use multiple domains for increased performance. - [cohttp-eio/examples](https://github.com/mirage/ocaml-cohttp/tree/master/cohttp-eio/examples) shows how to use Eio with HTTP.
- [capnp-rpc](https://github.com/mirage/capnp-rpc) shows how to use Eio with Cap'n Proto.
- [Awesome Multicore OCaml][] lists many other projects. - [Awesome Multicore OCaml][] lists many other projects.
## Integrations ## Integrations
@ -1906,7 +1896,6 @@ Some background about the effects system can be found in:
[Eio.Condition]: https://ocaml-multicore.github.io/eio/eio/Eio/Condition/index.html [Eio.Condition]: https://ocaml-multicore.github.io/eio/eio/Eio/Condition/index.html
[Domainslib]: https://github.com/ocaml-multicore/domainslib [Domainslib]: https://github.com/ocaml-multicore/domainslib
[kcas]: https://github.com/ocaml-multicore/kcas [kcas]: https://github.com/ocaml-multicore/kcas
[Meio]: https://github.com/tarides/meio
[Lambda Capabilities]: https://roscidus.com/blog/blog/2023/04/26/lambda-capabilities/ [Lambda Capabilities]: https://roscidus.com/blog/blog/2023/04/26/lambda-capabilities/
[Eio.Process]: https://ocaml-multicore.github.io/eio/eio/Eio/Process/index.html [Eio.Process]: https://ocaml-multicore.github.io/eio/eio/Eio/Process/index.html
[Dev meetings]: https://docs.google.com/document/d/1ZBfbjAkvEkv9ldumpZV5VXrEc_HpPeYjHPW_TiwJe4Q [Dev meetings]: https://docs.google.com/document/d/1ZBfbjAkvEkv9ldumpZV5VXrEc_HpPeYjHPW_TiwJe4Q

Binary file not shown.

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1280pt" height="152pt" viewBox="0 0 1280 152" version="1.1"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1280pt" height="184pt" viewBox="0 0 1280 184" version="1.1">
<defs> <defs>
<g> <g>
<symbol overflow="visible" id="glyph0-0"> <symbol overflow="visible" id="glyph0-0">
@ -81,489 +81,450 @@
<path style="stroke:none;" d="M 0.40625 1.421875 L 0.40625 -5.640625 L 4.40625 -5.640625 L 4.40625 1.421875 Z M 0.84375 0.96875 L 3.953125 0.96875 L 3.953125 -5.1875 L 0.84375 -5.1875 Z M 0.84375 0.96875 "/> <path style="stroke:none;" d="M 0.40625 1.421875 L 0.40625 -5.640625 L 4.40625 -5.640625 L 4.40625 1.421875 Z M 0.84375 0.96875 L 3.953125 0.96875 L 3.953125 -5.1875 L 0.84375 -5.1875 Z M 0.84375 0.96875 "/>
</symbol> </symbol>
<symbol overflow="visible" id="glyph1-1"> <symbol overflow="visible" id="glyph1-1">
<path style="stroke:none;" d="M 0.75 -4.375 L 1.46875 -4.375 L 1.46875 0 L 0.75 0 Z M 0.75 -6.078125 L 1.46875 -6.078125 L 1.46875 -5.171875 L 0.75 -5.171875 Z M 0.75 -6.078125 "/> <path style="stroke:none;" d="M 0.78125 -5.828125 L 1.578125 -5.828125 L 1.578125 -0.40625 C 1.578125 0.300781 1.441406 0.8125 1.171875 1.125 C 0.910156 1.445312 0.484375 1.609375 -0.109375 1.609375 L -0.421875 1.609375 L -0.421875 0.9375 L -0.171875 0.9375 C 0.179688 0.9375 0.425781 0.835938 0.5625 0.640625 C 0.707031 0.453125 0.78125 0.101562 0.78125 -0.40625 Z M 0.78125 -5.828125 "/>
</symbol> </symbol>
<symbol overflow="visible" id="glyph1-2"> <symbol overflow="visible" id="glyph1-2">
<path style="stroke:none;" d=""/>
</symbol>
<symbol overflow="visible" id="glyph1-3">
<path style="stroke:none;" d="M 0.84375 -3.640625 L 5.859375 -3.640625 L 5.859375 -2.984375 L 0.84375 -2.984375 Z M 0.84375 -2.046875 L 5.859375 -2.046875 L 5.859375 -1.375 L 0.84375 -1.375 Z M 0.84375 -2.046875 "/>
</symbol>
<symbol overflow="visible" id="glyph1-4">
<path style="stroke:none;" d="M 1 -0.671875 L 2.28125 -0.671875 L 2.28125 -5.109375 L 0.875 -4.828125 L 0.875 -5.546875 L 2.28125 -5.828125 L 3.0625 -5.828125 L 3.0625 -0.671875 L 4.359375 -0.671875 L 4.359375 0 L 1 0 Z M 1 -0.671875 "/>
</symbol>
<symbol overflow="visible" id="glyph1-5">
<path style="stroke:none;" d="M 1.53125 -0.671875 L 4.296875 -0.671875 L 4.296875 0 L 0.59375 0 L 0.59375 -0.671875 C 0.882812 -0.972656 1.289062 -1.382812 1.8125 -1.90625 C 2.332031 -2.4375 2.65625 -2.773438 2.78125 -2.921875 C 3.039062 -3.203125 3.21875 -3.441406 3.3125 -3.640625 C 3.414062 -3.835938 3.46875 -4.03125 3.46875 -4.21875 C 3.46875 -4.53125 3.359375 -4.785156 3.140625 -4.984375 C 2.921875 -5.179688 2.640625 -5.28125 2.296875 -5.28125 C 2.046875 -5.28125 1.78125 -5.234375 1.5 -5.140625 C 1.226562 -5.054688 0.9375 -4.925781 0.625 -4.75 L 0.625 -5.546875 C 0.945312 -5.679688 1.242188 -5.78125 1.515625 -5.84375 C 1.796875 -5.90625 2.050781 -5.9375 2.28125 -5.9375 C 2.882812 -5.9375 3.363281 -5.785156 3.71875 -5.484375 C 4.082031 -5.179688 4.265625 -4.78125 4.265625 -4.28125 C 4.265625 -4.039062 4.21875 -3.8125 4.125 -3.59375 C 4.03125 -3.375 3.867188 -3.117188 3.640625 -2.828125 C 3.566406 -2.753906 3.351562 -2.535156 3 -2.171875 C 2.65625 -1.816406 2.164062 -1.316406 1.53125 -0.671875 Z M 1.53125 -0.671875 "/>
</symbol>
<symbol overflow="visible" id="glyph1-6">
<path style="stroke:none;" d="M 3.25 -3.140625 C 3.625 -3.066406 3.914062 -2.898438 4.125 -2.640625 C 4.34375 -2.390625 4.453125 -2.078125 4.453125 -1.703125 C 4.453125 -1.117188 4.253906 -0.671875 3.859375 -0.359375 C 3.460938 -0.046875 2.898438 0.109375 2.171875 0.109375 C 1.921875 0.109375 1.664062 0.0820312 1.40625 0.03125 C 1.15625 -0.0078125 0.890625 -0.078125 0.609375 -0.171875 L 0.609375 -0.9375 C 0.828125 -0.8125 1.066406 -0.710938 1.328125 -0.640625 C 1.585938 -0.578125 1.859375 -0.546875 2.140625 -0.546875 C 2.640625 -0.546875 3.019531 -0.644531 3.28125 -0.84375 C 3.539062 -1.039062 3.671875 -1.328125 3.671875 -1.703125 C 3.671875 -2.046875 3.546875 -2.3125 3.296875 -2.5 C 3.054688 -2.695312 2.722656 -2.796875 2.296875 -2.796875 L 1.625 -2.796875 L 1.625 -3.4375 L 2.328125 -3.4375 C 2.710938 -3.4375 3.007812 -3.515625 3.21875 -3.671875 C 3.425781 -3.828125 3.53125 -4.050781 3.53125 -4.34375 C 3.53125 -4.644531 3.421875 -4.875 3.203125 -5.03125 C 2.992188 -5.195312 2.691406 -5.28125 2.296875 -5.28125 C 2.078125 -5.28125 1.84375 -5.253906 1.59375 -5.203125 C 1.351562 -5.160156 1.082031 -5.085938 0.78125 -4.984375 L 0.78125 -5.6875 C 1.082031 -5.769531 1.363281 -5.832031 1.625 -5.875 C 1.882812 -5.914062 2.132812 -5.9375 2.375 -5.9375 C 2.96875 -5.9375 3.4375 -5.800781 3.78125 -5.53125 C 4.132812 -5.257812 4.3125 -4.890625 4.3125 -4.421875 C 4.3125 -4.097656 4.21875 -3.828125 4.03125 -3.609375 C 3.851562 -3.390625 3.59375 -3.234375 3.25 -3.140625 Z M 3.25 -3.140625 "/>
</symbol>
<symbol overflow="visible" id="glyph1-7">
<path style="stroke:none;" d="M 0.78125 -5.828125 L 4.140625 -5.828125 L 4.140625 -5.171875 L 1.578125 -5.171875 L 1.578125 -3.453125 L 3.890625 -3.453125 L 3.890625 -2.78125 L 1.578125 -2.78125 L 1.578125 0 L 0.78125 0 Z M 0.78125 -5.828125 "/>
</symbol>
<symbol overflow="visible" id="glyph1-8">
<path style="stroke:none;" d="M 3.296875 -3.703125 C 3.210938 -3.753906 3.125 -3.789062 3.03125 -3.8125 C 2.9375 -3.832031 2.832031 -3.84375 2.71875 -3.84375 C 2.3125 -3.84375 2 -3.707031 1.78125 -3.4375 C 1.5625 -3.175781 1.453125 -2.800781 1.453125 -2.3125 L 1.453125 0 L 0.734375 0 L 0.734375 -4.375 L 1.453125 -4.375 L 1.453125 -3.703125 C 1.597656 -3.960938 1.789062 -4.15625 2.03125 -4.28125 C 2.28125 -4.414062 2.578125 -4.484375 2.921875 -4.484375 C 2.972656 -4.484375 3.023438 -4.476562 3.078125 -4.46875 C 3.140625 -4.46875 3.207031 -4.457031 3.28125 -4.4375 Z M 3.296875 -3.703125 "/>
</symbol>
<symbol overflow="visible" id="glyph1-9">
<path style="stroke:none;" d="M 3.546875 -4.25 L 3.546875 -3.5625 C 3.335938 -3.664062 3.125 -3.742188 2.90625 -3.796875 C 2.6875 -3.847656 2.460938 -3.875 2.234375 -3.875 C 1.878906 -3.875 1.609375 -3.816406 1.421875 -3.703125 C 1.242188 -3.597656 1.15625 -3.4375 1.15625 -3.21875 C 1.15625 -3.050781 1.21875 -2.921875 1.34375 -2.828125 C 1.476562 -2.734375 1.738281 -2.644531 2.125 -2.5625 L 2.375 -2.5 C 2.882812 -2.394531 3.242188 -2.242188 3.453125 -2.046875 C 3.671875 -1.847656 3.78125 -1.566406 3.78125 -1.203125 C 3.78125 -0.796875 3.617188 -0.472656 3.296875 -0.234375 C 2.972656 -0.00390625 2.53125 0.109375 1.96875 0.109375 C 1.738281 0.109375 1.492188 0.0820312 1.234375 0.03125 C 0.984375 -0.0078125 0.71875 -0.0703125 0.4375 -0.15625 L 0.4375 -0.90625 C 0.695312 -0.769531 0.957031 -0.664062 1.21875 -0.59375 C 1.476562 -0.519531 1.734375 -0.484375 1.984375 -0.484375 C 2.328125 -0.484375 2.585938 -0.539062 2.765625 -0.65625 C 2.953125 -0.78125 3.046875 -0.945312 3.046875 -1.15625 C 3.046875 -1.351562 2.976562 -1.503906 2.84375 -1.609375 C 2.707031 -1.710938 2.421875 -1.8125 1.984375 -1.90625 L 1.734375 -1.96875 C 1.285156 -2.0625 0.960938 -2.203125 0.765625 -2.390625 C 0.566406 -2.585938 0.46875 -2.851562 0.46875 -3.1875 C 0.46875 -3.601562 0.613281 -3.921875 0.90625 -4.140625 C 1.195312 -4.367188 1.609375 -4.484375 2.140625 -4.484375 C 2.410156 -4.484375 2.660156 -4.460938 2.890625 -4.421875 C 3.128906 -4.378906 3.347656 -4.320312 3.546875 -4.25 Z M 3.546875 -4.25 "/>
</symbol>
<symbol overflow="visible" id="glyph1-10">
<path style="stroke:none;" d="M 1.46875 -5.625 L 1.46875 -4.375 L 2.953125 -4.375 L 2.953125 -3.8125 L 1.46875 -3.8125 L 1.46875 -1.4375 C 1.46875 -1.082031 1.515625 -0.851562 1.609375 -0.75 C 1.710938 -0.65625 1.910156 -0.609375 2.203125 -0.609375 L 2.953125 -0.609375 L 2.953125 0 L 2.203125 0 C 1.648438 0 1.269531 -0.101562 1.0625 -0.3125 C 0.851562 -0.519531 0.75 -0.894531 0.75 -1.4375 L 0.75 -3.8125 L 0.21875 -3.8125 L 0.21875 -4.375 L 0.75 -4.375 L 0.75 -5.625 Z M 1.46875 -5.625 "/>
</symbol>
<symbol overflow="visible" id="glyph1-11">
<path style="stroke:none;" d="M 4.390625 -2.640625 L 4.390625 0 L 3.671875 0 L 3.671875 -2.625 C 3.671875 -3.03125 3.585938 -3.335938 3.421875 -3.546875 C 3.265625 -3.753906 3.023438 -3.859375 2.703125 -3.859375 C 2.316406 -3.859375 2.007812 -3.734375 1.78125 -3.484375 C 1.5625 -3.234375 1.453125 -2.894531 1.453125 -2.46875 L 1.453125 0 L 0.734375 0 L 0.734375 -6.078125 L 1.453125 -6.078125 L 1.453125 -3.703125 C 1.617188 -3.960938 1.816406 -4.15625 2.046875 -4.28125 C 2.285156 -4.414062 2.554688 -4.484375 2.859375 -4.484375 C 3.367188 -4.484375 3.75 -4.328125 4 -4.015625 C 4.257812 -3.703125 4.390625 -3.242188 4.390625 -2.640625 Z M 4.390625 -2.640625 "/>
</symbol>
<symbol overflow="visible" id="glyph1-12">
<path style="stroke:none;" d="M 4.5 -2.375 L 4.5 -2.015625 L 1.1875 -2.015625 C 1.21875 -1.523438 1.367188 -1.148438 1.640625 -0.890625 C 1.910156 -0.628906 2.28125 -0.5 2.75 -0.5 C 3.03125 -0.5 3.300781 -0.53125 3.5625 -0.59375 C 3.820312 -0.664062 4.078125 -0.769531 4.328125 -0.90625 L 4.328125 -0.21875 C 4.066406 -0.113281 3.800781 -0.0351562 3.53125 0.015625 C 3.257812 0.078125 2.988281 0.109375 2.71875 0.109375 C 2.019531 0.109375 1.460938 -0.09375 1.046875 -0.5 C 0.640625 -0.90625 0.4375 -1.453125 0.4375 -2.140625 C 0.4375 -2.859375 0.628906 -3.425781 1.015625 -3.84375 C 1.410156 -4.269531 1.9375 -4.484375 2.59375 -4.484375 C 3.175781 -4.484375 3.640625 -4.289062 3.984375 -3.90625 C 4.328125 -3.53125 4.5 -3.019531 4.5 -2.375 Z M 3.78125 -2.578125 C 3.769531 -2.972656 3.65625 -3.285156 3.4375 -3.515625 C 3.226562 -3.753906 2.945312 -3.875 2.59375 -3.875 C 2.195312 -3.875 1.875 -3.757812 1.625 -3.53125 C 1.382812 -3.300781 1.25 -2.984375 1.21875 -2.578125 Z M 3.78125 -2.578125 "/>
</symbol>
<symbol overflow="visible" id="glyph1-13">
<path style="stroke:none;" d="M 2.75 -2.203125 C 2.164062 -2.203125 1.757812 -2.132812 1.53125 -2 C 1.3125 -1.863281 1.203125 -1.640625 1.203125 -1.328125 C 1.203125 -1.066406 1.285156 -0.859375 1.453125 -0.703125 C 1.617188 -0.554688 1.847656 -0.484375 2.140625 -0.484375 C 2.535156 -0.484375 2.851562 -0.625 3.09375 -0.90625 C 3.332031 -1.195312 3.453125 -1.578125 3.453125 -2.046875 L 3.453125 -2.203125 Z M 4.171875 -2.5 L 4.171875 0 L 3.453125 0 L 3.453125 -0.671875 C 3.296875 -0.398438 3.09375 -0.203125 2.84375 -0.078125 C 2.601562 0.046875 2.304688 0.109375 1.953125 0.109375 C 1.503906 0.109375 1.144531 -0.015625 0.875 -0.265625 C 0.613281 -0.515625 0.484375 -0.851562 0.484375 -1.28125 C 0.484375 -1.769531 0.644531 -2.140625 0.96875 -2.390625 C 1.300781 -2.640625 1.796875 -2.765625 2.453125 -2.765625 L 3.453125 -2.765625 L 3.453125 -2.828125 C 3.453125 -3.160156 3.34375 -3.414062 3.125 -3.59375 C 2.914062 -3.78125 2.613281 -3.875 2.21875 -3.875 C 1.96875 -3.875 1.722656 -3.84375 1.484375 -3.78125 C 1.242188 -3.71875 1.015625 -3.628906 0.796875 -3.515625 L 0.796875 -4.171875 C 1.066406 -4.273438 1.320312 -4.351562 1.5625 -4.40625 C 1.8125 -4.457031 2.054688 -4.484375 2.296875 -4.484375 C 2.921875 -4.484375 3.390625 -4.316406 3.703125 -3.984375 C 4.015625 -3.660156 4.171875 -3.164062 4.171875 -2.5 Z M 4.171875 -2.5 "/>
</symbol>
<symbol overflow="visible" id="glyph1-14">
<path style="stroke:none;" d="M 3.640625 -3.71875 L 3.640625 -6.078125 L 4.359375 -6.078125 L 4.359375 0 L 3.640625 0 L 3.640625 -0.65625 C 3.484375 -0.394531 3.289062 -0.203125 3.0625 -0.078125 C 2.832031 0.046875 2.554688 0.109375 2.234375 0.109375 C 1.703125 0.109375 1.269531 -0.0976562 0.9375 -0.515625 C 0.601562 -0.941406 0.4375 -1.5 0.4375 -2.1875 C 0.4375 -2.875 0.601562 -3.425781 0.9375 -3.84375 C 1.269531 -4.269531 1.703125 -4.484375 2.234375 -4.484375 C 2.554688 -4.484375 2.832031 -4.421875 3.0625 -4.296875 C 3.289062 -4.171875 3.484375 -3.976562 3.640625 -3.71875 Z M 1.1875 -2.1875 C 1.1875 -1.65625 1.296875 -1.238281 1.515625 -0.9375 C 1.734375 -0.632812 2.03125 -0.484375 2.40625 -0.484375 C 2.789062 -0.484375 3.09375 -0.632812 3.3125 -0.9375 C 3.53125 -1.238281 3.640625 -1.65625 3.640625 -2.1875 C 3.640625 -2.71875 3.53125 -3.128906 3.3125 -3.421875 C 3.09375 -3.722656 2.789062 -3.875 2.40625 -3.875 C 2.03125 -3.875 1.734375 -3.722656 1.515625 -3.421875 C 1.296875 -3.128906 1.1875 -2.71875 1.1875 -2.1875 Z M 1.1875 -2.1875 "/>
</symbol>
<symbol overflow="visible" id="glyph1-15">
<path style="stroke:none;" d="M 2.96875 -6.078125 L 2.96875 -5.484375 L 2.28125 -5.484375 C 2.019531 -5.484375 1.835938 -5.429688 1.734375 -5.328125 C 1.640625 -5.222656 1.59375 -5.035156 1.59375 -4.765625 L 1.59375 -4.375 L 2.78125 -4.375 L 2.78125 -3.8125 L 1.59375 -3.8125 L 1.59375 0 L 0.875 0 L 0.875 -3.8125 L 0.1875 -3.8125 L 0.1875 -4.375 L 0.875 -4.375 L 0.875 -4.6875 C 0.875 -5.164062 0.984375 -5.515625 1.203125 -5.734375 C 1.429688 -5.960938 1.796875 -6.078125 2.296875 -6.078125 Z M 2.96875 -6.078125 "/>
</symbol>
<symbol overflow="visible" id="glyph1-16">
<path style="stroke:none;" d="M 2.453125 -3.875 C 2.066406 -3.875 1.757812 -3.722656 1.53125 -3.421875 C 1.3125 -3.117188 1.203125 -2.707031 1.203125 -2.1875 C 1.203125 -1.664062 1.3125 -1.253906 1.53125 -0.953125 C 1.757812 -0.648438 2.066406 -0.5 2.453125 -0.5 C 2.835938 -0.5 3.140625 -0.648438 3.359375 -0.953125 C 3.585938 -1.253906 3.703125 -1.664062 3.703125 -2.1875 C 3.703125 -2.707031 3.585938 -3.117188 3.359375 -3.421875 C 3.140625 -3.722656 2.835938 -3.875 2.453125 -3.875 Z M 2.453125 -4.484375 C 3.078125 -4.484375 3.566406 -4.28125 3.921875 -3.875 C 4.273438 -3.46875 4.453125 -2.90625 4.453125 -2.1875 C 4.453125 -1.46875 4.273438 -0.90625 3.921875 -0.5 C 3.566406 -0.09375 3.078125 0.109375 2.453125 0.109375 C 1.828125 0.109375 1.332031 -0.09375 0.96875 -0.5 C 0.613281 -0.90625 0.4375 -1.46875 0.4375 -2.1875 C 0.4375 -2.90625 0.613281 -3.46875 0.96875 -3.875 C 1.332031 -4.28125 1.828125 -4.484375 2.453125 -4.484375 Z M 2.453125 -4.484375 "/> <path style="stroke:none;" d="M 2.453125 -3.875 C 2.066406 -3.875 1.757812 -3.722656 1.53125 -3.421875 C 1.3125 -3.117188 1.203125 -2.707031 1.203125 -2.1875 C 1.203125 -1.664062 1.3125 -1.253906 1.53125 -0.953125 C 1.757812 -0.648438 2.066406 -0.5 2.453125 -0.5 C 2.835938 -0.5 3.140625 -0.648438 3.359375 -0.953125 C 3.585938 -1.253906 3.703125 -1.664062 3.703125 -2.1875 C 3.703125 -2.707031 3.585938 -3.117188 3.359375 -3.421875 C 3.140625 -3.722656 2.835938 -3.875 2.453125 -3.875 Z M 2.453125 -4.484375 C 3.078125 -4.484375 3.566406 -4.28125 3.921875 -3.875 C 4.273438 -3.46875 4.453125 -2.90625 4.453125 -2.1875 C 4.453125 -1.46875 4.273438 -0.90625 3.921875 -0.5 C 3.566406 -0.09375 3.078125 0.109375 2.453125 0.109375 C 1.828125 0.109375 1.332031 -0.09375 0.96875 -0.5 C 0.613281 -0.90625 0.4375 -1.46875 0.4375 -2.1875 C 0.4375 -2.90625 0.613281 -3.46875 0.96875 -3.875 C 1.332031 -4.28125 1.828125 -4.484375 2.453125 -4.484375 Z M 2.453125 -4.484375 "/>
</symbol> </symbol>
<symbol overflow="visible" id="glyph1-17"> <symbol overflow="visible" id="glyph1-3">
<path style="stroke:none;" d="M 0.734375 -6.078125 L 1.453125 -6.078125 L 1.453125 -2.484375 L 3.59375 -4.375 L 4.515625 -4.375 L 2.1875 -2.328125 L 4.609375 0 L 3.671875 0 L 1.453125 -2.140625 L 1.453125 0 L 0.734375 0 Z M 0.734375 -6.078125 "/> <path style="stroke:none;" d="M 3.890625 -2.1875 C 3.890625 -2.71875 3.78125 -3.128906 3.5625 -3.421875 C 3.351562 -3.722656 3.054688 -3.875 2.671875 -3.875 C 2.296875 -3.875 2 -3.722656 1.78125 -3.421875 C 1.5625 -3.128906 1.453125 -2.71875 1.453125 -2.1875 C 1.453125 -1.65625 1.5625 -1.238281 1.78125 -0.9375 C 2 -0.632812 2.296875 -0.484375 2.671875 -0.484375 C 3.054688 -0.484375 3.351562 -0.632812 3.5625 -0.9375 C 3.78125 -1.238281 3.890625 -1.65625 3.890625 -2.1875 Z M 1.453125 -3.71875 C 1.597656 -3.976562 1.785156 -4.171875 2.015625 -4.296875 C 2.253906 -4.421875 2.53125 -4.484375 2.84375 -4.484375 C 3.375 -4.484375 3.804688 -4.269531 4.140625 -3.84375 C 4.472656 -3.425781 4.640625 -2.875 4.640625 -2.1875 C 4.640625 -1.5 4.472656 -0.941406 4.140625 -0.515625 C 3.804688 -0.0976562 3.375 0.109375 2.84375 0.109375 C 2.53125 0.109375 2.253906 0.046875 2.015625 -0.078125 C 1.785156 -0.203125 1.597656 -0.394531 1.453125 -0.65625 L 1.453125 0 L 0.734375 0 L 0.734375 -6.078125 L 1.453125 -6.078125 Z M 1.453125 -3.71875 "/>
</symbol> </symbol>
<symbol overflow="visible" id="glyph1-18"> <symbol overflow="visible" id="glyph1-4">
<path style="stroke:none;" d="M 0.75 -4.375 L 1.46875 -4.375 L 1.46875 0.078125 C 1.46875 0.640625 1.363281 1.046875 1.15625 1.296875 C 0.945312 1.546875 0.601562 1.671875 0.125 1.671875 L -0.140625 1.671875 L -0.140625 1.0625 L 0.046875 1.0625 C 0.316406 1.0625 0.5 0.992188 0.59375 0.859375 C 0.695312 0.734375 0.75 0.472656 0.75 0.078125 Z M 0.75 -6.078125 L 1.46875 -6.078125 L 1.46875 -5.171875 L 0.75 -5.171875 Z M 0.75 -6.078125 "/> <path style="stroke:none;" d=""/>
</symbol> </symbol>
<symbol overflow="visible" id="glyph1-19"> <symbol overflow="visible" id="glyph1-5">
<path style="stroke:none;" d="M 4.28125 -5.640625 L 4.28125 -4.875 C 3.976562 -5.019531 3.691406 -5.125 3.421875 -5.1875 C 3.160156 -5.257812 2.910156 -5.296875 2.671875 -5.296875 C 2.234375 -5.296875 1.898438 -5.210938 1.671875 -5.046875 C 1.441406 -4.878906 1.328125 -4.644531 1.328125 -4.34375 C 1.328125 -4.082031 1.398438 -3.882812 1.546875 -3.75 C 1.703125 -3.625 2 -3.519531 2.4375 -3.4375 L 2.90625 -3.34375 C 3.5 -3.226562 3.9375 -3.03125 4.21875 -2.75 C 4.5 -2.46875 4.640625 -2.085938 4.640625 -1.609375 C 4.640625 -1.046875 4.445312 -0.617188 4.0625 -0.328125 C 3.6875 -0.0351562 3.132812 0.109375 2.40625 0.109375 C 2.125 0.109375 1.828125 0.078125 1.515625 0.015625 C 1.203125 -0.046875 0.878906 -0.140625 0.546875 -0.265625 L 0.546875 -1.078125 C 0.867188 -0.890625 1.179688 -0.75 1.484375 -0.65625 C 1.796875 -0.570312 2.101562 -0.53125 2.40625 -0.53125 C 2.851562 -0.53125 3.195312 -0.617188 3.4375 -0.796875 C 3.6875 -0.972656 3.8125 -1.222656 3.8125 -1.546875 C 3.8125 -1.835938 3.722656 -2.0625 3.546875 -2.21875 C 3.367188 -2.382812 3.082031 -2.507812 2.6875 -2.59375 L 2.203125 -2.6875 C 1.609375 -2.800781 1.179688 -2.984375 0.921875 -3.234375 C 0.660156 -3.484375 0.53125 -3.832031 0.53125 -4.28125 C 0.53125 -4.789062 0.710938 -5.191406 1.078125 -5.484375 C 1.441406 -5.785156 1.941406 -5.9375 2.578125 -5.9375 C 2.847656 -5.9375 3.125 -5.910156 3.40625 -5.859375 C 3.695312 -5.816406 3.988281 -5.742188 4.28125 -5.640625 Z M 4.28125 -5.640625 "/> <path style="stroke:none;" d="M 1 -0.671875 L 2.28125 -0.671875 L 2.28125 -5.109375 L 0.875 -4.828125 L 0.875 -5.546875 L 2.28125 -5.828125 L 3.0625 -5.828125 L 3.0625 -0.671875 L 4.359375 -0.671875 L 4.359375 0 L 1 0 Z M 1 -0.671875 "/>
</symbol> </symbol>
<symbol overflow="visible" id="glyph1-20"> <symbol overflow="visible" id="glyph1-6">
<path style="stroke:none;" d="M 3.90625 -4.203125 L 3.90625 -3.53125 C 3.695312 -3.644531 3.488281 -3.726562 3.28125 -3.78125 C 3.082031 -3.84375 2.878906 -3.875 2.671875 -3.875 C 2.203125 -3.875 1.835938 -3.722656 1.578125 -3.421875 C 1.328125 -3.128906 1.203125 -2.71875 1.203125 -2.1875 C 1.203125 -1.65625 1.328125 -1.238281 1.578125 -0.9375 C 1.835938 -0.644531 2.203125 -0.5 2.671875 -0.5 C 2.878906 -0.5 3.082031 -0.523438 3.28125 -0.578125 C 3.488281 -0.640625 3.695312 -0.722656 3.90625 -0.828125 L 3.90625 -0.171875 C 3.707031 -0.078125 3.5 -0.0078125 3.28125 0.03125 C 3.0625 0.0820312 2.832031 0.109375 2.59375 0.109375 C 1.9375 0.109375 1.410156 -0.09375 1.015625 -0.5 C 0.628906 -0.914062 0.4375 -1.476562 0.4375 -2.1875 C 0.4375 -2.894531 0.632812 -3.453125 1.03125 -3.859375 C 1.425781 -4.273438 1.960938 -4.484375 2.640625 -4.484375 C 2.859375 -4.484375 3.070312 -4.457031 3.28125 -4.40625 C 3.5 -4.363281 3.707031 -4.296875 3.90625 -4.203125 Z M 3.90625 -4.203125 "/> <path style="stroke:none;" d="M 3.546875 -4.25 L 3.546875 -3.5625 C 3.335938 -3.664062 3.125 -3.742188 2.90625 -3.796875 C 2.6875 -3.847656 2.460938 -3.875 2.234375 -3.875 C 1.878906 -3.875 1.609375 -3.816406 1.421875 -3.703125 C 1.242188 -3.597656 1.15625 -3.4375 1.15625 -3.21875 C 1.15625 -3.050781 1.21875 -2.921875 1.34375 -2.828125 C 1.476562 -2.734375 1.738281 -2.644531 2.125 -2.5625 L 2.375 -2.5 C 2.882812 -2.394531 3.242188 -2.242188 3.453125 -2.046875 C 3.671875 -1.847656 3.78125 -1.566406 3.78125 -1.203125 C 3.78125 -0.796875 3.617188 -0.472656 3.296875 -0.234375 C 2.972656 -0.00390625 2.53125 0.109375 1.96875 0.109375 C 1.738281 0.109375 1.492188 0.0820312 1.234375 0.03125 C 0.984375 -0.0078125 0.71875 -0.0703125 0.4375 -0.15625 L 0.4375 -0.90625 C 0.695312 -0.769531 0.957031 -0.664062 1.21875 -0.59375 C 1.476562 -0.519531 1.734375 -0.484375 1.984375 -0.484375 C 2.328125 -0.484375 2.585938 -0.539062 2.765625 -0.65625 C 2.953125 -0.78125 3.046875 -0.945312 3.046875 -1.15625 C 3.046875 -1.351562 2.976562 -1.503906 2.84375 -1.609375 C 2.707031 -1.710938 2.421875 -1.8125 1.984375 -1.90625 L 1.734375 -1.96875 C 1.285156 -2.0625 0.960938 -2.203125 0.765625 -2.390625 C 0.566406 -2.585938 0.46875 -2.851562 0.46875 -3.1875 C 0.46875 -3.601562 0.613281 -3.921875 0.90625 -4.140625 C 1.195312 -4.367188 1.609375 -4.484375 2.140625 -4.484375 C 2.410156 -4.484375 2.660156 -4.460938 2.890625 -4.421875 C 3.128906 -4.378906 3.347656 -4.320312 3.546875 -4.25 Z M 3.546875 -4.25 "/>
</symbol> </symbol>
<symbol overflow="visible" id="glyph1-21"> <symbol overflow="visible" id="glyph1-7">
<path style="stroke:none;" d="M 1.46875 -5.625 L 1.46875 -4.375 L 2.953125 -4.375 L 2.953125 -3.8125 L 1.46875 -3.8125 L 1.46875 -1.4375 C 1.46875 -1.082031 1.515625 -0.851562 1.609375 -0.75 C 1.710938 -0.65625 1.910156 -0.609375 2.203125 -0.609375 L 2.953125 -0.609375 L 2.953125 0 L 2.203125 0 C 1.648438 0 1.269531 -0.101562 1.0625 -0.3125 C 0.851562 -0.519531 0.75 -0.894531 0.75 -1.4375 L 0.75 -3.8125 L 0.21875 -3.8125 L 0.21875 -4.375 L 0.75 -4.375 L 0.75 -5.625 Z M 1.46875 -5.625 "/>
</symbol>
<symbol overflow="visible" id="glyph1-8">
<path style="stroke:none;" d="M 2.75 -2.203125 C 2.164062 -2.203125 1.757812 -2.132812 1.53125 -2 C 1.3125 -1.863281 1.203125 -1.640625 1.203125 -1.328125 C 1.203125 -1.066406 1.285156 -0.859375 1.453125 -0.703125 C 1.617188 -0.554688 1.847656 -0.484375 2.140625 -0.484375 C 2.535156 -0.484375 2.851562 -0.625 3.09375 -0.90625 C 3.332031 -1.195312 3.453125 -1.578125 3.453125 -2.046875 L 3.453125 -2.203125 Z M 4.171875 -2.5 L 4.171875 0 L 3.453125 0 L 3.453125 -0.671875 C 3.296875 -0.398438 3.09375 -0.203125 2.84375 -0.078125 C 2.601562 0.046875 2.304688 0.109375 1.953125 0.109375 C 1.503906 0.109375 1.144531 -0.015625 0.875 -0.265625 C 0.613281 -0.515625 0.484375 -0.851562 0.484375 -1.28125 C 0.484375 -1.769531 0.644531 -2.140625 0.96875 -2.390625 C 1.300781 -2.640625 1.796875 -2.765625 2.453125 -2.765625 L 3.453125 -2.765625 L 3.453125 -2.828125 C 3.453125 -3.160156 3.34375 -3.414062 3.125 -3.59375 C 2.914062 -3.78125 2.613281 -3.875 2.21875 -3.875 C 1.96875 -3.875 1.722656 -3.84375 1.484375 -3.78125 C 1.242188 -3.71875 1.015625 -3.628906 0.796875 -3.515625 L 0.796875 -4.171875 C 1.066406 -4.273438 1.320312 -4.351562 1.5625 -4.40625 C 1.8125 -4.457031 2.054688 -4.484375 2.296875 -4.484375 C 2.921875 -4.484375 3.390625 -4.316406 3.703125 -3.984375 C 4.015625 -3.660156 4.171875 -3.164062 4.171875 -2.5 Z M 4.171875 -2.5 "/>
</symbol>
<symbol overflow="visible" id="glyph1-9">
<path style="stroke:none;" d="M 3.296875 -3.703125 C 3.210938 -3.753906 3.125 -3.789062 3.03125 -3.8125 C 2.9375 -3.832031 2.832031 -3.84375 2.71875 -3.84375 C 2.3125 -3.84375 2 -3.707031 1.78125 -3.4375 C 1.5625 -3.175781 1.453125 -2.800781 1.453125 -2.3125 L 1.453125 0 L 0.734375 0 L 0.734375 -4.375 L 1.453125 -4.375 L 1.453125 -3.703125 C 1.597656 -3.960938 1.789062 -4.15625 2.03125 -4.28125 C 2.28125 -4.414062 2.578125 -4.484375 2.921875 -4.484375 C 2.972656 -4.484375 3.023438 -4.476562 3.078125 -4.46875 C 3.140625 -4.46875 3.207031 -4.457031 3.28125 -4.4375 Z M 3.296875 -3.703125 "/>
</symbol>
<symbol overflow="visible" id="glyph1-10">
<path style="stroke:none;" d="M 0.75 -4.375 L 1.46875 -4.375 L 1.46875 0 L 0.75 0 Z M 0.75 -6.078125 L 1.46875 -6.078125 L 1.46875 -5.171875 L 0.75 -5.171875 Z M 0.75 -6.078125 "/>
</symbol>
<symbol overflow="visible" id="glyph1-11">
<path style="stroke:none;" d="M 4.390625 -2.640625 L 4.390625 0 L 3.671875 0 L 3.671875 -2.625 C 3.671875 -3.03125 3.585938 -3.335938 3.421875 -3.546875 C 3.265625 -3.753906 3.023438 -3.859375 2.703125 -3.859375 C 2.316406 -3.859375 2.007812 -3.734375 1.78125 -3.484375 C 1.5625 -3.234375 1.453125 -2.894531 1.453125 -2.46875 L 1.453125 0 L 0.734375 0 L 0.734375 -4.375 L 1.453125 -4.375 L 1.453125 -3.703125 C 1.617188 -3.960938 1.816406 -4.15625 2.046875 -4.28125 C 2.285156 -4.414062 2.554688 -4.484375 2.859375 -4.484375 C 3.367188 -4.484375 3.75 -4.328125 4 -4.015625 C 4.257812 -3.703125 4.390625 -3.242188 4.390625 -2.640625 Z M 4.390625 -2.640625 "/> <path style="stroke:none;" d="M 4.390625 -2.640625 L 4.390625 0 L 3.671875 0 L 3.671875 -2.625 C 3.671875 -3.03125 3.585938 -3.335938 3.421875 -3.546875 C 3.265625 -3.753906 3.023438 -3.859375 2.703125 -3.859375 C 2.316406 -3.859375 2.007812 -3.734375 1.78125 -3.484375 C 1.5625 -3.234375 1.453125 -2.894531 1.453125 -2.46875 L 1.453125 0 L 0.734375 0 L 0.734375 -4.375 L 1.453125 -4.375 L 1.453125 -3.703125 C 1.617188 -3.960938 1.816406 -4.15625 2.046875 -4.28125 C 2.285156 -4.414062 2.554688 -4.484375 2.859375 -4.484375 C 3.367188 -4.484375 3.75 -4.328125 4 -4.015625 C 4.257812 -3.703125 4.390625 -3.242188 4.390625 -2.640625 Z M 4.390625 -2.640625 "/>
</symbol> </symbol>
<symbol overflow="visible" id="glyph1-22"> <symbol overflow="visible" id="glyph1-12">
<path style="stroke:none;" d="M 0.9375 -4.140625 L 1.765625 -4.140625 L 1.765625 -3.140625 L 0.9375 -3.140625 Z M 0.9375 -1 L 1.765625 -1 L 1.765625 -0.328125 L 1.125 0.9375 L 0.625 0.9375 L 0.9375 -0.328125 Z M 0.9375 -1 "/> <path style="stroke:none;" d="M 3.640625 -2.234375 C 3.640625 -2.753906 3.53125 -3.15625 3.3125 -3.4375 C 3.09375 -3.726562 2.789062 -3.875 2.40625 -3.875 C 2.019531 -3.875 1.71875 -3.726562 1.5 -3.4375 C 1.289062 -3.15625 1.1875 -2.753906 1.1875 -2.234375 C 1.1875 -1.722656 1.289062 -1.320312 1.5 -1.03125 C 1.71875 -0.75 2.019531 -0.609375 2.40625 -0.609375 C 2.789062 -0.609375 3.09375 -0.75 3.3125 -1.03125 C 3.53125 -1.320312 3.640625 -1.722656 3.640625 -2.234375 Z M 4.359375 -0.546875 C 4.359375 0.203125 4.191406 0.757812 3.859375 1.125 C 3.523438 1.488281 3.019531 1.671875 2.34375 1.671875 C 2.082031 1.671875 1.835938 1.648438 1.609375 1.609375 C 1.390625 1.566406 1.175781 1.507812 0.96875 1.4375 L 0.96875 0.734375 C 1.175781 0.847656 1.382812 0.929688 1.59375 0.984375 C 1.800781 1.046875 2.007812 1.078125 2.21875 1.078125 C 2.695312 1.078125 3.050781 0.953125 3.28125 0.703125 C 3.519531 0.453125 3.640625 0.078125 3.640625 -0.421875 L 3.640625 -0.765625 C 3.484375 -0.515625 3.289062 -0.320312 3.0625 -0.1875 C 2.832031 -0.0625 2.554688 0 2.234375 0 C 1.691406 0 1.253906 -0.203125 0.921875 -0.609375 C 0.597656 -1.023438 0.4375 -1.566406 0.4375 -2.234375 C 0.4375 -2.910156 0.597656 -3.453125 0.921875 -3.859375 C 1.253906 -4.273438 1.691406 -4.484375 2.234375 -4.484375 C 2.554688 -4.484375 2.832031 -4.414062 3.0625 -4.28125 C 3.289062 -4.15625 3.484375 -3.96875 3.640625 -3.71875 L 3.640625 -4.375 L 4.359375 -4.375 Z M 4.359375 -0.546875 "/>
</symbol> </symbol>
<symbol overflow="visible" id="glyph1-23"> <symbol overflow="visible" id="glyph1-13">
<path style="stroke:none;" d="M 1.453125 -0.65625 L 1.453125 1.671875 L 0.734375 1.671875 L 0.734375 -4.375 L 1.453125 -4.375 L 1.453125 -3.71875 C 1.597656 -3.976562 1.785156 -4.171875 2.015625 -4.296875 C 2.253906 -4.421875 2.53125 -4.484375 2.84375 -4.484375 C 3.375 -4.484375 3.804688 -4.269531 4.140625 -3.84375 C 4.472656 -3.425781 4.640625 -2.875 4.640625 -2.1875 C 4.640625 -1.5 4.472656 -0.941406 4.140625 -0.515625 C 3.804688 -0.0976562 3.375 0.109375 2.84375 0.109375 C 2.53125 0.109375 2.253906 0.046875 2.015625 -0.078125 C 1.785156 -0.203125 1.597656 -0.394531 1.453125 -0.65625 Z M 3.890625 -2.1875 C 3.890625 -2.71875 3.78125 -3.128906 3.5625 -3.421875 C 3.351562 -3.722656 3.054688 -3.875 2.671875 -3.875 C 2.296875 -3.875 2 -3.722656 1.78125 -3.421875 C 1.5625 -3.128906 1.453125 -2.71875 1.453125 -2.1875 C 1.453125 -1.65625 1.5625 -1.238281 1.78125 -0.9375 C 2 -0.632812 2.296875 -0.484375 2.671875 -0.484375 C 3.054688 -0.484375 3.351562 -0.632812 3.5625 -0.9375 C 3.78125 -1.238281 3.890625 -1.65625 3.890625 -2.1875 Z M 3.890625 -2.1875 "/> <path style="stroke:none;" d="M 3.640625 -3.71875 L 3.640625 -6.078125 L 4.359375 -6.078125 L 4.359375 0 L 3.640625 0 L 3.640625 -0.65625 C 3.484375 -0.394531 3.289062 -0.203125 3.0625 -0.078125 C 2.832031 0.046875 2.554688 0.109375 2.234375 0.109375 C 1.703125 0.109375 1.269531 -0.0976562 0.9375 -0.515625 C 0.601562 -0.941406 0.4375 -1.5 0.4375 -2.1875 C 0.4375 -2.875 0.601562 -3.425781 0.9375 -3.84375 C 1.269531 -4.269531 1.703125 -4.484375 2.234375 -4.484375 C 2.554688 -4.484375 2.832031 -4.421875 3.0625 -4.296875 C 3.289062 -4.171875 3.484375 -3.976562 3.640625 -3.71875 Z M 1.1875 -2.1875 C 1.1875 -1.65625 1.296875 -1.238281 1.515625 -0.9375 C 1.734375 -0.632812 2.03125 -0.484375 2.40625 -0.484375 C 2.789062 -0.484375 3.09375 -0.632812 3.3125 -0.9375 C 3.53125 -1.238281 3.640625 -1.65625 3.640625 -2.1875 C 3.640625 -2.71875 3.53125 -3.128906 3.3125 -3.421875 C 3.09375 -3.722656 2.789062 -3.875 2.40625 -3.875 C 2.03125 -3.875 1.734375 -3.722656 1.515625 -3.421875 C 1.296875 -3.128906 1.1875 -2.71875 1.1875 -2.1875 Z M 1.1875 -2.1875 "/>
</symbol> </symbol>
<symbol overflow="visible" id="glyph1-24"> <symbol overflow="visible" id="glyph1-14">
<path style="stroke:none;" d="M 0.390625 -2.515625 L 2.5 -2.515625 L 2.5 -1.875 L 0.390625 -1.875 Z M 0.390625 -2.515625 "/> <path style="stroke:none;" d="M 4.5 -2.375 L 4.5 -2.015625 L 1.1875 -2.015625 C 1.21875 -1.523438 1.367188 -1.148438 1.640625 -0.890625 C 1.910156 -0.628906 2.28125 -0.5 2.75 -0.5 C 3.03125 -0.5 3.300781 -0.53125 3.5625 -0.59375 C 3.820312 -0.664062 4.078125 -0.769531 4.328125 -0.90625 L 4.328125 -0.21875 C 4.066406 -0.113281 3.800781 -0.0351562 3.53125 0.015625 C 3.257812 0.078125 2.988281 0.109375 2.71875 0.109375 C 2.019531 0.109375 1.460938 -0.09375 1.046875 -0.5 C 0.640625 -0.90625 0.4375 -1.453125 0.4375 -2.140625 C 0.4375 -2.859375 0.628906 -3.425781 1.015625 -3.84375 C 1.410156 -4.269531 1.9375 -4.484375 2.59375 -4.484375 C 3.175781 -4.484375 3.640625 -4.289062 3.984375 -3.90625 C 4.328125 -3.53125 4.5 -3.019531 4.5 -2.375 Z M 3.78125 -2.578125 C 3.769531 -2.972656 3.65625 -3.285156 3.4375 -3.515625 C 3.226562 -3.753906 2.945312 -3.875 2.59375 -3.875 C 2.195312 -3.875 1.875 -3.757812 1.625 -3.53125 C 1.382812 -3.300781 1.25 -2.984375 1.21875 -2.578125 Z M 3.78125 -2.578125 "/>
</symbol> </symbol>
<symbol overflow="visible" id="glyph1-25"> <symbol overflow="visible" id="glyph1-15">
<path style="stroke:none;" d="M 1.53125 -0.671875 L 4.296875 -0.671875 L 4.296875 0 L 0.59375 0 L 0.59375 -0.671875 C 0.882812 -0.972656 1.289062 -1.382812 1.8125 -1.90625 C 2.332031 -2.4375 2.65625 -2.773438 2.78125 -2.921875 C 3.039062 -3.203125 3.21875 -3.441406 3.3125 -3.640625 C 3.414062 -3.835938 3.46875 -4.03125 3.46875 -4.21875 C 3.46875 -4.53125 3.359375 -4.785156 3.140625 -4.984375 C 2.921875 -5.179688 2.640625 -5.28125 2.296875 -5.28125 C 2.046875 -5.28125 1.78125 -5.234375 1.5 -5.140625 C 1.226562 -5.054688 0.9375 -4.925781 0.625 -4.75 L 0.625 -5.546875 C 0.945312 -5.679688 1.242188 -5.78125 1.515625 -5.84375 C 1.796875 -5.90625 2.050781 -5.9375 2.28125 -5.9375 C 2.882812 -5.9375 3.363281 -5.785156 3.71875 -5.484375 C 4.082031 -5.179688 4.265625 -4.78125 4.265625 -4.28125 C 4.265625 -4.039062 4.21875 -3.8125 4.125 -3.59375 C 4.03125 -3.375 3.867188 -3.117188 3.640625 -2.828125 C 3.566406 -2.753906 3.351562 -2.535156 3 -2.171875 C 2.65625 -1.816406 2.164062 -1.316406 1.53125 -0.671875 Z M 1.53125 -0.671875 "/>
</symbol>
<symbol overflow="visible" id="glyph1-16">
<path style="stroke:none;" d="M 3.25 -3.140625 C 3.625 -3.066406 3.914062 -2.898438 4.125 -2.640625 C 4.34375 -2.390625 4.453125 -2.078125 4.453125 -1.703125 C 4.453125 -1.117188 4.253906 -0.671875 3.859375 -0.359375 C 3.460938 -0.046875 2.898438 0.109375 2.171875 0.109375 C 1.921875 0.109375 1.664062 0.0820312 1.40625 0.03125 C 1.15625 -0.0078125 0.890625 -0.078125 0.609375 -0.171875 L 0.609375 -0.9375 C 0.828125 -0.8125 1.066406 -0.710938 1.328125 -0.640625 C 1.585938 -0.578125 1.859375 -0.546875 2.140625 -0.546875 C 2.640625 -0.546875 3.019531 -0.644531 3.28125 -0.84375 C 3.539062 -1.039062 3.671875 -1.328125 3.671875 -1.703125 C 3.671875 -2.046875 3.546875 -2.3125 3.296875 -2.5 C 3.054688 -2.695312 2.722656 -2.796875 2.296875 -2.796875 L 1.625 -2.796875 L 1.625 -3.4375 L 2.328125 -3.4375 C 2.710938 -3.4375 3.007812 -3.515625 3.21875 -3.671875 C 3.425781 -3.828125 3.53125 -4.050781 3.53125 -4.34375 C 3.53125 -4.644531 3.421875 -4.875 3.203125 -5.03125 C 2.992188 -5.195312 2.691406 -5.28125 2.296875 -5.28125 C 2.078125 -5.28125 1.84375 -5.253906 1.59375 -5.203125 C 1.351562 -5.160156 1.082031 -5.085938 0.78125 -4.984375 L 0.78125 -5.6875 C 1.082031 -5.769531 1.363281 -5.832031 1.625 -5.875 C 1.882812 -5.914062 2.132812 -5.9375 2.375 -5.9375 C 2.96875 -5.9375 3.4375 -5.800781 3.78125 -5.53125 C 4.132812 -5.257812 4.3125 -4.890625 4.3125 -4.421875 C 4.3125 -4.097656 4.21875 -3.828125 4.03125 -3.609375 C 3.851562 -3.390625 3.59375 -3.234375 3.25 -3.140625 Z M 3.25 -3.140625 "/>
</symbol>
<symbol overflow="visible" id="glyph1-17">
<path style="stroke:none;" d="M 2.734375 -5.0625 L 1.671875 -2.15625 L 3.8125 -2.15625 Z M 2.296875 -5.828125 L 3.1875 -5.828125 L 5.40625 0 L 4.59375 0 L 4.0625 -1.5 L 1.421875 -1.5 L 0.890625 0 L 0.0625 0 Z M 2.296875 -5.828125 "/>
</symbol>
<symbol overflow="visible" id="glyph1-18">
<path style="stroke:none;" d="M 0.75 -6.078125 L 1.46875 -6.078125 L 1.46875 0 L 0.75 0 Z M 0.75 -6.078125 "/> <path style="stroke:none;" d="M 0.75 -6.078125 L 1.46875 -6.078125 L 1.46875 0 L 0.75 0 Z M 0.75 -6.078125 "/>
</symbol> </symbol>
<symbol overflow="visible" id="glyph1-26"> <symbol overflow="visible" id="glyph1-19">
<path style="stroke:none;" d="M 0.234375 -4.375 L 1 -4.375 L 2.375 -0.703125 L 3.734375 -4.375 L 4.5 -4.375 L 2.859375 0 L 1.875 0 Z M 0.234375 -4.375 "/> <path style="stroke:none;" d="M 3.90625 -4.203125 L 3.90625 -3.53125 C 3.695312 -3.644531 3.488281 -3.726562 3.28125 -3.78125 C 3.082031 -3.84375 2.878906 -3.875 2.671875 -3.875 C 2.203125 -3.875 1.835938 -3.722656 1.578125 -3.421875 C 1.328125 -3.128906 1.203125 -2.71875 1.203125 -2.1875 C 1.203125 -1.65625 1.328125 -1.238281 1.578125 -0.9375 C 1.835938 -0.644531 2.203125 -0.5 2.671875 -0.5 C 2.878906 -0.5 3.082031 -0.523438 3.28125 -0.578125 C 3.488281 -0.640625 3.695312 -0.722656 3.90625 -0.828125 L 3.90625 -0.171875 C 3.707031 -0.078125 3.5 -0.0078125 3.28125 0.03125 C 3.0625 0.0820312 2.832031 0.109375 2.59375 0.109375 C 1.9375 0.109375 1.410156 -0.09375 1.015625 -0.5 C 0.628906 -0.914062 0.4375 -1.476562 0.4375 -2.1875 C 0.4375 -2.894531 0.632812 -3.453125 1.03125 -3.859375 C 1.425781 -4.273438 1.960938 -4.484375 2.640625 -4.484375 C 2.859375 -4.484375 3.070312 -4.457031 3.28125 -4.40625 C 3.5 -4.363281 3.707031 -4.296875 3.90625 -4.203125 Z M 3.90625 -4.203125 "/>
</symbol> </symbol>
<symbol overflow="visible" id="glyph1-27"> <symbol overflow="visible" id="glyph1-20">
<path style="stroke:none;" d="M 4.390625 -2.640625 L 4.390625 0 L 3.671875 0 L 3.671875 -2.625 C 3.671875 -3.03125 3.585938 -3.335938 3.421875 -3.546875 C 3.265625 -3.753906 3.023438 -3.859375 2.703125 -3.859375 C 2.316406 -3.859375 2.007812 -3.734375 1.78125 -3.484375 C 1.5625 -3.234375 1.453125 -2.894531 1.453125 -2.46875 L 1.453125 0 L 0.734375 0 L 0.734375 -6.078125 L 1.453125 -6.078125 L 1.453125 -3.703125 C 1.617188 -3.960938 1.816406 -4.15625 2.046875 -4.28125 C 2.285156 -4.414062 2.554688 -4.484375 2.859375 -4.484375 C 3.367188 -4.484375 3.75 -4.328125 4 -4.015625 C 4.257812 -3.703125 4.390625 -3.242188 4.390625 -2.640625 Z M 4.390625 -2.640625 "/>
</symbol>
<symbol overflow="visible" id="glyph1-21">
<path style="stroke:none;" d="M 2.96875 -6.078125 L 2.96875 -5.484375 L 2.28125 -5.484375 C 2.019531 -5.484375 1.835938 -5.429688 1.734375 -5.328125 C 1.640625 -5.222656 1.59375 -5.035156 1.59375 -4.765625 L 1.59375 -4.375 L 2.78125 -4.375 L 2.78125 -3.8125 L 1.59375 -3.8125 L 1.59375 0 L 0.875 0 L 0.875 -3.8125 L 0.1875 -3.8125 L 0.1875 -4.375 L 0.875 -4.375 L 0.875 -4.6875 C 0.875 -5.164062 0.984375 -5.515625 1.203125 -5.734375 C 1.429688 -5.960938 1.796875 -6.078125 2.296875 -6.078125 Z M 2.96875 -6.078125 "/>
</symbol>
<symbol overflow="visible" id="glyph1-22">
<path style="stroke:none;" d="M 0.734375 -6.078125 L 1.453125 -6.078125 L 1.453125 -2.484375 L 3.59375 -4.375 L 4.515625 -4.375 L 2.1875 -2.328125 L 4.609375 0 L 3.671875 0 L 1.453125 -2.140625 L 1.453125 0 L 0.734375 0 Z M 0.734375 -6.078125 "/>
</symbol>
<symbol overflow="visible" id="glyph1-23">
<path style="stroke:none;" d="M 0.34375 -4.375 L 1.0625 -4.375 L 1.953125 -0.96875 L 2.84375 -4.375 L 3.703125 -4.375 L 4.59375 -0.96875 L 5.484375 -4.375 L 6.203125 -4.375 L 5.0625 0 L 4.21875 0 L 3.28125 -3.59375 L 2.328125 0 L 1.484375 0 Z M 0.34375 -4.375 "/> <path style="stroke:none;" d="M 0.34375 -4.375 L 1.0625 -4.375 L 1.953125 -0.96875 L 2.84375 -4.375 L 3.703125 -4.375 L 4.59375 -0.96875 L 5.484375 -4.375 L 6.203125 -4.375 L 5.0625 0 L 4.21875 0 L 3.28125 -3.59375 L 2.328125 0 L 1.484375 0 Z M 0.34375 -4.375 "/>
</symbol> </symbol>
<symbol overflow="visible" id="glyph1-24">
<path style="stroke:none;" d="M 4.28125 -5.640625 L 4.28125 -4.875 C 3.976562 -5.019531 3.691406 -5.125 3.421875 -5.1875 C 3.160156 -5.257812 2.910156 -5.296875 2.671875 -5.296875 C 2.234375 -5.296875 1.898438 -5.210938 1.671875 -5.046875 C 1.441406 -4.878906 1.328125 -4.644531 1.328125 -4.34375 C 1.328125 -4.082031 1.398438 -3.882812 1.546875 -3.75 C 1.703125 -3.625 2 -3.519531 2.4375 -3.4375 L 2.90625 -3.34375 C 3.5 -3.226562 3.9375 -3.03125 4.21875 -2.75 C 4.5 -2.46875 4.640625 -2.085938 4.640625 -1.609375 C 4.640625 -1.046875 4.445312 -0.617188 4.0625 -0.328125 C 3.6875 -0.0351562 3.132812 0.109375 2.40625 0.109375 C 2.125 0.109375 1.828125 0.078125 1.515625 0.015625 C 1.203125 -0.046875 0.878906 -0.140625 0.546875 -0.265625 L 0.546875 -1.078125 C 0.867188 -0.890625 1.179688 -0.75 1.484375 -0.65625 C 1.796875 -0.570312 2.101562 -0.53125 2.40625 -0.53125 C 2.851562 -0.53125 3.195312 -0.617188 3.4375 -0.796875 C 3.6875 -0.972656 3.8125 -1.222656 3.8125 -1.546875 C 3.8125 -1.835938 3.722656 -2.0625 3.546875 -2.21875 C 3.367188 -2.382812 3.082031 -2.507812 2.6875 -2.59375 L 2.203125 -2.6875 C 1.609375 -2.800781 1.179688 -2.984375 0.921875 -3.234375 C 0.660156 -3.484375 0.53125 -3.832031 0.53125 -4.28125 C 0.53125 -4.789062 0.710938 -5.191406 1.078125 -5.484375 C 1.441406 -5.785156 1.941406 -5.9375 2.578125 -5.9375 C 2.847656 -5.9375 3.125 -5.910156 3.40625 -5.859375 C 3.695312 -5.816406 3.988281 -5.742188 4.28125 -5.640625 Z M 4.28125 -5.640625 "/>
</symbol>
</g> </g>
<clipPath id="clip1"> <clipPath id="clip1">
<path d="M 683.339844 48 L 725 48 L 725 59 L 683.339844 59 Z M 683.339844 48 "/> <path d="M 1149.457031 48 L 1191 48 L 1191 59 L 1149.457031 59 Z M 1149.457031 48 "/>
</clipPath> </clipPath>
<clipPath id="clip2"> <clipPath id="clip2">
<path d="M 812.351562 48 L 847.65625 48 L 847.65625 59 L 812.351562 59 Z M 812.351562 48 "/> <path d="M 168.484375 80 L 210 80 L 210 91 L 168.484375 91 Z M 168.484375 80 "/>
</clipPath> </clipPath>
<clipPath id="clip3"> <clipPath id="clip3">
<path d="M 1068.0625 48 L 1099.449219 48 L 1099.449219 59 L 1068.0625 59 Z M 1068.0625 48 "/> <path d="M 945.597656 80 L 987 80 L 987 91 L 945.597656 91 Z M 945.597656 80 "/>
</clipPath> </clipPath>
<clipPath id="clip4"> <clipPath id="clip4">
<path d="M 865.34375 80 L 898 80 L 898 91 L 865.34375 91 Z M 865.34375 80 "/> <path d="M 250.011719 69 L 303 69 L 303 78 L 250.011719 78 Z M 250.011719 69 "/>
</clipPath> </clipPath>
<clipPath id="clip5"> <clipPath id="clip5">
<path d="M 951.027344 80 L 981.15625 80 L 981.15625 91 L 951.027344 91 Z M 951.027344 80 "/> <path d="M 979.003906 69 L 1003.683594 69 L 1003.683594 77 L 979.003906 77 Z M 979.003906 69 "/>
</clipPath> </clipPath>
<clipPath id="clip6"> <clipPath id="clip6">
<path d="M 764.328125 112 L 801.253906 112 L 801.253906 123 L 764.328125 123 Z M 764.328125 112 "/> <path d="M 1018.117188 112 L 1060 112 L 1060 123 L 1018.117188 123 Z M 1018.117188 112 "/>
</clipPath> </clipPath>
<clipPath id="clip7"> <clipPath id="clip7">
<path d="M 910 112 L 939.957031 112 L 939.957031 123 L 910 123 Z M 910 112 "/> <path d="M 583.375 101 L 637 101 L 637 110 L 583.375 110 Z M 583.375 101 "/>
</clipPath> </clipPath>
<clipPath id="clip8"> <clipPath id="clip8">
<path d="M 1007.089844 112 L 1022.230469 112 L 1022.230469 123 L 1007.089844 123 Z M 1007.089844 112 "/> <path d="M 1049.300781 101 L 1070.394531 101 L 1070.394531 109 L 1049.300781 109 Z M 1049.300781 101 "/>
</clipPath> </clipPath>
<clipPath id="clip9"> <clipPath id="clip9">
<path d="M 789.792969 101 L 809 101 L 809 110 L 789.792969 110 Z M 789.792969 101 "/> <path d="M 1082.304688 144 L 1124 144 L 1124 155 L 1082.304688 155 Z M 1082.304688 144 "/>
</clipPath> </clipPath>
<clipPath id="clip10"> <clipPath id="clip10">
<path d="M 931.160156 101 L 950 101 L 950 110 L 931.160156 110 Z M 931.160156 101 "/> <path d="M 819.433594 133 L 873 133 L 873 142 L 819.433594 142 Z M 819.433594 133 "/>
</clipPath> </clipPath>
<clipPath id="clip11"> <clipPath id="clip11">
<path d="M 1013.980469 101 L 1033 101 L 1033 110 L 1013.980469 110 Z M 1013.980469 101 "/> <path d="M 1112 133 L 1132.316406 133 L 1132.316406 141 L 1112 141 Z M 1112 133 "/>
</clipPath> </clipPath>
<clipPath id="clip12"> <clipPath id="clip12">
<path d="M 836.167969 37 L 1028 37 L 1028 46 L 836.167969 46 Z M 836.167969 37 "/> <path d="M 901.039062 37 L 985 37 L 985 45 L 901.039062 45 Z M 901.039062 37 "/>
</clipPath> </clipPath>
<clipPath id="clip13"> <clipPath id="clip13">
<path d="M 1088.320312 37 L 1158 37 L 1158 45 L 1088.320312 45 Z M 1088.320312 37 "/> <path d="M 1179.296875 37 L 1200.945312 37 L 1200.945312 45 L 1179.296875 45 Z M 1179.296875 37 "/>
</clipPath> </clipPath>
</defs> </defs>
<g id="surface2"> <g id="surface2">
<rect x="0" y="0" width="1280" height="152" style="fill:rgb(90%,90%,90%);fill-opacity:1;stroke:none;"/> <rect x="0" y="0" width="1280" height="184" style="fill:rgb(90%,90%,90%);fill-opacity:1;stroke:none;"/>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 4 0 L 4 152 "/> <path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 4 0 L 4 184 "/>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 155.40625 0 L 155.40625 152 "/> <path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 195.652344 0 L 195.652344 184 "/>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 306.808594 0 L 306.808594 152 "/> <path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 387.304688 0 L 387.304688 184 "/>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 458.214844 0 L 458.214844 152 "/> <path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 578.957031 0 L 578.957031 184 "/>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 609.621094 0 L 609.621094 152 "/> <path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 770.613281 0 L 770.613281 184 "/>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 761.027344 0 L 761.027344 152 "/> <path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 962.265625 0 L 962.265625 184 "/>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 912.429688 0 L 912.429688 152 "/> <path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1153.917969 0 L 1153.917969 184 "/>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1063.835938 0 L 1063.835938 152 "/>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(70%,70%,70%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1215.242188 0 L 1215.242188 152 "/>
<g style="fill:rgb(40%,40%,40%);fill-opacity:1;"> <g style="fill:rgb(40%,40%,40%);fill-opacity:1;">
<use xlink:href="#glyph0-1" x="4" y="147.503906"/> <use xlink:href="#glyph0-1" x="4" y="179.503906"/>
<use xlink:href="#glyph0-2" x="11.582031" y="147.503906"/> <use xlink:href="#glyph0-2" x="11.582031" y="179.503906"/>
<use xlink:href="#glyph0-3" x="18.935547" y="147.503906"/> <use xlink:href="#glyph0-3" x="18.935547" y="179.503906"/>
<use xlink:href="#glyph0-4" x="25.533203" y="147.503906"/> <use xlink:href="#glyph0-4" x="25.533203" y="179.503906"/>
<use xlink:href="#glyph0-5" x="33.138672" y="147.503906"/> <use xlink:href="#glyph0-5" x="33.138672" y="179.503906"/>
<use xlink:href="#glyph0-6" x="36.953125" y="147.503906"/> <use xlink:href="#glyph0-6" x="36.953125" y="179.503906"/>
<use xlink:href="#glyph0-7" x="44.570312" y="147.503906"/> <use xlink:href="#glyph0-7" x="44.570312" y="179.503906"/>
<use xlink:href="#glyph0-8" x="49.503906" y="147.503906"/> <use xlink:href="#glyph0-8" x="49.503906" y="179.503906"/>
<use xlink:href="#glyph0-9" x="52.837891" y="147.503906"/> <use xlink:href="#glyph0-9" x="52.837891" y="179.503906"/>
<use xlink:href="#glyph0-5" x="60.455078" y="147.503906"/> <use xlink:href="#glyph0-5" x="60.455078" y="179.503906"/>
<use xlink:href="#glyph0-9" x="64.269531" y="147.503906"/> <use xlink:href="#glyph0-9" x="64.269531" y="179.503906"/>
<use xlink:href="#glyph0-8" x="71.886719" y="147.503906"/> <use xlink:href="#glyph0-8" x="71.886719" y="179.503906"/>
<use xlink:href="#glyph0-10" x="75.220703" y="147.503906"/> <use xlink:href="#glyph0-10" x="75.220703" y="179.503906"/>
<use xlink:href="#glyph0-8" x="82.322266" y="147.503906"/> <use xlink:href="#glyph0-8" x="82.322266" y="179.503906"/>
<use xlink:href="#glyph0-11" x="85.65625" y="147.503906"/> <use xlink:href="#glyph0-11" x="85.65625" y="179.503906"/>
<use xlink:href="#glyph0-8" x="91.908203" y="147.503906"/> <use xlink:href="#glyph0-8" x="91.908203" y="179.503906"/>
<use xlink:href="#glyph0-12" x="95.242188" y="147.503906"/> <use xlink:href="#glyph0-12" x="95.242188" y="179.503906"/>
<use xlink:href="#glyph0-13" x="102.583984" y="147.503906"/> <use xlink:href="#glyph0-13" x="102.583984" y="179.503906"/>
<use xlink:href="#glyph0-14" x="110.189453" y="147.503906"/> <use xlink:href="#glyph0-14" x="110.189453" y="179.503906"/>
<use xlink:href="#glyph0-5" x="114.232422" y="147.503906"/> <use xlink:href="#glyph0-5" x="114.232422" y="179.503906"/>
<use xlink:href="#glyph0-15" x="118.046875" y="147.503906"/> <use xlink:href="#glyph0-15" x="118.046875" y="179.503906"/>
<use xlink:href="#glyph0-16" x="125.681641" y="147.503906"/> <use xlink:href="#glyph0-16" x="125.681641" y="179.503906"/>
<use xlink:href="#glyph0-5" x="133.316406" y="147.503906"/> <use xlink:href="#glyph0-5" x="133.316406" y="179.503906"/>
<use xlink:href="#glyph0-17" x="137.130859" y="147.503906"/> <use xlink:href="#glyph0-17" x="137.130859" y="179.503906"/>
<use xlink:href="#glyph0-11" x="144.736328" y="147.503906"/> <use xlink:href="#glyph0-11" x="144.736328" y="179.503906"/>
</g> </g>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 11.480469 46 L 18.324219 46 L 18.324219 60 L 11.480469 60 Z M 11.480469 46 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 10.746094 46 L 18.640625 46 L 18.640625 60 L 10.746094 60 Z M 10.746094 46 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 18.324219 46 L 93.210938 46 L 93.210938 60 L 18.324219 60 Z M 18.324219 46 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 18.640625 46 L 59.882812 46 L 59.882812 60 L 18.640625 60 Z M 18.640625 46 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 93.207031 46 L 671.605469 46 L 671.605469 60 L 93.207031 60 Z M 93.207031 46 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 59.886719 46 L 491.21875 46 L 491.21875 60 L 59.886719 60 Z M 59.886719 46 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 671.605469 46 L 681.339844 46 L 681.339844 60 L 671.605469 60 Z M 671.605469 46 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 491.21875 46 L 523.03125 46 L 523.03125 60 L 491.21875 60 Z M 491.21875 46 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 681.339844 46 L 732.652344 46 L 732.652344 60 L 681.339844 60 Z M 681.339844 46 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 523.035156 46 L 618.25 46 L 618.25 60 L 523.035156 60 Z M 523.035156 46 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 618.246094 46 L 621.214844 46 L 621.214844 60 L 618.246094 60 Z M 618.246094 46 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 621.21875 46 L 864.0625 46 L 864.0625 60 L 621.21875 60 Z M 621.21875 46 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 864.0625 46 L 871 46 L 871 60 L 864.0625 60 Z M 864.0625 46 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 871 46 L 918.414062 46 L 918.414062 60 L 871 60 Z M 871 46 "/>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-18" x="873" y="58"/>
<use xlink:href="#glyph0-7" x="877.705078" y="58"/>
<use xlink:href="#glyph0-2" x="882.638672" y="58"/>
<use xlink:href="#glyph0-3" x="889.992188" y="58"/>
<use xlink:href="#glyph0-19" x="896.589844" y="58"/>
<use xlink:href="#glyph0-20" x="903.972656" y="58"/>
<use xlink:href="#glyph0-13" x="907.306641" y="58"/>
</g>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 918.414062 46 L 927.882812 46 L 927.882812 60 L 918.414062 60 Z M 918.414062 46 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 927.882812 46 L 1134.214844 46 L 1134.214844 60 L 927.882812 60 Z M 927.882812 46 "/>
<g style="fill:rgb(100%,100%,100%);fill-opacity:1;">
<use xlink:href="#glyph0-21" x="929.882812" y="58"/>
<use xlink:href="#glyph0-22" x="937.5" y="58"/>
<use xlink:href="#glyph0-8" x="947.314453" y="58"/>
<use xlink:href="#glyph0-18" x="950.648438" y="58"/>
<use xlink:href="#glyph0-3" x="955.353516" y="58"/>
<use xlink:href="#glyph0-4" x="961.951172" y="58"/>
<use xlink:href="#glyph0-23" x="969.556641" y="58"/>
<use xlink:href="#glyph0-2" x="973.371094" y="58"/>
<use xlink:href="#glyph0-22" x="980.724609" y="58"/>
<use xlink:href="#glyph0-2" x="990.539062" y="58"/>
<use xlink:href="#glyph0-8" x="997.892578" y="58"/>
<use xlink:href="#glyph0-18" x="1001.226562" y="58"/>
<use xlink:href="#glyph0-24" x="1005.931641" y="58"/>
<use xlink:href="#glyph0-8" x="1011.931641" y="58"/>
<use xlink:href="#glyph0-9" x="1015.265625" y="58"/>
<use xlink:href="#glyph0-20" x="1022.882812" y="58"/>
<use xlink:href="#glyph0-19" x="1026.216797" y="58"/>
</g>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 1134.214844 46 L 1147.457031 46 L 1147.457031 60 L 1134.214844 60 Z M 1134.214844 46 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 1147.457031 46 L 1196.730469 46 L 1196.730469 60 L 1147.457031 60 Z M 1147.457031 46 "/>
<g clip-path="url(#clip1)" clip-rule="nonzero"> <g clip-path="url(#clip1)" clip-rule="nonzero">
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> <g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-18" x="683.339844" y="58"/> <use xlink:href="#glyph0-18" x="1149.457031" y="58"/>
<use xlink:href="#glyph0-7" x="688.044922" y="58"/> <use xlink:href="#glyph0-7" x="1154.162109" y="58"/>
<use xlink:href="#glyph0-2" x="692.978516" y="58"/> <use xlink:href="#glyph0-2" x="1159.095703" y="58"/>
<use xlink:href="#glyph0-3" x="700.332031" y="58"/> <use xlink:href="#glyph0-3" x="1166.449219" y="58"/>
<use xlink:href="#glyph0-19" x="706.929688" y="58"/> <use xlink:href="#glyph0-19" x="1173.046875" y="58"/>
<use xlink:href="#glyph0-20" x="714.3125" y="58"/> <use xlink:href="#glyph0-20" x="1180.429688" y="58"/>
<use xlink:href="#glyph0-13" x="717.646484" y="58"/> <use xlink:href="#glyph0-13" x="1183.763672" y="58"/>
</g> </g>
</g> </g>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 732.652344 46 L 740.554688 46 L 740.554688 60 L 732.652344 60 Z M 732.652344 46 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 1196.730469 46 L 1200.945312 46 L 1200.945312 60 L 1196.730469 60 Z M 1196.730469 46 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 740.554688 46 L 805.128906 46 L 805.128906 60 L 740.554688 60 Z M 740.554688 46 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 59.886719 78 L 62.914062 78 L 62.914062 92 L 59.886719 92 Z M 59.886719 78 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 805.128906 46 L 810.351562 46 L 810.351562 60 L 805.128906 60 Z M 805.128906 46 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 62.914062 78 L 166.484375 78 L 166.484375 92 L 62.914062 92 Z M 62.914062 78 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 810.351562 46 L 847.65625 46 L 847.65625 60 L 810.351562 60 Z M 810.351562 46 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 166.484375 78 L 471.960938 78 L 471.960938 92 L 166.484375 92 Z M 166.484375 78 "/>
<g clip-path="url(#clip2)" clip-rule="nonzero"> <g clip-path="url(#clip2)" clip-rule="nonzero">
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> <g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-18" x="812.351562" y="58"/> <use xlink:href="#glyph0-18" x="168.484375" y="90"/>
<use xlink:href="#glyph0-7" x="817.056641" y="58"/> <use xlink:href="#glyph0-7" x="173.189453" y="90"/>
<use xlink:href="#glyph0-2" x="821.990234" y="58"/> <use xlink:href="#glyph0-2" x="178.123047" y="90"/>
<use xlink:href="#glyph0-3" x="829.34375" y="58"/> <use xlink:href="#glyph0-3" x="185.476562" y="90"/>
<use xlink:href="#glyph0-19" x="835.941406" y="58"/> <use xlink:href="#glyph0-19" x="192.074219" y="90"/>
<use xlink:href="#glyph0-20" x="843.324219" y="58"/> <use xlink:href="#glyph0-20" x="199.457031" y="90"/>
<use xlink:href="#glyph0-13" x="846.658203" y="58"/> <use xlink:href="#glyph0-13" x="202.791016" y="90"/>
</g> </g>
</g> </g>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 847.660156 46 L 852.671875 46 L 852.671875 60 L 847.660156 60 Z M 847.660156 46 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 471.957031 78 L 483.074219 78 L 483.074219 92 L 471.957031 92 Z M 471.957031 78 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 852.671875 46 L 1053.917969 46 L 1053.917969 60 L 852.671875 60 Z M 852.671875 46 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 483.074219 78 L 932.367188 78 L 932.367188 92 L 483.074219 92 Z M 483.074219 78 "/>
<g style="fill:rgb(100%,100%,100%);fill-opacity:1;"> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 932.367188 78 L 943.597656 78 L 943.597656 92 L 932.367188 92 Z M 932.367188 78 "/>
<use xlink:href="#glyph0-21" x="854.671875" y="58"/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 943.597656 78 L 996.683594 78 L 996.683594 92 L 943.597656 92 Z M 943.597656 78 "/>
<use xlink:href="#glyph0-22" x="862.289062" y="58"/>
<use xlink:href="#glyph0-8" x="872.103516" y="58"/>
<use xlink:href="#glyph0-18" x="875.4375" y="58"/>
<use xlink:href="#glyph0-3" x="880.142578" y="58"/>
<use xlink:href="#glyph0-4" x="886.740234" y="58"/>
<use xlink:href="#glyph0-23" x="894.345703" y="58"/>
<use xlink:href="#glyph0-2" x="898.160156" y="58"/>
<use xlink:href="#glyph0-22" x="905.513672" y="58"/>
<use xlink:href="#glyph0-2" x="915.328125" y="58"/>
<use xlink:href="#glyph0-8" x="922.681641" y="58"/>
<use xlink:href="#glyph0-18" x="926.015625" y="58"/>
<use xlink:href="#glyph0-24" x="930.720703" y="58"/>
<use xlink:href="#glyph0-8" x="936.720703" y="58"/>
<use xlink:href="#glyph0-9" x="940.054688" y="58"/>
<use xlink:href="#glyph0-20" x="947.671875" y="58"/>
<use xlink:href="#glyph0-19" x="951.005859" y="58"/>
</g>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 1053.917969 46 L 1066.0625 46 L 1066.0625 60 L 1053.917969 60 Z M 1053.917969 46 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 1066.0625 46 L 1099.449219 46 L 1099.449219 60 L 1066.0625 60 Z M 1066.0625 46 "/>
<g clip-path="url(#clip3)" clip-rule="nonzero"> <g clip-path="url(#clip3)" clip-rule="nonzero">
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> <g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-18" x="1068.0625" y="58"/> <use xlink:href="#glyph0-18" x="945.597656" y="90"/>
<use xlink:href="#glyph0-7" x="1072.767578" y="58"/> <use xlink:href="#glyph0-7" x="950.302734" y="90"/>
<use xlink:href="#glyph0-2" x="1077.701172" y="58"/> <use xlink:href="#glyph0-2" x="955.236328" y="90"/>
<use xlink:href="#glyph0-3" x="1085.054688" y="58"/> <use xlink:href="#glyph0-3" x="962.589844" y="90"/>
<use xlink:href="#glyph0-19" x="1091.652344" y="58"/> <use xlink:href="#glyph0-19" x="969.1875" y="90"/>
<use xlink:href="#glyph0-20" x="1099.035156" y="58"/> <use xlink:href="#glyph0-20" x="976.570312" y="90"/>
<use xlink:href="#glyph0-13" x="1102.369141" y="58"/> <use xlink:href="#glyph0-13" x="979.904297" y="90"/>
</g> </g>
</g> </g>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 1099.445312 46 L 1190.574219 46 L 1190.574219 60 L 1099.445312 60 Z M 1099.445312 46 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 996.683594 78 L 1006.457031 78 L 1006.457031 92 L 996.683594 92 Z M 996.683594 78 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 93.207031 78 L 97.839844 78 L 97.839844 92 L 93.207031 92 Z M 93.207031 78 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 1006.460938 78 L 1003.683594 78 L 1003.683594 92 L 1006.460938 92 Z M 1006.460938 78 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 97.839844 78 L 311.714844 78 L 311.714844 92 L 97.839844 92 Z M 97.839844 78 "/> <path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 248.011719 81 L 248.011719 75 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 311.714844 78 L 657.222656 78 L 657.222656 92 L 311.714844 92 Z M 311.714844 78 "/>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-18" x="313.714844" y="90"/>
<use xlink:href="#glyph0-7" x="318.419922" y="90"/>
<use xlink:href="#glyph0-2" x="323.353516" y="90"/>
<use xlink:href="#glyph0-3" x="330.707031" y="90"/>
<use xlink:href="#glyph0-19" x="337.304688" y="90"/>
<use xlink:href="#glyph0-20" x="344.6875" y="90"/>
<use xlink:href="#glyph0-13" x="348.021484" y="90"/>
</g>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 657.222656 78 L 662.550781 78 L 662.550781 92 L 657.222656 92 Z M 657.222656 78 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 662.550781 78 L 856.289062 78 L 856.289062 92 L 662.550781 92 Z M 662.550781 78 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 856.289062 78 L 863.34375 78 L 863.34375 92 L 856.289062 92 Z M 856.289062 78 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 863.34375 78 L 898 78 L 898 92 L 863.34375 92 Z M 863.34375 78 "/>
<g clip-path="url(#clip4)" clip-rule="nonzero"> <g clip-path="url(#clip4)" clip-rule="nonzero">
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> <g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-18" x="865.34375" y="90"/> <use xlink:href="#glyph1-1" x="250.011719" y="76"/>
<use xlink:href="#glyph0-7" x="870.048828" y="90"/> <use xlink:href="#glyph1-2" x="252.371094" y="76"/>
<use xlink:href="#glyph0-2" x="874.982422" y="90"/> <use xlink:href="#glyph1-3" x="257.265625" y="76"/>
<use xlink:href="#glyph0-3" x="882.335938" y="90"/> <use xlink:href="#glyph1-4" x="262.34375" y="76"/>
<use xlink:href="#glyph0-19" x="888.933594" y="90"/> <use xlink:href="#glyph1-5" x="264.886719" y="76"/>
<use xlink:href="#glyph0-20" x="896.316406" y="90"/> <use xlink:href="#glyph1-4" x="269.976562" y="76"/>
<use xlink:href="#glyph0-13" x="899.650391" y="90"/> <use xlink:href="#glyph1-6" x="272.519531" y="76"/>
<use xlink:href="#glyph1-7" x="276.6875" y="76"/>
<use xlink:href="#glyph1-8" x="279.824219" y="76"/>
<use xlink:href="#glyph1-9" x="284.726562" y="76"/>
<use xlink:href="#glyph1-7" x="288.015625" y="76"/>
<use xlink:href="#glyph1-10" x="291.152344" y="76"/>
<use xlink:href="#glyph1-11" x="293.375" y="76"/>
<use xlink:href="#glyph1-12" x="298.445312" y="76"/>
</g> </g>
</g> </g>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 898 78 L 899.648438 78 L 899.648438 92 L 898 92 Z M 898 78 "/> <path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 977.003906 81 L 977.003906 75 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 899.652344 78 L 943.257812 78 L 943.257812 92 L 899.652344 92 Z M 899.652344 78 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 943.257812 78 L 949.027344 78 L 949.027344 92 L 943.257812 92 Z M 943.257812 78 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 949.027344 78 L 981.15625 78 L 981.15625 92 L 949.027344 92 Z M 949.027344 78 "/>
<g clip-path="url(#clip5)" clip-rule="nonzero"> <g clip-path="url(#clip5)" clip-rule="nonzero">
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> <g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-18" x="951.027344" y="90"/> <use xlink:href="#glyph1-5" x="979.003906" y="76"/>
<use xlink:href="#glyph0-7" x="955.732422" y="90"/> <use xlink:href="#glyph1-4" x="984.09375" y="76"/>
<use xlink:href="#glyph0-2" x="960.666016" y="90"/> <use xlink:href="#glyph1-13" x="986.636719" y="76"/>
<use xlink:href="#glyph0-3" x="968.019531" y="90"/> <use xlink:href="#glyph1-2" x="991.714844" y="76"/>
<use xlink:href="#glyph0-19" x="974.617188" y="90"/> <use xlink:href="#glyph1-11" x="996.609375" y="76"/>
<use xlink:href="#glyph0-20" x="982" y="90"/> <use xlink:href="#glyph1-14" x="1001.679688" y="76"/>
<use xlink:href="#glyph0-13" x="985.333984" y="90"/>
</g> </g>
</g> </g>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 981.152344 78 L 982.46875 78 L 982.46875 92 L 981.152344 92 Z M 981.152344 78 "/> <path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(40%,80%,40%);stroke-opacity:1;stroke-miterlimit:10;" d="M 59.886719 46 L 59.886719 92 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 982.472656 78 L 1041.113281 78 L 1041.113281 92 L 982.472656 92 Z M 982.472656 78 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 523.035156 110 L 525.679688 110 L 525.679688 124 L 523.035156 124 Z M 523.035156 110 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 1041.109375 78 L 1048.574219 78 L 1048.574219 92 L 1041.109375 92 Z M 1041.109375 78 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 525.679688 110 L 544.980469 110 L 544.980469 124 L 525.679688 124 Z M 525.679688 110 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 1048.574219 78 L 1046.953125 78 L 1046.953125 92 L 1048.574219 92 Z M 1048.574219 78 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 544.976562 110 L 611.632812 110 L 611.632812 124 L 544.976562 124 Z M 544.976562 110 "/>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 430.597656 81 L 430.597656 75 "/>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> <g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph1-1" x="432.597656" y="76"/> <use xlink:href="#glyph0-18" x="546.976562" y="122"/>
<use xlink:href="#glyph1-2" x="434.820312" y="76"/> <use xlink:href="#glyph0-7" x="551.681641" y="122"/>
<use xlink:href="#glyph1-3" x="437.363281" y="76"/> <use xlink:href="#glyph0-2" x="556.615234" y="122"/>
<use xlink:href="#glyph1-2" x="444.066406" y="76"/> <use xlink:href="#glyph0-3" x="563.96875" y="122"/>
<use xlink:href="#glyph1-4" x="446.609375" y="76"/> <use xlink:href="#glyph0-19" x="570.566406" y="122"/>
<use xlink:href="#glyph0-20" x="577.949219" y="122"/>
<use xlink:href="#glyph0-13" x="581.283203" y="122"/>
</g> </g>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 886.390625 81 L 886.390625 75 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 611.636719 110 L 615.625 110 L 615.625 124 L 611.636719 124 Z M 611.636719 110 "/>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 615.621094 110 L 1006.457031 110 L 1006.457031 124 L 615.621094 124 Z M 615.621094 110 "/>
<use xlink:href="#glyph1-1" x="888.390625" y="76"/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 1006.460938 110 L 1016.121094 110 L 1016.121094 124 L 1006.460938 124 Z M 1006.460938 110 "/>
<use xlink:href="#glyph1-2" x="890.613281" y="76"/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 1016.117188 110 L 1065.160156 110 L 1065.160156 124 L 1016.117188 124 Z M 1016.117188 110 "/>
<use xlink:href="#glyph1-3" x="893.15625" y="76"/>
<use xlink:href="#glyph1-2" x="899.859375" y="76"/>
<use xlink:href="#glyph1-5" x="902.402344" y="76"/>
</g>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 970.753906 81 L 970.753906 75 "/>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph1-1" x="972.753906" y="76"/>
<use xlink:href="#glyph1-2" x="974.976562" y="76"/>
<use xlink:href="#glyph1-3" x="977.519531" y="76"/>
<use xlink:href="#glyph1-2" x="984.222656" y="76"/>
<use xlink:href="#glyph1-6" x="986.765625" y="76"/>
</g>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(40%,80%,40%);stroke-opacity:1;stroke-miterlimit:10;" d="M 93.207031 46 L 93.207031 92 "/>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 713.289062 49 L 713.289062 43 "/>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph1-7" x="715.289062" y="44"/>
<use xlink:href="#glyph1-1" x="719.890625" y="44"/>
<use xlink:href="#glyph1-8" x="722.113281" y="44"/>
<use xlink:href="#glyph1-9" x="725.402344" y="44"/>
<use xlink:href="#glyph1-10" x="729.570312" y="44"/>
<use xlink:href="#glyph1-2" x="732.707031" y="44"/>
<use xlink:href="#glyph1-10" x="735.25" y="44"/>
<use xlink:href="#glyph1-11" x="738.386719" y="44"/>
<use xlink:href="#glyph1-8" x="743.457031" y="44"/>
<use xlink:href="#glyph1-12" x="746.746094" y="44"/>
<use xlink:href="#glyph1-13" x="751.667969" y="44"/>
<use xlink:href="#glyph1-14" x="756.570312" y="44"/>
<use xlink:href="#glyph1-2" x="761.648438" y="44"/>
<use xlink:href="#glyph1-15" x="764.191406" y="44"/>
<use xlink:href="#glyph1-16" x="767.007812" y="44"/>
<use xlink:href="#glyph1-8" x="771.902344" y="44"/>
<use xlink:href="#glyph1-17" x="775.191406" y="44"/>
<use xlink:href="#glyph1-12" x="779.824219" y="44"/>
<use xlink:href="#glyph1-14" x="784.746094" y="44"/>
</g>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 740.554688 110 L 742.976562 110 L 742.976562 124 L 740.554688 124 Z M 740.554688 110 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 742.976562 110 L 762.324219 110 L 762.324219 124 L 742.976562 124 Z M 742.976562 110 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 762.328125 110 L 801.253906 110 L 801.253906 124 L 762.328125 124 Z M 762.328125 110 "/>
<g clip-path="url(#clip6)" clip-rule="nonzero"> <g clip-path="url(#clip6)" clip-rule="nonzero">
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> <g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-18" x="764.328125" y="122"/> <use xlink:href="#glyph0-18" x="1018.117188" y="122"/>
<use xlink:href="#glyph0-7" x="769.033203" y="122"/> <use xlink:href="#glyph0-7" x="1022.822266" y="122"/>
<use xlink:href="#glyph0-2" x="773.966797" y="122"/> <use xlink:href="#glyph0-2" x="1027.755859" y="122"/>
<use xlink:href="#glyph0-3" x="781.320312" y="122"/> <use xlink:href="#glyph0-3" x="1035.109375" y="122"/>
<use xlink:href="#glyph0-19" x="787.917969" y="122"/> <use xlink:href="#glyph0-19" x="1041.707031" y="122"/>
<use xlink:href="#glyph0-20" x="795.300781" y="122"/> <use xlink:href="#glyph0-20" x="1049.089844" y="122"/>
<use xlink:href="#glyph0-13" x="798.634766" y="122"/> <use xlink:href="#glyph0-13" x="1052.423828" y="122"/>
</g> </g>
</g> </g>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 801.253906 110 L 803.496094 110 L 803.496094 124 L 801.253906 124 Z M 801.253906 110 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 1065.164062 110 L 1071.8125 110 L 1071.8125 124 L 1065.164062 124 Z M 1065.164062 110 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 803.496094 110 L 901.589844 110 L 901.589844 124 L 803.496094 124 Z M 803.496094 110 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 1071.8125 110 L 1070.394531 110 L 1070.394531 124 L 1071.8125 124 Z M 1071.8125 110 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 901.589844 110 L 907.84375 110 L 907.84375 124 L 901.589844 124 Z M 901.589844 110 "/> <path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 581.375 113 L 581.375 107 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 907.84375 110 L 939.957031 110 L 939.957031 124 L 907.84375 124 Z M 907.84375 110 "/>
<g clip-path="url(#clip7)" clip-rule="nonzero"> <g clip-path="url(#clip7)" clip-rule="nonzero">
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> <g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-18" x="909.84375" y="122"/> <use xlink:href="#glyph1-1" x="583.375" y="108"/>
<use xlink:href="#glyph0-7" x="914.548828" y="122"/> <use xlink:href="#glyph1-2" x="585.734375" y="108"/>
<use xlink:href="#glyph0-2" x="919.482422" y="122"/> <use xlink:href="#glyph1-3" x="590.628906" y="108"/>
<use xlink:href="#glyph0-3" x="926.835938" y="122"/> <use xlink:href="#glyph1-4" x="595.707031" y="108"/>
<use xlink:href="#glyph0-19" x="933.433594" y="122"/> <use xlink:href="#glyph1-15" x="598.25" y="108"/>
<use xlink:href="#glyph0-20" x="940.816406" y="122"/> <use xlink:href="#glyph1-4" x="603.339844" y="108"/>
<use xlink:href="#glyph0-13" x="944.150391" y="122"/> <use xlink:href="#glyph1-6" x="605.882812" y="108"/>
<use xlink:href="#glyph1-7" x="610.050781" y="108"/>
<use xlink:href="#glyph1-8" x="613.1875" y="108"/>
<use xlink:href="#glyph1-9" x="618.089844" y="108"/>
<use xlink:href="#glyph1-7" x="621.378906" y="108"/>
<use xlink:href="#glyph1-10" x="624.515625" y="108"/>
<use xlink:href="#glyph1-11" x="626.738281" y="108"/>
<use xlink:href="#glyph1-12" x="631.808594" y="108"/>
</g> </g>
</g> </g>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 939.957031 110 L 941.289062 110 L 941.289062 124 L 939.957031 124 Z M 939.957031 110 "/> <path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1047.300781 113 L 1047.300781 107 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 941.289062 110 L 984.167969 110 L 984.167969 124 L 941.289062 124 Z M 941.289062 110 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 984.167969 110 L 1005.09375 110 L 1005.09375 124 L 984.167969 124 Z M 984.167969 110 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 1005.089844 110 L 1022.230469 110 L 1022.230469 124 L 1005.089844 124 Z M 1005.089844 110 "/>
<g clip-path="url(#clip8)" clip-rule="nonzero"> <g clip-path="url(#clip8)" clip-rule="nonzero">
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> <g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-18" x="1007.089844" y="122"/> <use xlink:href="#glyph1-15" x="1049.300781" y="108"/>
<use xlink:href="#glyph0-7" x="1011.794922" y="122"/> <use xlink:href="#glyph1-4" x="1054.390625" y="108"/>
<use xlink:href="#glyph0-2" x="1016.728516" y="122"/> <use xlink:href="#glyph1-13" x="1056.933594" y="108"/>
<use xlink:href="#glyph0-3" x="1024.082031" y="122"/> <use xlink:href="#glyph1-2" x="1062.011719" y="108"/>
<use xlink:href="#glyph0-19" x="1030.679688" y="122"/> <use xlink:href="#glyph1-11" x="1066.90625" y="108"/>
<use xlink:href="#glyph0-20" x="1038.0625" y="122"/> <use xlink:href="#glyph1-14" x="1071.976562" y="108"/>
<use xlink:href="#glyph0-13" x="1041.396484" y="122"/>
</g> </g>
</g> </g>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 1022.230469 110 L 1023.820312 110 L 1023.820312 124 L 1022.230469 124 Z M 1022.230469 110 "/> <path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(40%,80%,40%);stroke-opacity:1;stroke-miterlimit:10;" d="M 523.035156 46 L 523.035156 124 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 1023.820312 110 L 1048.574219 110 L 1048.574219 124 L 1023.820312 124 Z M 1023.820312 110 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 621.21875 142 L 622.960938 142 L 622.960938 156 L 621.21875 156 Z M 621.21875 142 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 1048.574219 110 L 1053.917969 110 L 1053.917969 124 L 1048.574219 124 Z M 1048.574219 110 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 622.960938 142 L 634.785156 142 L 634.785156 156 L 622.960938 156 Z M 622.960938 142 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 1053.917969 110 L 1052.722656 110 L 1052.722656 124 L 1053.917969 124 Z M 1053.917969 110 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 634.785156 142 L 838.511719 142 L 838.511719 156 L 634.785156 156 Z M 634.785156 142 "/>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 787.792969 113 L 787.792969 107 "/> <g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-18" x="636.785156" y="154"/>
<use xlink:href="#glyph0-7" x="641.490234" y="154"/>
<use xlink:href="#glyph0-2" x="646.423828" y="154"/>
<use xlink:href="#glyph0-3" x="653.777344" y="154"/>
<use xlink:href="#glyph0-19" x="660.375" y="154"/>
<use xlink:href="#glyph0-20" x="667.757812" y="154"/>
<use xlink:href="#glyph0-13" x="671.091797" y="154"/>
</g>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 838.515625 142 L 861.878906 142 L 861.878906 156 L 838.515625 156 Z M 838.515625 142 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 861.875 142 L 1071.8125 142 L 1071.8125 156 L 861.875 156 Z M 861.875 142 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 1071.8125 142 L 1080.300781 142 L 1080.300781 156 L 1071.8125 156 Z M 1071.8125 142 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(50%,90%,50%);fill-opacity:1;" d="M 1080.304688 142 L 1126.570312 142 L 1126.570312 156 L 1080.304688 156 Z M 1080.304688 142 "/>
<g clip-path="url(#clip9)" clip-rule="nonzero"> <g clip-path="url(#clip9)" clip-rule="nonzero">
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> <g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph1-18" x="789.792969" y="108"/> <use xlink:href="#glyph0-18" x="1082.304688" y="154"/>
<use xlink:href="#glyph1-2" x="792.015625" y="108"/> <use xlink:href="#glyph0-7" x="1087.009766" y="154"/>
<use xlink:href="#glyph1-3" x="794.558594" y="108"/> <use xlink:href="#glyph0-2" x="1091.943359" y="154"/>
<use xlink:href="#glyph1-2" x="801.261719" y="108"/> <use xlink:href="#glyph0-3" x="1099.296875" y="154"/>
<use xlink:href="#glyph1-4" x="803.804688" y="108"/> <use xlink:href="#glyph0-19" x="1105.894531" y="154"/>
<use xlink:href="#glyph0-20" x="1113.277344" y="154"/>
<use xlink:href="#glyph0-13" x="1116.611328" y="154"/>
</g> </g>
</g> </g>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 929.160156 113 L 929.160156 107 "/> <path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,80%,40%);fill-opacity:1;" d="M 1126.566406 142 L 1134.214844 142 L 1134.214844 156 L 1126.566406 156 Z M 1126.566406 142 "/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(40%,40%,40%);fill-opacity:1;" d="M 1134.214844 142 L 1132.316406 142 L 1132.316406 156 L 1134.214844 156 Z M 1134.214844 142 "/>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 817.433594 145 L 817.433594 139 "/>
<g clip-path="url(#clip10)" clip-rule="nonzero"> <g clip-path="url(#clip10)" clip-rule="nonzero">
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> <g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph1-18" x="931.160156" y="108"/> <use xlink:href="#glyph1-1" x="819.433594" y="140"/>
<use xlink:href="#glyph1-2" x="933.382812" y="108"/> <use xlink:href="#glyph1-2" x="821.792969" y="140"/>
<use xlink:href="#glyph1-3" x="935.925781" y="108"/> <use xlink:href="#glyph1-3" x="826.6875" y="140"/>
<use xlink:href="#glyph1-2" x="942.628906" y="108"/> <use xlink:href="#glyph1-4" x="831.765625" y="140"/>
<use xlink:href="#glyph1-5" x="945.171875" y="108"/> <use xlink:href="#glyph1-16" x="834.308594" y="140"/>
<use xlink:href="#glyph1-4" x="839.398438" y="140"/>
<use xlink:href="#glyph1-6" x="841.941406" y="140"/>
<use xlink:href="#glyph1-7" x="846.109375" y="140"/>
<use xlink:href="#glyph1-8" x="849.246094" y="140"/>
<use xlink:href="#glyph1-9" x="854.148438" y="140"/>
<use xlink:href="#glyph1-7" x="857.4375" y="140"/>
<use xlink:href="#glyph1-10" x="860.574219" y="140"/>
<use xlink:href="#glyph1-11" x="862.796875" y="140"/>
<use xlink:href="#glyph1-12" x="867.867188" y="140"/>
</g> </g>
</g> </g>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1011.980469 113 L 1011.980469 107 "/> <path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1109.605469 145 L 1109.605469 139 "/>
<g clip-path="url(#clip11)" clip-rule="nonzero"> <g clip-path="url(#clip11)" clip-rule="nonzero">
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> <g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph1-18" x="1013.980469" y="108"/> <use xlink:href="#glyph1-16" x="1111.605469" y="140"/>
<use xlink:href="#glyph1-2" x="1016.203125" y="108"/> <use xlink:href="#glyph1-4" x="1116.695312" y="140"/>
<use xlink:href="#glyph1-3" x="1018.746094" y="108"/> <use xlink:href="#glyph1-13" x="1119.238281" y="140"/>
<use xlink:href="#glyph1-2" x="1025.449219" y="108"/> <use xlink:href="#glyph1-2" x="1124.316406" y="140"/>
<use xlink:href="#glyph1-6" x="1027.992188" y="108"/> <use xlink:href="#glyph1-11" x="1129.210938" y="140"/>
<use xlink:href="#glyph1-14" x="1134.28125" y="140"/>
</g> </g>
</g> </g>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(40%,80%,40%);stroke-opacity:1;stroke-miterlimit:10;" d="M 740.554688 46 L 740.554688 124 "/> <path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(40%,80%,40%);stroke-opacity:1;stroke-miterlimit:10;" d="M 621.21875 46 L 621.21875 156 "/>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 834.167969 49 L 834.167969 43 "/> <path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 899.039062 49 L 899.039062 43 "/>
<g clip-path="url(#clip12)" clip-rule="nonzero"> <g clip-path="url(#clip12)" clip-rule="nonzero">
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> <g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph1-19" x="836.167969" y="44"/> <use xlink:href="#glyph1-17" x="901.039062" y="44"/>
<use xlink:href="#glyph1-12" x="841.246094" y="44"/> <use xlink:href="#glyph1-18" x="906.511719" y="44"/>
<use xlink:href="#glyph1-20" x="846.167969" y="44"/> <use xlink:href="#glyph1-18" x="908.734375" y="44"/>
<use xlink:href="#glyph1-16" x="850.566406" y="44"/> <use xlink:href="#glyph1-4" x="910.957031" y="44"/>
<use xlink:href="#glyph1-21" x="855.460938" y="44"/> <use xlink:href="#glyph1-19" x="913.5" y="44"/>
<use xlink:href="#glyph1-14" x="860.53125" y="44"/> <use xlink:href="#glyph1-20" x="917.898438" y="44"/>
<use xlink:href="#glyph1-2" x="865.609375" y="44"/> <use xlink:href="#glyph1-10" x="922.96875" y="44"/>
<use xlink:href="#glyph1-10" x="868.152344" y="44"/> <use xlink:href="#glyph1-18" x="925.191406" y="44"/>
<use xlink:href="#glyph1-11" x="871.289062" y="44"/> <use xlink:href="#glyph1-13" x="927.414062" y="44"/>
<use xlink:href="#glyph1-8" x="876.359375" y="44"/> <use xlink:href="#glyph1-4" x="932.492188" y="44"/>
<use xlink:href="#glyph1-12" x="879.648438" y="44"/> <use xlink:href="#glyph1-21" x="935.035156" y="44"/>
<use xlink:href="#glyph1-13" x="884.570312" y="44"/> <use xlink:href="#glyph1-10" x="937.851562" y="44"/>
<use xlink:href="#glyph1-14" x="889.472656" y="44"/> <use xlink:href="#glyph1-3" x="940.074219" y="44"/>
<use xlink:href="#glyph1-2" x="894.550781" y="44"/> <use xlink:href="#glyph1-14" x="945.152344" y="44"/>
<use xlink:href="#glyph1-15" x="897.09375" y="44"/> <use xlink:href="#glyph1-9" x="950.074219" y="44"/>
<use xlink:href="#glyph1-16" x="899.910156" y="44"/> <use xlink:href="#glyph1-6" x="953.363281" y="44"/>
<use xlink:href="#glyph1-8" x="904.804688" y="44"/> <use xlink:href="#glyph1-4" x="957.53125" y="44"/>
<use xlink:href="#glyph1-17" x="908.09375" y="44"/> <use xlink:href="#glyph1-21" x="960.074219" y="44"/>
<use xlink:href="#glyph1-12" x="912.726562" y="44"/> <use xlink:href="#glyph1-2" x="962.890625" y="44"/>
<use xlink:href="#glyph1-14" x="917.648438" y="44"/> <use xlink:href="#glyph1-9" x="967.785156" y="44"/>
<use xlink:href="#glyph1-22" x="922.726562" y="44"/> <use xlink:href="#glyph1-22" x="971.074219" y="44"/>
<use xlink:href="#glyph1-2" x="925.421875" y="44"/> <use xlink:href="#glyph1-14" x="975.707031" y="44"/>
<use xlink:href="#glyph1-10" x="927.964844" y="44"/> <use xlink:href="#glyph1-13" x="980.628906" y="44"/>
<use xlink:href="#glyph1-16" x="931.101562" y="44"/>
<use xlink:href="#glyph1-23" x="935.996094" y="44"/>
<use xlink:href="#glyph1-24" x="941.074219" y="44"/>
<use xlink:href="#glyph1-25" x="943.960938" y="44"/>
<use xlink:href="#glyph1-12" x="946.183594" y="44"/>
<use xlink:href="#glyph1-26" x="951.105469" y="44"/>
<use xlink:href="#glyph1-12" x="955.839844" y="44"/>
<use xlink:href="#glyph1-25" x="960.761719" y="44"/>
<use xlink:href="#glyph1-2" x="962.984375" y="44"/>
<use xlink:href="#glyph1-20" x="965.527344" y="44"/>
<use xlink:href="#glyph1-16" x="969.925781" y="44"/>
<use xlink:href="#glyph1-14" x="974.820312" y="44"/>
<use xlink:href="#glyph1-12" x="979.898438" y="44"/>
<use xlink:href="#glyph1-2" x="984.820312" y="44"/>
<use xlink:href="#glyph1-1" x="987.363281" y="44"/>
<use xlink:href="#glyph1-9" x="989.585938" y="44"/>
<use xlink:href="#glyph1-2" x="993.753906" y="44"/>
<use xlink:href="#glyph1-15" x="996.296875" y="44"/>
<use xlink:href="#glyph1-1" x="999.113281" y="44"/>
<use xlink:href="#glyph1-21" x="1001.335938" y="44"/>
<use xlink:href="#glyph1-1" x="1006.40625" y="44"/>
<use xlink:href="#glyph1-9" x="1008.628906" y="44"/>
<use xlink:href="#glyph1-11" x="1012.796875" y="44"/>
<use xlink:href="#glyph1-12" x="1017.867188" y="44"/>
<use xlink:href="#glyph1-14" x="1022.789062" y="44"/>
</g> </g>
</g> </g>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 35.601562 36 L 31.601562 36 L 31.601562 128 L 35.601562 128 "/> <path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 46.675781 36 L 42.675781 36 L 42.675781 160 L 46.675781 160 "/>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1057.957031 36 L 1061.957031 36 L 1061.957031 128 L 1057.957031 128 "/> <path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1138.78125 36 L 1142.78125 36 L 1142.78125 160 L 1138.78125 160 "/>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> <g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph1-9" x="33.601562" y="44"/> <use xlink:href="#glyph1-6" x="44.675781" y="44"/>
<use xlink:href="#glyph1-27" x="37.769531" y="44"/> <use xlink:href="#glyph1-23" x="48.84375" y="44"/>
<use xlink:href="#glyph1-1" x="44.3125" y="44"/> <use xlink:href="#glyph1-10" x="55.386719" y="44"/>
<use xlink:href="#glyph1-10" x="46.535156" y="44"/> <use xlink:href="#glyph1-7" x="57.609375" y="44"/>
<use xlink:href="#glyph1-20" x="49.671875" y="44"/> <use xlink:href="#glyph1-19" x="60.746094" y="44"/>
<use xlink:href="#glyph1-11" x="54.070312" y="44"/> <use xlink:href="#glyph1-20" x="65.144531" y="44"/>
</g> </g>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1086.320312 49 L 1086.320312 43 "/> <path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 1177.296875 49 L 1177.296875 43 "/>
<g clip-path="url(#clip13)" clip-rule="nonzero"> <g clip-path="url(#clip13)" clip-rule="nonzero">
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;"> <g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph1-19" x="1088.320312" y="44"/> <use xlink:href="#glyph1-24" x="1179.296875" y="44"/>
<use xlink:href="#glyph1-27" x="1093.398438" y="44"/> <use xlink:href="#glyph1-23" x="1184.375" y="44"/>
<use xlink:href="#glyph1-1" x="1099.941406" y="44"/> <use xlink:href="#glyph1-10" x="1190.917969" y="44"/>
<use xlink:href="#glyph1-10" x="1102.164062" y="44"/> <use xlink:href="#glyph1-7" x="1193.140625" y="44"/>
<use xlink:href="#glyph1-20" x="1105.300781" y="44"/> <use xlink:href="#glyph1-19" x="1196.277344" y="44"/>
<use xlink:href="#glyph1-11" x="1109.699219" y="44"/> <use xlink:href="#glyph1-20" x="1200.675781" y="44"/>
<use xlink:href="#glyph1-2" x="1114.769531" y="44"/> <use xlink:href="#glyph1-4" x="1205.746094" y="44"/>
<use xlink:href="#glyph1-1" x="1117.3125" y="44"/> <use xlink:href="#glyph1-10" x="1208.289062" y="44"/>
<use xlink:href="#glyph1-9" x="1119.535156" y="44"/> <use xlink:href="#glyph1-6" x="1210.511719" y="44"/>
<use xlink:href="#glyph1-2" x="1123.703125" y="44"/> <use xlink:href="#glyph1-4" x="1214.679688" y="44"/>
<use xlink:href="#glyph1-15" x="1126.246094" y="44"/> <use xlink:href="#glyph1-21" x="1217.222656" y="44"/>
<use xlink:href="#glyph1-1" x="1129.0625" y="44"/> <use xlink:href="#glyph1-10" x="1220.039062" y="44"/>
<use xlink:href="#glyph1-21" x="1131.285156" y="44"/> <use xlink:href="#glyph1-11" x="1222.261719" y="44"/>
<use xlink:href="#glyph1-1" x="1136.355469" y="44"/> <use xlink:href="#glyph1-10" x="1227.332031" y="44"/>
<use xlink:href="#glyph1-9" x="1138.578125" y="44"/> <use xlink:href="#glyph1-6" x="1229.554688" y="44"/>
<use xlink:href="#glyph1-11" x="1142.746094" y="44"/> <use xlink:href="#glyph1-20" x="1233.722656" y="44"/>
<use xlink:href="#glyph1-12" x="1147.816406" y="44"/> <use xlink:href="#glyph1-14" x="1238.792969" y="44"/>
<use xlink:href="#glyph1-14" x="1152.738281" y="44"/> <use xlink:href="#glyph1-13" x="1243.714844" y="44"/>
</g> </g>
</g> </g>
<path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(40%,80%,40%);stroke-opacity:1;stroke-miterlimit:10;" d="M 11.480469 46 L 11.480469 60 "/> <path style="fill:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(40%,80%,40%);stroke-opacity:1;stroke-miterlimit:10;" d="M 10.746094 46 L 10.746094 60 "/>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 61 KiB

View File

@ -1,6 +1,7 @@
(lang dune 3.9) (lang dune 3.9)
(name eio) (name eio)
(formatting disabled) (formatting disabled)
(subst disabled)
(generate_opam_files true) (generate_opam_files true)
(source (github ocaml-multicore/eio)) (source (github ocaml-multicore/eio))
(license ISC) (license ISC)
@ -13,7 +14,7 @@
(description "An effect-based IO API for multicore OCaml with fibers.") (description "An effect-based IO API for multicore OCaml with fibers.")
(conflicts (seq (< 0.3))) (conflicts (seq (< 0.3)))
(depends (depends
(ocaml (>= 5.1.0)) (ocaml (>= 5.2.0))
(bigstringaf (>= 0.9.0)) (bigstringaf (>= 0.9.0))
(cstruct (>= 6.0.1)) (cstruct (>= 6.0.1))
lwt-dllist lwt-dllist
@ -48,6 +49,7 @@
(eio (= :version)) (eio (= :version))
(iomux (>= 0.2)) (iomux (>= 0.2))
(mdx (and (>= 2.4.1) :with-test)) (mdx (and (>= 2.4.1) :with-test))
(conf-bash :with-test)
(fmt (>= 0.8.9)))) (fmt (>= 0.8.9))))
(package (package
(name eio_windows) (name eio_windows)

View File

@ -10,7 +10,7 @@ doc: "https://ocaml-multicore.github.io/eio/"
bug-reports: "https://github.com/ocaml-multicore/eio/issues" bug-reports: "https://github.com/ocaml-multicore/eio/issues"
depends: [ depends: [
"dune" {>= "3.9"} "dune" {>= "3.9"}
"ocaml" {>= "5.1.0"} "ocaml" {>= "5.2.0"}
"bigstringaf" {>= "0.9.0"} "bigstringaf" {>= "0.9.0"}
"cstruct" {>= "6.0.1"} "cstruct" {>= "6.0.1"}
"lwt-dllist" "lwt-dllist"
@ -29,7 +29,6 @@ conflicts: [
"seq" {< "0.3"} "seq" {< "0.3"}
] ]
build: [ build: [
["dune" "subst"] {dev}
[ [
"dune" "dune"
"build" "build"

View File

@ -20,7 +20,6 @@ depends: [
"odoc" {with-doc} "odoc" {with-doc}
] ]
build: [ build: [
["dune" "subst"] {dev}
[ [
"dune" "dune"
"build" "build"

View File

@ -21,7 +21,6 @@ depends: [
"odoc" {with-doc} "odoc" {with-doc}
] ]
build: [ build: [
["dune" "subst"] {dev}
[ [
"dune" "dune"
"build" "build"

View File

@ -13,11 +13,11 @@ depends: [
"eio" {= version} "eio" {= version}
"iomux" {>= "0.2"} "iomux" {>= "0.2"}
"mdx" {>= "2.4.1" & with-test} "mdx" {>= "2.4.1" & with-test}
"conf-bash" {with-test}
"fmt" {>= "0.8.9"} "fmt" {>= "0.8.9"}
"odoc" {with-doc} "odoc" {with-doc}
] ]
build: [ build: [
["dune" "subst"] {dev}
[ [
"dune" "dune"
"build" "build"

View File

@ -17,7 +17,6 @@ depends: [
"odoc" {with-doc} "odoc" {with-doc}
] ]
build: [ build: [
["dune" "subst"] {dev}
[ [
"dune" "dune"
"build" "build"

View File

@ -1,21 +1,32 @@
(* Walks the directory tree rooted at the current directory, (* Walk the directory tree rooted at the current directory,
displaying all directory names (skipping hidden directories and `_build`). *) showing a summary for any .mli files. *)
open Eio.Std
let ( / ) = Eio.Path.( / ) let ( / ) = Eio.Path.( / )
let rec scan t = let is_doc_comment = String.starts_with ~prefix:"(** "
(* Print the first line of [t]'s doc-comment, if any *)
let scan_mli t f =
Eio.Path.with_lines t (fun lines ->
Seq.find is_doc_comment lines
|> Option.iter (fun line ->
let stop = String.index_from_opt line 4 '*' |> Option.value ~default:(String.length line) in
Format.fprintf f "%a: %s@." Eio.Path.pp t (String.sub line 4 (stop - 4))
)
)
(* Walk the tree rooted at [t] and scan any .mli files found. *)
let rec scan t f =
match Eio.Path.kind ~follow:false t with match Eio.Path.kind ~follow:false t with
| `Directory -> | `Directory ->
traceln "Visiting %a" Eio.Path.pp t;
Eio.Path.read_dir t |> List.iter (function Eio.Path.read_dir t |> List.iter (function
| "_build" -> () | "_build" | "_opam" -> () (* Don't examine these directories *)
| item when String.starts_with ~prefix:"." item -> () | item when String.starts_with ~prefix:"." item -> () (* Skip hidden items *)
| item -> scan (t / item) | item -> scan (t / item) f
) )
| `Regular_file when Filename.check_suffix (snd t) ".mli" -> scan_mli t f
| _ -> () | _ -> ()
let () = let () =
Eio_main.run @@ fun env -> Eio_main.run @@ fun env ->
scan (Eio.Stdenv.cwd env) scan (Eio.Stdenv.cwd env) Format.std_formatter

View File

@ -218,6 +218,13 @@ val seq : ?stop:bool parser -> 'a parser -> 'a Seq.t parser
It is not necessary to consume all the elements of the It is not necessary to consume all the elements of the
sequence. sequence.
Example ([head 4] is a parser that takes 4 lines):
{[
let head n r =
r |> Buf_read.(seq line) |> Seq.take n |> List.of_seq
]}
@param stop This is used before parsing each item. @param stop This is used before parsing each item.
The sequence ends if this returns [true]. The sequence ends if this returns [true].
The default is {!at_end_of_input}. *) The default is {!at_end_of_input}. *)

View File

@ -1,4 +1,4 @@
(library (library
(name eio__core) (name eio__core)
(public_name eio.core) (public_name eio.core)
(libraries cstruct hmap lwt-dllist fmt optint domain-local-await eio.runtime_events)) (libraries hmap lwt-dllist fmt optint eio.runtime_events))

View File

@ -7,6 +7,7 @@ module Private = struct
module Suspend = Suspend module Suspend = Suspend
module Cells = Cells module Cells = Cells
module Broadcast = Broadcast module Broadcast = Broadcast
module Single_waiter = Single_waiter
module Trace = Trace module Trace = Trace
module Fiber_context = Cancel.Fiber_context module Fiber_context = Cancel.Fiber_context
module Debug = Debug module Debug = Debug
@ -18,6 +19,4 @@ module Private = struct
| Fork = Fiber.Fork | Fork = Fiber.Fork
| Get_context = Cancel.Get_context | Get_context = Cancel.Get_context
end end
module Dla = Dla
end end

View File

@ -606,6 +606,7 @@ module Private : sig
module Cells = Cells module Cells = Cells
module Broadcast = Broadcast module Broadcast = Broadcast
module Single_waiter = Single_waiter
(** Every fiber has an associated context. *) (** Every fiber has an associated context. *)
module Fiber_context : sig module Fiber_context : sig
@ -781,8 +782,4 @@ module Private : sig
val v : t val v : t
(** Backends should use this for {!Eio.Stdenv.debug}. *) (** Backends should use this for {!Eio.Stdenv.debug}. *)
end end
module Dla : sig
val prepare_for_await : unit -> Domain_local_await.t
end
end end

View File

@ -39,7 +39,8 @@ let fork_daemon ~sw f =
(* The daemon was cancelled because all non-daemon fibers are finished. *) (* The daemon was cancelled because all non-daemon fibers are finished. *)
() ()
| exception ex -> | exception ex ->
Switch.fail sw ex; (* The [with_daemon] ensures this will succeed *) let bt = Printexc.get_raw_backtrace () in
Switch.fail ~bt sw ex; (* The [with_daemon] ensures this will succeed *)
) (* else the fiber should report the error to [sw], but [sw] is failed anyway *) ) (* else the fiber should report the error to [sw], but [sw] is failed anyway *)
let fork_promise ~sw f = let fork_promise ~sw f =
@ -65,7 +66,8 @@ let fork_promise_exn ~sw f =
match Switch.with_op sw f with match Switch.with_op sw f with
| x -> Promise.resolve r x | x -> Promise.resolve r x
| exception ex -> | exception ex ->
Switch.fail sw ex (* The [with_op] ensures this will succeed *) let bt = Printexc.get_raw_backtrace () in
Switch.fail ~bt sw ex (* The [with_op] ensures this will succeed *)
); );
p p
@ -226,7 +228,7 @@ module List = struct
let release t = let release t =
t.free_fibers <- t.free_fibers + 1; t.free_fibers <- t.free_fibers + 1;
if t.free_fibers = 1 then Single_waiter.wake t.cond (Ok ()) if t.free_fibers = 1 then Single_waiter.wake_if_sleeping t.cond
let use t fn x = let use t fn x =
await_free t; await_free t;

View File

@ -1,25 +1,32 @@
(* Allows a single fiber to wait to be notified by another fiber in the same domain. type 'a state =
If multiple fibers need to wait at once, or the notification comes from another domain, | Running
this can't be used. *) | Sleeping of (('a, exn) result -> unit)
type 'a t = { type 'a t = 'a state ref
mutable wake : ('a, exn) result -> unit;
}
let create () = { wake = ignore } let create () = ref Running
let wake t v = t.wake v let wake t v =
match !t with
| Running -> false
| Sleeping fn ->
t := Running;
fn v;
true
let wake_if_sleeping t =
ignore (wake t (Ok ()) : bool)
let await t op id = let await t op id =
let x = let x =
Suspend.enter op @@ fun ctx enqueue -> Suspend.enter op @@ fun ctx enqueue ->
Cancel.Fiber_context.set_cancel_fn ctx (fun ex -> Cancel.Fiber_context.set_cancel_fn ctx (fun ex ->
t.wake <- ignore; t := Running;
enqueue (Error ex) enqueue (Error ex)
); );
t.wake <- (fun x -> t := Sleeping (fun x ->
Cancel.Fiber_context.clear_cancel_fn ctx; Cancel.Fiber_context.clear_cancel_fn ctx;
t.wake <- ignore; t := Running;
enqueue x enqueue x
) )
in in
@ -29,7 +36,7 @@ let await t op id =
let await_protect t op id = let await_protect t op id =
let x = let x =
Suspend.enter_unchecked op @@ fun _ctx enqueue -> Suspend.enter_unchecked op @@ fun _ctx enqueue ->
t.wake <- (fun x -> t.wake <- ignore; enqueue x) t := Sleeping (fun x -> t := Running; enqueue x)
in in
Trace.get id; Trace.get id;
x x

View File

@ -0,0 +1,25 @@
(** Allows a single fiber to wait to be notified by another fiber in the same domain.
If multiple fibers need to wait at once, or the notification comes from another domain,
this can't be used. *)
type 'a t
(** A handle representing a fiber that might be sleeping.
It is either in the Running or Sleeping state. *)
val create : unit -> 'a t
(** [create ()] is a new waiter, initially in the Running state. *)
val wake : 'a t -> ('a, exn) result -> bool
(** [wake t v] resumes [t]'s fiber with value [v] and returns [true] if it was sleeping.
If [t] is Running then this just returns [false]. *)
val wake_if_sleeping : unit t -> unit
(** [wake_if_sleeping] is [ignore (wake t (Ok ()))]. *)
val await : 'a t -> string -> Trace.id -> 'a
(** [await t op id] suspends the calling fiber, changing [t]'s state to Sleeping.
If the fiber is cancelled, a cancel exception is raised.
[op] and [id] are used for tracing. *)
val await_protect : 'a t -> string -> Trace.id -> 'a
(** [await_protect] is like {!await}, but the sleep cannot be cancelled. *)

View File

@ -72,7 +72,7 @@ let dec_fibers t =
if t.daemon_fibers > 0 && t.fibers = t.daemon_fibers then if t.daemon_fibers > 0 && t.fibers = t.daemon_fibers then
Cancel.cancel t.cancel Exit; Cancel.cancel t.cancel Exit;
if t.fibers = 0 then if t.fibers = 0 then
Single_waiter.wake t.waiter (Ok ()) Single_waiter.wake_if_sleeping t.waiter
let with_op t fn = let with_op t fn =
inc_fibers t; inc_fibers t;

View File

@ -105,7 +105,7 @@ let run_full main =
let result = ref None in let result = ref None in
let `Exit_scheduler = let `Exit_scheduler =
Domain_local_await.using Domain_local_await.using
~prepare_for_await:Eio.Private.Dla.prepare_for_await ~prepare_for_await:Eio_utils.Dla.prepare_for_await
~while_running:(fun () -> ~while_running:(fun () ->
fork ~new_fiber (fun () -> result := Some (main stdenv))) in fork ~new_fiber (fun () -> result := Some (main stdenv))) in
match !result with match !result with

View File

@ -231,6 +231,9 @@ val run_server :
In such cases you must ensure that [connection_handler] only accesses thread-safe values. In such cases you must ensure that [connection_handler] only accesses thread-safe values.
Note that having more than {!Domain.recommended_domain_count} domains in total is likely to result in bad performance. Note that having more than {!Domain.recommended_domain_count} domains in total is likely to result in bad performance.
For services that are bottlenecked on CPU rather than IO,
you can run a single accept loop and have the handler submit CPU-intensive jobs to an {!module:Executor_pool}.
@param max_connections The maximum number of concurrent connections accepted by [s] at any time. @param max_connections The maximum number of concurrent connections accepted by [s] at any time.
The default is [Int.max_int]. The default is [Int.max_int].
@param stop Resolving this promise causes [s] to stop accepting new connections. @param stop Resolving this promise causes [s] to stop accepting new connections.

View File

@ -1,11 +1,22 @@
type 'a t = 'a Fs.dir * Fs.path type 'a t = 'a Fs.dir * Fs.path
(* Like [Filename.is_relative] but always using "/" as the separator. *)
let is_relative = function
| "" -> true
| x -> x.[0] <> '/'
(* Like [Filename.concat] but always using "/" as the separator. *)
let concat a b =
let l = String.length a in
if l = 0 || a.[l - 1] = '/' then a ^ b
else a ^ "/" ^ b
let ( / ) (dir, p1) p2 = let ( / ) (dir, p1) p2 =
match p1, p2 with match p1, p2 with
| p1, "" -> (dir, Filename.concat p1 p2) | p1, "" -> (dir, concat p1 p2)
| _, p2 when not (Filename.is_relative p2) -> (dir, p2) | _, p2 when not (is_relative p2) -> (dir, p2)
| ".", p2 -> (dir, p2) | ".", p2 -> (dir, p2)
| p1, p2 -> (dir, Filename.concat p1 p2) | p1, p2 -> (dir, concat p1 p2)
let pp f (Resource.T (t, ops), p) = let pp f (Resource.T (t, ops), p) =
let module X = (val (Resource.get ops Fs.Pi.Dir)) in let module X = (val (Resource.get ops Fs.Pi.Dir)) in

View File

@ -1,3 +1,7 @@
type error = ECONNRESET
exception Unix_error of error * string * string
type file_descr = [`Open | `Closed] Atomic.t type file_descr = [`Open | `Closed] Atomic.t
let make () = Atomic.make `Open let make () = Atomic.make `Open

View File

@ -1,6 +1,7 @@
(library (library
(name eio_unix) (name eio_unix)
(public_name eio.unix) (public_name eio.unix)
(public_headers include/fork_action.h)
(foreign_stubs (foreign_stubs
(language c) (language c)
(include_dirs include) (include_dirs include)

View File

@ -68,9 +68,9 @@ type _ Effect.t +=
| Socketpair_datagram : Switch.t * Unix.socket_domain * int -> | Socketpair_datagram : Switch.t * Unix.socket_domain * int ->
([`Unix_fd | datagram_socket_ty] r * [`Unix_fd | datagram_socket_ty] r) Effect.t ([`Unix_fd | datagram_socket_ty] r * [`Unix_fd | datagram_socket_ty] r) Effect.t
let open_stream s = (s : _ stream_socket :> [< `Unix_fd | stream_socket_ty] r) let open_stream s = (s : [`Unix_fd | stream_socket_ty] r :> [< `Unix_fd | stream_socket_ty] r)
let open_listening s = (s : _ listening_socket :> [< `Unix_fd | listening_socket_ty] r) let open_listening s = (s : [`Unix_fd | listening_socket_ty] r :> [< `Unix_fd | listening_socket_ty] r)
let open_datagram s = (s : _ datagram_socket :> [< `Unix_fd | datagram_socket_ty] r) let open_datagram s = (s : [`Unix_fd | datagram_socket_ty] r :> [< `Unix_fd | datagram_socket_ty] r)
let import_socket_stream ~sw ~close_unix fd = let import_socket_stream ~sw ~close_unix fd =
open_stream @@ Effect.perform (Import_socket_stream (sw, close_unix, fd)) open_stream @@ Effect.perform (Import_socket_stream (sw, close_unix, fd))
@ -86,7 +86,8 @@ let socketpair_stream ~sw ?(domain=Unix.PF_UNIX) ?(protocol=0) () =
(open_stream a, open_stream b) (open_stream a, open_stream b)
let socketpair_datagram ~sw ?(domain=Unix.PF_UNIX) ?(protocol=0) () = let socketpair_datagram ~sw ?(domain=Unix.PF_UNIX) ?(protocol=0) () =
Effect.perform (Socketpair_datagram (sw, domain, protocol)) let a, b = Effect.perform (Socketpair_datagram (sw, domain, protocol)) in
(open_datagram a, open_datagram b)
let fd socket = let fd socket =
Option.get (Resource.fd_opt socket) Option.get (Resource.fd_opt socket)

View File

@ -55,7 +55,7 @@ end
(** {2 Creating or importing sockets} *) (** {2 Creating or importing sockets} *)
val import_socket_stream : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [`Unix_fd | stream_socket_ty] r val import_socket_stream : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [< `Unix_fd | stream_socket_ty] r
(** [import_socket_stream ~sw ~close_unix fd] is an Eio flow that uses [fd]. (** [import_socket_stream ~sw ~close_unix fd] is an Eio flow that uses [fd].
It can be cast to e.g. {!source} for a one-way flow. It can be cast to e.g. {!source} for a one-way flow.
@ -63,14 +63,14 @@ val import_socket_stream : sw:Switch.t -> close_unix:bool -> Unix.file_descr ->
The [close_unix] and [sw] arguments are passed to {!Fd.of_unix}. *) The [close_unix] and [sw] arguments are passed to {!Fd.of_unix}. *)
val import_socket_listening : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [`Unix_fd | listening_socket_ty] r val import_socket_listening : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [< `Unix_fd | listening_socket_ty] r
(** [import_socket_listening ~sw ~close_unix fd] is an Eio listening socket that uses [fd]. (** [import_socket_listening ~sw ~close_unix fd] is an Eio listening socket that uses [fd].
The socket object will be closed when [sw] finishes. The socket object will be closed when [sw] finishes.
The [close_unix] and [sw] arguments are passed to {!Fd.of_unix}. *) The [close_unix] and [sw] arguments are passed to {!Fd.of_unix}. *)
val import_socket_datagram : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [`Unix_fd | datagram_socket_ty] r val import_socket_datagram : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [< `Unix_fd | datagram_socket_ty] r
(** [import_socket_datagram ~sw ~close_unix fd] is an Eio datagram socket that uses [fd]. (** [import_socket_datagram ~sw ~close_unix fd] is an Eio datagram socket that uses [fd].
The socket object will be closed when [sw] finishes. The socket object will be closed when [sw] finishes.
@ -82,7 +82,7 @@ val socketpair_stream :
?domain:Unix.socket_domain -> ?domain:Unix.socket_domain ->
?protocol:int -> ?protocol:int ->
unit -> unit ->
[`Unix_fd | stream_socket_ty] r * [`Unix_fd | stream_socket_ty] r [< `Unix_fd | stream_socket_ty] r * [< `Unix_fd | stream_socket_ty] r
(** [socketpair_stream ~sw ()] returns a connected pair of flows, such that writes to one can be read by the other. (** [socketpair_stream ~sw ()] returns a connected pair of flows, such that writes to one can be read by the other.
This creates OS-level resources using [socketpair(2)]. This creates OS-level resources using [socketpair(2)].
@ -93,7 +93,7 @@ val socketpair_datagram :
?domain:Unix.socket_domain -> ?domain:Unix.socket_domain ->
?protocol:int -> ?protocol:int ->
unit -> unit ->
[`Unix_fd | datagram_socket_ty] r * [`Unix_fd | datagram_socket_ty] r [< `Unix_fd | datagram_socket_ty] r * [< `Unix_fd | datagram_socket_ty] r
(** [socketpair_datagram ~sw ()] returns a connected pair of flows, such that writes to one can be read by the other. (** [socketpair_datagram ~sw ()] returns a connected pair of flows, such that writes to one can be read by the other.
This creates OS-level resources using [socketpair(2)]. This creates OS-level resources using [socketpair(2)].

View File

@ -98,7 +98,13 @@ let get t =
None None
let close_fd fd = let close_fd fd =
Eio.Private.Trace.with_span "close" (fun () -> Unix.close fd) Eio.Private.Trace.with_span "close" (fun () ->
try
Unix.close fd
with Unix.Unix_error (ECONNRESET, _, _) ->
(* For FreeBSD. See https://github.com/ocaml-multicore/eio/issues/786 *)
()
)
(* Note: we could simplify this a bit by incrementing [t.ops], as [remove] does. (* Note: we could simplify this a bit by incrementing [t.ops], as [remove] does.
However, that makes dscheck too slow. *) However, that makes dscheck too slow. *)

View File

@ -7,10 +7,10 @@ let prepare_for_await () =
| _ -> () | _ -> ()
and await () = and await () =
if Atomic.get state != `Released then if Atomic.get state != `Released then
Suspend.enter "domain-local-await" @@ fun ctx enqueue -> Eio.Private.Suspend.enter "domain-local-await" @@ fun ctx enqueue ->
let awaiting = `Awaiting enqueue in let awaiting = `Awaiting enqueue in
if Atomic.compare_and_set state `Init awaiting then ( if Atomic.compare_and_set state `Init awaiting then (
Cancel.Fiber_context.set_cancel_fn ctx (fun ex -> Eio.Private.Fiber_context.set_cancel_fn ctx (fun ex ->
if Atomic.compare_and_set state awaiting `Released then ( if Atomic.compare_and_set state awaiting `Released then (
enqueue (Error ex) enqueue (Error ex)
) )

1
lib_eio/utils/dla.mli Normal file
View File

@ -0,0 +1 @@
val prepare_for_await : unit -> Domain_local_await.t

View File

@ -1,4 +1,4 @@
(library (library
(name eio_utils) (name eio_utils)
(public_name eio.utils) (public_name eio.utils)
(libraries eio psq fmt optint)) (libraries eio psq fmt optint domain-local-await))

View File

@ -5,3 +5,4 @@
module Lf_queue = Lf_queue module Lf_queue = Lf_queue
module Suspended = Suspended module Suspended = Suspended
module Zzz = Zzz module Zzz = Zzz
module Dla = Dla

View File

@ -39,7 +39,11 @@
#ifndef SYS_clone3 #ifndef SYS_clone3
# define SYS_clone3 435 # define SYS_clone3 435
# define CLONE_PIDFD 0x00001000 # define CLONE_PIDFD 0x00001000
struct clone_args { #endif
// struct clone_args isn't defined in linux-lts headers, so define it here
// Note that this struct is versioned by size. See linux/sched.h for details
struct caml_eio_clone_args {
uint64_t flags; uint64_t flags;
uint64_t pidfd; uint64_t pidfd;
uint64_t child_tid; uint64_t child_tid;
@ -48,11 +52,7 @@ struct clone_args {
uint64_t stack; uint64_t stack;
uint64_t stack_size; uint64_t stack_size;
uint64_t tls; uint64_t tls;
uint64_t set_tid;
uint64_t set_tid_size;
uint64_t cgroup;
}; };
#endif
// Make sure we have enough space for at least one entry. // Make sure we have enough space for at least one entry.
#define DIRENT_BUF_SIZE (PATH_MAX + sizeof(struct dirent64)) #define DIRENT_BUF_SIZE (PATH_MAX + sizeof(struct dirent64))
@ -178,9 +178,9 @@ static int pidfd_open(pid_t pid, unsigned int flags) {
/* Like clone3, but falls back to fork if not supported. /* Like clone3, but falls back to fork if not supported.
Also, raises exceptions rather then returning an error. */ Also, raises exceptions rather then returning an error. */
static pid_t clone3_with_fallback(struct clone_args *cl_args) { static pid_t clone3_with_fallback(struct caml_eio_clone_args *cl_args) {
int *pidfd = (int *)(uintptr_t) cl_args->pidfd; int *pidfd = (int *)(uintptr_t) cl_args->pidfd;
pid_t child_pid = syscall(SYS_clone3, cl_args, sizeof(struct clone_args)); pid_t child_pid = syscall(SYS_clone3, cl_args, sizeof(struct caml_eio_clone_args));
if (child_pid >= 0) if (child_pid >= 0)
return child_pid; /* Success! */ return child_pid; /* Success! */
@ -216,7 +216,7 @@ CAMLprim value caml_eio_clone3(value v_errors, value v_actions) {
CAMLlocal1(v_result); CAMLlocal1(v_result);
pid_t child_pid; pid_t child_pid;
int pidfd = -1; /* Is automatically close-on-exec */ int pidfd = -1; /* Is automatically close-on-exec */
struct clone_args cl_args = { struct caml_eio_clone_args cl_args = {
.flags = CLONE_PIDFD, .flags = CLONE_PIDFD,
.pidfd = (uintptr_t) &pidfd, .pidfd = (uintptr_t) &pidfd,
.exit_signal = SIGCHLD, /* Needed for wait4 to work if we exit before exec */ .exit_signal = SIGCHLD, /* Needed for wait4 to work if we exit before exec */

View File

@ -207,11 +207,39 @@ let write ?file_offset:off fd buf len =
raise @@ Err.wrap (Uring.error_of_errno res) "write" "" raise @@ Err.wrap (Uring.error_of_errno res) "write" ""
) )
let alloc_fixed () = Effect.perform Sched.Alloc let alloc_fixed () =
let s = Sched.get () in
match s.mem with
| None -> None
| Some mem ->
match Uring.Region.alloc mem with
| buf -> Some buf
| exception Uring.Region.No_space -> None
let alloc_fixed_or_wait () = Effect.perform Sched.Alloc_or_wait let alloc_fixed_or_wait () =
let s = Sched.get () in
match s.mem with
| None -> failwith "No fixed buffer available"
| Some mem ->
match Uring.Region.alloc mem with
| buf -> buf
| exception Uring.Region.No_space ->
let id = Eio.Private.Trace.mint_id () in
let trigger = Eio.Private.Single_waiter.create () in
let node = Lwt_dllist.add_r trigger s.mem_q in
try
Eio.Private.Single_waiter.await trigger "alloc_fixed_or_wait" id
with ex ->
Lwt_dllist.remove node;
raise ex
let free_fixed buf = Effect.perform (Sched.Free buf) let rec free_fixed buf =
let s = Sched.get () in
match Lwt_dllist.take_opt_l s.mem_q with
| None -> Uring.Region.free buf
| Some k ->
if not (Eio.Private.Single_waiter.wake k (Ok buf)) then
free_fixed buf (* [k] was already cancelled, but not yet removed from the queue *)
let splice src ~dst ~len = let splice src ~dst ~len =
Fd.use_exn "splice-src" src @@ fun src -> Fd.use_exn "splice-src" src @@ fun src ->
@ -571,7 +599,10 @@ module Process = struct
let exit_status, set_exit_status = Promise.create () in let exit_status, set_exit_status = Promise.create () in
let t = let t =
Fd.use_exn "errors-w" errors_w @@ fun errors_w -> Fd.use_exn "errors-w" errors_w @@ fun errors_w ->
let pid, pid_fd = eio_spawn errors_w c_actions in let pid, pid_fd =
Eio.Private.Trace.with_span "spawn" @@ fun () ->
eio_spawn errors_w c_actions
in
let pid_fd = Fd.of_unix ~sw ~seekable:false ~close_unix:true pid_fd in let pid_fd = Fd.of_unix ~sw ~seekable:false ~close_unix:true pid_fd in
{ pid; pid_fd; exit_status } { pid; pid_fd; exit_status }
in in

View File

@ -50,7 +50,7 @@ type t = {
uring: io_job Uring.t; uring: io_job Uring.t;
mem: Uring.Region.t option; mem: Uring.Region.t option;
io_q: (t -> unit) Queue.t; (* waiting for room on [uring] *) io_q: (t -> unit) Queue.t; (* waiting for room on [uring] *)
mem_q : Uring.Region.chunk Suspended.t Queue.t; mem_q : Uring.Region.chunk Eio.Private.Single_waiter.t Lwt_dllist.t;
(* The queue of runnable fibers ready to be resumed. Note: other domains can also add work items here. *) (* The queue of runnable fibers ready to be resumed. Note: other domains can also add work items here. *)
run_q : runnable Lf_queue.t; run_q : runnable Lf_queue.t;
@ -74,9 +74,9 @@ type t = {
type _ Effect.t += type _ Effect.t +=
| Enter : (t -> 'a Suspended.t -> unit) -> 'a Effect.t | Enter : (t -> 'a Suspended.t -> unit) -> 'a Effect.t
| Cancel : io_job Uring.job -> unit Effect.t | Cancel : io_job Uring.job -> unit Effect.t
| Alloc : Uring.Region.chunk option Effect.t | Get : t Effect.t
| Alloc_or_wait : Uring.Region.chunk Effect.t
| Free : Uring.Region.chunk -> unit Effect.t let get () = Effect.perform Get
let wake_buffer = let wake_buffer =
let b = Bytes.create 8 in let b = Bytes.create 8 in
@ -247,7 +247,7 @@ let rec schedule ({run_q; sleep_q; mem_q; uring; _} as st) : [`Exit_scheduler] =
) else if timeout = None && Uring.active_ops uring = 0 then ( ) else if timeout = None && Uring.active_ops uring = 0 then (
(* Nothing further can happen at this point. (* Nothing further can happen at this point.
If there are no events in progress but also still no memory available, something has gone wrong! *) If there are no events in progress but also still no memory available, something has gone wrong! *)
assert (Queue.length mem_q = 0); assert (Lwt_dllist.length mem_q = 0);
Lf_queue.close st.run_q; (* Just to catch bugs if something tries to enqueue later *) Lf_queue.close st.run_q; (* Just to catch bugs if something tries to enqueue later *)
`Exit_scheduler `Exit_scheduler
) else ( ) else (
@ -339,21 +339,6 @@ and complete_rw_req st ({len; cur_off; action; _} as req) res =
| _, Exactly len -> Suspended.continue action len | _, Exactly len -> Suspended.continue action len
| n, Upto _ -> Suspended.continue action n | n, Upto _ -> Suspended.continue action n
let alloc_buf_or_wait st k =
match st.mem with
| None -> Suspended.discontinue k (Failure "No fixed buffer available")
| Some mem ->
match Uring.Region.alloc mem with
| buf -> Suspended.continue k buf
| exception Uring.Region.No_space ->
Queue.push k st.mem_q;
schedule st
let free_buf st buf =
match Queue.take_opt st.mem_q with
| None -> Uring.Region.free buf
| Some k -> enqueue_thread st k buf
let rec enqueue_poll_add fd poll_mask st action = let rec enqueue_poll_add fd poll_mask st action =
Trace.log "poll_add"; Trace.log "poll_add";
let retry = with_cancel_hook ~action st (fun () -> let retry = with_cancel_hook ~action st (fun () ->
@ -411,8 +396,9 @@ let run ~extra_effects st main arg =
Fiber_context.destroy fiber; Fiber_context.destroy fiber;
Printexc.raise_with_backtrace ex (Printexc.get_raw_backtrace ()) Printexc.raise_with_backtrace ex (Printexc.get_raw_backtrace ())
); );
effc = fun (type a) (e : a Effect.t) -> effc = fun (type a) (e : a Effect.t) : ((a, _) continuation -> _) option ->
match e with match e with
| Get -> Some (fun k -> continue k st)
| Enter fn -> Some (fun k -> | Enter fn -> Some (fun k ->
match Fiber_context.get_error fiber with match Fiber_context.get_error fiber with
| Some e -> discontinue k e | Some e -> discontinue k e
@ -467,22 +453,6 @@ let run ~extra_effects st main arg =
Eio_unix.Private.Thread_pool.submit st.thread_pool ~ctx:fiber ~enqueue fn; Eio_unix.Private.Thread_pool.submit st.thread_pool ~ctx:fiber ~enqueue fn;
schedule st schedule st
) )
| Alloc -> Some (fun k ->
match st.mem with
| None -> continue k None
| Some mem ->
match Uring.Region.alloc mem with
| buf -> continue k (Some buf)
| exception Uring.Region.No_space -> continue k None
)
| Alloc_or_wait -> Some (fun k ->
let k = { Suspended.k; fiber } in
alloc_buf_or_wait st k
)
| Free buf -> Some (fun k ->
free_buf st buf;
continue k ()
)
| e -> extra_effects.effc e | e -> extra_effects.effc e
} }
in in
@ -490,7 +460,7 @@ let run ~extra_effects st main arg =
let `Exit_scheduler = let `Exit_scheduler =
let new_fiber = Fiber_context.make_root () in let new_fiber = Fiber_context.make_root () in
Domain_local_await.using Domain_local_await.using
~prepare_for_await:Eio.Private.Dla.prepare_for_await ~prepare_for_await:Eio_utils.Dla.prepare_for_await
~while_running:(fun () -> ~while_running:(fun () ->
fork ~new_fiber (fun () -> fork ~new_fiber (fun () ->
Switch.run_protected ~name:"eio_linux" (fun sw -> Switch.run_protected ~name:"eio_linux" (fun sw ->
@ -551,7 +521,10 @@ let with_sched ?(fallback=no_fallback) config fn =
Uring.exit uring; Uring.exit uring;
fallback (`Msg "Linux >= 5.15 is required for io_uring support") fallback (`Msg "Linux >= 5.15 is required for io_uring support")
) else ( ) else (
statx_works := Uring.op_supported probe Uring.Op.msg_ring; (* The reason for an if here is to make sure we only set it once, when
the first domain is starting. This is just to avoid a tsan warning. *)
if not !statx_works && Uring.op_supported probe Uring.Op.msg_ring then
statx_works := true;
match match
let mem = let mem =
let fixed_buf_len = block_size * n_blocks in let fixed_buf_len = block_size * n_blocks in
@ -566,7 +539,7 @@ let with_sched ?(fallback=no_fallback) config fn =
Lf_queue.push run_q IO; Lf_queue.push run_q IO;
let sleep_q = Zzz.create () in let sleep_q = Zzz.create () in
let io_q = Queue.create () in let io_q = Queue.create () in
let mem_q = Queue.create () in let mem_q = Lwt_dllist.create () in
with_eventfd @@ fun eventfd -> with_eventfd @@ fun eventfd ->
let thread_pool = Eio_unix.Private.Thread_pool.create ~sleep_q in let thread_pool = Eio_unix.Private.Thread_pool.create ~sleep_q in
fn { mem; uring; run_q; io_q; mem_q; eventfd; need_wakeup = Atomic.make false; sleep_q; thread_pool } fn { mem; uring; run_q; io_q; mem_q; eventfd; need_wakeup = Atomic.make false; sleep_q; thread_pool }

View File

@ -211,19 +211,52 @@ let test_signal_race () =
(fun () -> Eio.Condition.await_no_mutex cond) (fun () -> Eio.Condition.await_no_mutex cond)
(fun () -> ignore (Unix.setitimer ITIMER_REAL { it_interval = 0.; it_value = 0.001 } : Unix.interval_timer_status)) (fun () -> ignore (Unix.setitimer ITIMER_REAL { it_interval = 0.; it_value = 0.001 } : Unix.interval_timer_status))
let test_alloc_fixed_or_wait () =
Eio_linux.run ~n_blocks:1 @@ fun _env ->
let block = Eio_linux.Low_level.alloc_fixed_or_wait () in
(* We have to wait for the block, but get cancelled while waiting. *)
begin
try
Fiber.both
(fun () -> ignore (Eio_linux.Low_level.alloc_fixed_or_wait () : Uring.Region.chunk))
(fun () -> raise Exit);
with Exit -> ()
end;
(* We have to wait for the block, and get it when the old one is freed. *)
Fiber.both
(fun () ->
let x = Eio_linux.Low_level.alloc_fixed_or_wait () in
Eio_linux.Low_level.free_fixed x
)
(fun () ->
Eio_linux.Low_level.free_fixed block
);
(* The old block is passed to the waiting fiber, but it's cancelled. *)
let block = Eio_linux.Low_level.alloc_fixed_or_wait () in
Fiber.both
(fun () ->
Fiber.first
(fun () -> ignore (Eio_linux.Low_level.alloc_fixed_or_wait ()); assert false)
(fun () -> ())
)
(fun () -> Eio_linux.Low_level.free_fixed block);
let block = Eio_linux.Low_level.alloc_fixed_or_wait () in
Eio_linux.Low_level.free_fixed block
let () = let () =
let open Alcotest in let open Alcotest in
run "eio_linux" [ run "eio_linux" [
"io", [ "io", [
test_case "copy" `Quick test_copy; test_case "copy" `Quick test_copy;
test_case "direct_copy" `Quick test_direct_copy; test_case "direct_copy" `Quick test_direct_copy;
test_case "poll_add" `Quick test_poll_add; test_case "poll_add" `Quick test_poll_add;
test_case "poll_add_busy" `Quick test_poll_add_busy; test_case "poll_add_busy" `Quick test_poll_add_busy;
test_case "iovec" `Quick test_iovec; test_case "iovec" `Quick test_iovec;
test_case "no_sqe" `Quick test_no_sqe; test_case "no_sqe" `Quick test_no_sqe;
test_case "read_exact" `Quick test_read_exact; test_case "read_exact" `Quick test_read_exact;
test_case "expose_backend" `Quick test_expose_backend; test_case "expose_backend" `Quick test_expose_backend;
test_case "statx" `Quick test_statx; test_case "statx" `Quick test_statx;
test_case "signal_race" `Quick test_signal_race; test_case "signal_race" `Quick test_signal_race;
test_case "alloc-fixed-or-wait" `Quick test_alloc_fixed_or_wait;
]; ];
] ]

View File

@ -558,6 +558,7 @@ module Process = struct
let t = let t =
let pid = let pid =
Fd.use_exn "errors-w" errors_w @@ fun errors_w -> Fd.use_exn "errors-w" errors_w @@ fun errors_w ->
Eio.Private.Trace.with_span "spawn" @@ fun () ->
eio_spawn errors_w c_actions eio_spawn errors_w c_actions
in in
Fd.close errors_w; Fd.close errors_w;

View File

@ -379,7 +379,7 @@ let run ~extra_effects t main x =
let `Exit_scheduler = let `Exit_scheduler =
let new_fiber = Fiber_context.make_root () in let new_fiber = Fiber_context.make_root () in
Domain_local_await.using Domain_local_await.using
~prepare_for_await:Eio.Private.Dla.prepare_for_await ~prepare_for_await:Eio_utils.Dla.prepare_for_await
~while_running:(fun () -> ~while_running:(fun () ->
fork ~new_fiber (fun () -> fork ~new_fiber (fun () ->
Eio_unix.Private.Thread_pool.run t.thread_pool @@ fun () -> Eio_unix.Private.Thread_pool.run t.thread_pool @@ fun () ->

View File

@ -3,8 +3,8 @@
(enabled_if (= %{os_type} "Unix")) (enabled_if (= %{os_type} "Unix"))
(deps (package eio_posix))) (deps (package eio_posix)))
(test (tests
(name open_beneath) (names open_beneath test_await)
(package eio_posix) (package eio_posix)
(build_if (= %{os_type} "Unix")) (build_if (= %{os_type} "Unix"))
(libraries eio_posix)) (libraries eio_posix))

View File

@ -0,0 +1,25 @@
open Eio.Std
let () =
Eio_posix.run @@ fun _ ->
let a, b = Unix.(socketpair PF_UNIX SOCK_STREAM 0) in
(* Start awaiting readable/writable state, but cancel immediately. *)
try
Eio.Cancel.sub (fun cc ->
Fiber.all [
(fun () -> Eio_unix.await_readable a);
(fun () -> Eio_unix.await_writable b);
(fun () -> Eio.Cancel.cancel cc Exit);
];
assert false
)
with Eio.Cancel.Cancelled _ ->
(* Now wait for something else. Will fail if the old FDs are still being waited on. *)
let c, d = Unix.(socketpair PF_UNIX SOCK_STREAM 0) in
Unix.close a;
Unix.close b;
Fiber.first
(fun () -> Eio_unix.await_readable c)
(fun () -> Eio_unix.await_writable d);
Unix.close c;
Unix.close d

View File

@ -6,7 +6,7 @@
(foreign_stubs (foreign_stubs
(language c) (language c)
(include_dirs ../lib_eio/unix/include) (include_dirs ../lib_eio/unix/include)
(names eio_windows_stubs eio_windows_cstruct_stubs)) (names eio_windows_stubs))
(c_library_flags :standard -lbcrypt -lntdll) (c_library_flags :standard -lbcrypt -lntdll)
(libraries eio eio.unix eio.utils fmt)) (libraries eio eio.unix eio.utils fmt))

View File

@ -1,149 +0,0 @@
/* From mirage/ocaml-cstruct
Copyright (c) 2012 Anil Madhavapeddy <anil@recoil.org>
Copyright (c) 2012 Pierre Chambart
Copyright (c) Christiano F. Haesbaert <haesbaert@haesbaert.org>
Copyright (c) Citrix Inc
Copyright (c) David Sheets <sheets@alum.mit.edu>
Copyright (c) Drup <drupyog@zoho.com>
Copyright (c) Hannes Mehnert <hannes@mehnert.org>
Copyright (c) Jeremy Yallop <yallop@gmail.com>
Copyright (c) Mindy Preston <meetup@yomimono.org>
Copyright (c) Nicolas Ojeda Bar <n.oje.bar@gmail.com>
Copyright (c) Richard Mortier <mort@cantab.net>
Copyright (c) Rudi Grinberg <rudi.grinberg@gmail.com>
Copyright (c) Thomas Gazagnaire <thomas@gazagnaire.com>
Copyright (c) Thomas Leonard <talex5@gmail.com>
Copyright (c) Vincent Bernardoff <vb@luminar.eu.org>
Copyright (c) pqwy <david@numm.org>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/custom.h>
#include <caml/callback.h>
#include <caml/alloc.h>
#include <caml/unixsupport.h>
#include <caml/bigarray.h>
#include <caml/threads.h>
#include <caml/fail.h>
#include <stdio.h>
#include <errno.h>
CAMLprim value eio_windows_cstruct_read(value val_fd, value val_c)
{
CAMLparam2(val_fd, val_c);
CAMLlocal3(val_buf, val_ofs, val_len);
uint8_t *buf;
size_t len;
ssize_t n = 0;
int win32err = 0;
SOCKET s;
HANDLE h;
DWORD numread;
int ok;
val_buf = Field(val_c, 0);
val_ofs = Field(val_c, 1);
val_len = Field(val_c, 2);
buf = (uint8_t *)Caml_ba_data_val(val_buf) + Long_val(val_ofs);
len = (size_t)Long_val(val_len);
switch (Descr_kind_val(val_fd))
{
case KIND_SOCKET:
s = Socket_val(val_fd);
caml_release_runtime_system();
n = recv(s, buf, len, 0);
win32err = WSAGetLastError();
caml_acquire_runtime_system();
if (n == SOCKET_ERROR)
{
win32_maperr(win32err);
uerror("stub_cstruct_read", Nothing);
}
break;
case KIND_HANDLE:
h = Handle_val(val_fd);
caml_release_runtime_system();
ok = ReadFile(h, buf, len, &numread, NULL);
win32err = GetLastError();
n = numread;
caml_acquire_runtime_system();
if (!ok)
{
win32_maperr(win32err);
uerror("stub_cstruct_read", Nothing);
}
break;
default:
caml_failwith("unknown Descr_kind_val");
}
CAMLreturn(Val_int(n));
}
CAMLprim value eio_windows_cstruct_write(value val_fd, value val_c)
{
CAMLparam2(val_fd, val_c);
CAMLlocal3(val_buf, val_ofs, val_len);
val_buf = Field(val_c, 0);
val_ofs = Field(val_c, 1);
val_len = Field(val_c, 2);
void *buf = (char *)Caml_ba_data_val(val_buf) + Long_val(val_ofs);
size_t len = Long_val(val_len);
ssize_t n = 0;
int win32err = 0;
switch (Descr_kind_val(val_fd))
{
case KIND_SOCKET:
SOCKET s = Socket_val(val_fd);
caml_release_runtime_system();
n = send(s, buf, len, 0);
win32err = WSAGetLastError();
caml_acquire_runtime_system();
if (n == SOCKET_ERROR)
{
win32_maperr(win32err);
unix_error(errno, "stub_cstruct_write", Nothing);
}
break;
case KIND_HANDLE:
HANDLE h = Handle_val(val_fd);
DWORD numwritten;
caml_release_runtime_system();
int ok = WriteFile(h, buf, len, &numwritten, NULL);
win32err = GetLastError();
n = numwritten;
caml_acquire_runtime_system();
if (!ok)
{
win32_maperr(win32err);
uerror("stub_cstruct_write", Nothing);
}
break;
default:
caml_failwith("unknown Descr_kind_val");
}
CAMLreturn(Val_int(n));
}

View File

@ -237,7 +237,7 @@ CAMLprim value caml_eio_windows_unlinkat(value v_dirfd, value v_pathname, value
if (!NT_SUCCESS(r)) { if (!NT_SUCCESS(r)) {
caml_win32_maperr(RtlNtStatusToDosError(r)); caml_win32_maperr(RtlNtStatusToDosError(r));
uerror("openat", Nothing); uerror("openat", v_pathname);
} }
// Now close the file to delete it // Now close the file to delete it

View File

@ -20,7 +20,7 @@ let wrap code name arg =
| ENOENT -> Eio.Fs.err (Not_found e) | ENOENT -> Eio.Fs.err (Not_found e)
| EXDEV | EACCES | EPERM -> Eio.Fs.err (Permission_denied e) | EXDEV | EACCES | EPERM -> Eio.Fs.err (Permission_denied e)
| ECONNREFUSED -> Eio.Net.err (Connection_failure (Refused e)) | ECONNREFUSED -> Eio.Net.err (Connection_failure (Refused e))
| ECONNRESET | EPIPE -> Eio.Net.err (Connection_reset e) | ECONNRESET | EPIPE | ECONNABORTED -> Eio.Net.err (Connection_reset e)
| _ -> unclassified_error e | _ -> unclassified_error e
let run fn x = let run fn x =

View File

@ -82,7 +82,7 @@ end = struct
let dir = resolve t dir in let dir = resolve t dir in
Switch.run @@ fun sw -> Switch.run @@ fun sw ->
let open Low_level in let open Low_level in
let dirfd = Low_level.openat ~sw ~nofollow:true dir Flags.Open.(generic_read + synchronise) Flags.Disposition.(open_if) Flags.Create.(directory) in let dirfd = Err.run (Low_level.openat ~sw ~nofollow:true dir Flags.Open.(generic_read + synchronise) Flags.Disposition.(open_if)) Flags.Create.(directory) in
fn (Some dirfd) leaf fn (Some dirfd) leaf
) )
) else fn None path ) else fn None path

View File

@ -39,17 +39,25 @@ let rec do_nonblocking ty fn fd =
do_nonblocking ty fn fd do_nonblocking ty fn fd
let read fd buf start len = let read fd buf start len =
await_readable fd;
Fd.use_exn "read" fd @@ fun fd -> Fd.use_exn "read" fd @@ fun fd ->
do_nonblocking Read (fun fd -> Unix.read fd buf start len) fd do_nonblocking Read (fun fd -> Unix.read fd buf start len) fd
let read_cstruct fd buf = let read_cstruct fd (buf:Cstruct.t) =
await_readable fd;
Fd.use_exn "read_cstruct" fd @@ fun fd -> Fd.use_exn "read_cstruct" fd @@ fun fd ->
do_nonblocking Read (fun fd -> Unix_cstruct.read fd buf) fd do_nonblocking Read (fun fd -> Unix.read_bigarray fd buf.buffer buf.off buf.len) fd
let write fd buf start len = let write fd buf start len =
await_writable fd;
Fd.use_exn "write" fd @@ fun fd -> Fd.use_exn "write" fd @@ fun fd ->
do_nonblocking Write (fun fd -> Unix.write fd buf start len) fd do_nonblocking Write (fun fd -> Unix.write fd buf start len) fd
let write_cstruct fd (buf:Cstruct.t) =
await_writable fd;
Fd.use_exn "write_cstruct" fd @@ fun fd ->
do_nonblocking Write (fun fd -> Unix.write_bigarray fd buf.buffer buf.off buf.len) fd
let sleep_until time = let sleep_until time =
Sched.enter @@ fun t k -> Sched.enter @@ fun t k ->
Sched.await_timeout t k time Sched.await_timeout t k time
@ -148,8 +156,11 @@ let readv fd bufs =
do_nonblocking Read (fun fd -> eio_readv fd bufs) fd do_nonblocking Read (fun fd -> eio_readv fd bufs) fd
let writev fd bufs = let writev fd bufs =
Fd.use_exn "writev" fd @@ fun fd -> let rec loop buf = if Cstruct.length buf > 0 then begin
do_nonblocking Write (fun fd -> Unix_cstruct.writev fd bufs) fd let n = write_cstruct fd buf in
loop @@ Cstruct.shift buf n
end in
List.iter loop bufs
let preadv ~file_offset fd bufs = let preadv ~file_offset fd bufs =
Fd.use_exn "preadv" fd @@ fun fd -> Fd.use_exn "preadv" fd @@ fun fd ->

View File

@ -22,6 +22,7 @@ val sleep_until : Mtime.t -> unit
val read : fd -> bytes -> int -> int -> int val read : fd -> bytes -> int -> int -> int
val read_cstruct : fd -> Cstruct.t -> int val read_cstruct : fd -> Cstruct.t -> int
val write : fd -> bytes -> int -> int -> int val write : fd -> bytes -> int -> int -> int
val write_cstruct : fd -> Cstruct.t -> int
val socket : sw:Switch.t -> Unix.socket_domain -> Unix.socket_type -> int -> fd val socket : sw:Switch.t -> Unix.socket_domain -> Unix.socket_type -> int -> fd
val connect : fd -> Unix.sockaddr -> unit val connect : fd -> Unix.sockaddr -> unit

View File

@ -83,24 +83,23 @@ let datagram_handler = Eio_unix.Pi.datagram_handler (module Datagram_socket)
let datagram_socket fd = let datagram_socket fd =
Eio.Resource.T (fd, datagram_handler) Eio.Resource.T (fd, datagram_handler)
(* https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml *)
let getaddrinfo ~service node = let getaddrinfo ~service node =
let to_eio_sockaddr_t {Unix.ai_family; ai_addr; ai_socktype; ai_protocol; _ } = (* OCaml's [Unix.getaddrinfo] on Windows doesn't set [ai_protocol] to
match ai_family, ai_socktype, ai_addr with anything useful, so you can't tell which addresses are TCP and which are
| (Unix.PF_INET | PF_INET6), UDP. So, do two separate queries. *)
(Unix.SOCK_STREAM | SOCK_DGRAM), let get ty k =
Unix.ADDR_INET (inet_addr,port) -> ( Unix.getaddrinfo node service [AI_SOCKTYPE ty]
match ai_protocol with |> List.filter_map (function
| 6 -> Some (`Tcp (Eio_unix.Net.Ipaddr.of_unix inet_addr, port)) | {Unix.ai_addr = ADDR_INET (host, port); _} ->
| 17 -> Some (`Udp (Eio_unix.Net.Ipaddr.of_unix inet_addr, port)) Some (k (Eio_unix.Net.Ipaddr.of_unix host, port))
| _ -> None) | _ -> None
| _ -> None )
in in
Err.run (Eio_unix.run_in_systhread ~label:"getaddrinfo") @@ fun () -> Err.run (Eio_unix.run_in_systhread ~label:"getaddrinfo") @@ fun () ->
let rec aux () = let rec aux () =
try try
Unix.getaddrinfo node service [] get SOCK_STREAM (fun x -> `Tcp x) @
|> List.filter_map to_eio_sockaddr_t get SOCK_DGRAM (fun x -> `Udp x)
with Unix.Unix_error (EINTR, _, _) -> aux () with Unix.Unix_error (EINTR, _, _) -> aux ()
in in
aux () aux ()

View File

@ -271,6 +271,8 @@ let await_readable t (k : unit Suspended.t) fd =
if was_empty then update t waiters fd; if was_empty then update t waiters fd;
Fiber_context.set_cancel_fn k.fiber (fun ex -> Fiber_context.set_cancel_fn k.fiber (fun ex ->
Lwt_dllist.remove node; Lwt_dllist.remove node;
if Lwt_dllist.is_empty waiters.read then
update t waiters fd;
t.active_ops <- t.active_ops - 1; t.active_ops <- t.active_ops - 1;
enqueue_failed_thread t k ex enqueue_failed_thread t k ex
); );
@ -287,6 +289,8 @@ let await_writable t (k : unit Suspended.t) fd =
if was_empty then update t waiters fd; if was_empty then update t waiters fd;
Fiber_context.set_cancel_fn k.fiber (fun ex -> Fiber_context.set_cancel_fn k.fiber (fun ex ->
Lwt_dllist.remove node; Lwt_dllist.remove node;
if Lwt_dllist.is_empty waiters.write then
update t waiters fd;
t.active_ops <- t.active_ops - 1; t.active_ops <- t.active_ops - 1;
enqueue_failed_thread t k ex enqueue_failed_thread t k ex
); );
@ -370,7 +374,7 @@ let run ~extra_effects t main x =
let `Exit_scheduler = let `Exit_scheduler =
let new_fiber = Fiber_context.make_root () in let new_fiber = Fiber_context.make_root () in
Domain_local_await.using Domain_local_await.using
~prepare_for_await:Eio.Private.Dla.prepare_for_await ~prepare_for_await:Eio_utils.Dla.prepare_for_await
~while_running:(fun () -> ~while_running:(fun () ->
fork ~new_fiber (fun () -> fork ~new_fiber (fun () ->
Eio_unix.Private.Thread_pool.run t.thread_pool @@ fun () -> Eio_unix.Private.Thread_pool.run t.thread_pool @@ fun () ->

View File

@ -1,3 +1,5 @@
open Eio.Std
module Timeout = struct module Timeout = struct
let test clock () = let test clock () =
let t0 = Unix.gettimeofday () in let t0 = Unix.gettimeofday () in
@ -48,6 +50,35 @@ module Dla = struct
] ]
end end
module Await_fd = struct
let test_cancel () =
let a, b = Unix.(socketpair PF_UNIX SOCK_STREAM 0) in
(* Start awaiting readable/writable state, but cancel immediately. *)
try
Eio.Cancel.sub (fun cc ->
Fiber.all [
(fun () -> Eio_unix.await_readable a);
(fun () -> Eio_unix.await_writable b);
(fun () -> Eio.Cancel.cancel cc Exit);
];
assert false
)
with Eio.Cancel.Cancelled _ ->
(* Now wait for something else. Will fail if the old FDs are still being waited on. *)
let c, d = Unix.(socketpair PF_UNIX SOCK_STREAM 0) in
Unix.close a;
Unix.close b;
Fiber.first
(fun () -> Eio_unix.await_readable c)
(fun () -> Eio_unix.await_writable d);
Unix.close c;
Unix.close d
let tests = [
"cancel", `Quick, test_cancel;
]
end
let () = let () =
Eio_windows.run @@ fun env -> Eio_windows.run @@ fun env ->
@ -56,5 +87,6 @@ let () =
"fs", Test_fs.tests env; "fs", Test_fs.tests env;
"timeout", Timeout.tests env; "timeout", Timeout.tests env;
"random", Random.tests env; "random", Random.tests env;
"dla", Dla.tests "dla", Dla.tests;
] "await", Await_fd.tests;
]

View File

@ -158,6 +158,9 @@ let test_symlink env () =
Unix.mkdir "another" 0o700; Unix.mkdir "another" 0o700;
print_endline @@ Unix.realpath "to-subdir" |} print_endline @@ Unix.realpath "to-subdir" |}
*) *)
if not (Unix.has_symlink ()) then
Printf.printf "Skipping test_symlink on systems that don't support symlinks.\n"
else
let cwd = Eio.Stdenv.cwd env in let cwd = Eio.Stdenv.cwd env in
try_mkdir (cwd / "sandbox"); try_mkdir (cwd / "sandbox");
Unix.symlink ~to_dir:true ".." "sandbox\\to-root"; Unix.symlink ~to_dir:true ".." "sandbox\\to-root";
@ -277,4 +280,5 @@ let tests env = [
"unlink", `Quick, test_unlink env; "unlink", `Quick, test_unlink env;
"failing-unlink", `Quick, try_failing_unlink env; "failing-unlink", `Quick, try_failing_unlink env;
"rmdir", `Quick, test_remove_dir env; "rmdir", `Quick, test_remove_dir env;
"mkdirs", `Quick, test_mkdirs env;
] ]

View File

@ -985,3 +985,29 @@ Exception: Failure "Simulated error".
+seek from end: 9 +seek from end: 9
- : unit = () - : unit = ()
``` ```
# Extending paths
```ocaml
# run @@ fun env ->
let base = fst env#cwd in
List.iter (fun (a, b) -> traceln "%S / %S = %S" a b (snd ((base, a) / b))) [
"foo", "bar";
"foo/", "bar";
"foo", "/bar";
"foo", "";
"foo/", "";
"", "";
"", "bar";
"/", "";
]
+"foo" / "bar" = "foo/bar"
+"foo/" / "bar" = "foo/bar"
+"foo" / "/bar" = "/bar"
+"foo" / "" = "foo/"
+"foo/" / "" = "foo/"
+"" / "" = ""
+"" / "bar" = "bar"
+"/" / "" = "/"
- : unit = ()
```

View File

@ -620,7 +620,9 @@ Exception: Eio.Io Fs Not_found _,
```ocaml ```ocaml
# Eio_main.run @@ fun env -> # Eio_main.run @@ fun env ->
let sockaddr = `Tcp (Eio.Net.Ipaddr.V4.loopback, 80) in let sockaddr = `Tcp (Eio.Net.Ipaddr.V4.loopback, 80) in
Eio.Net.getnameinfo env#net sockaddr;; let (host, service) = Eio.Net.getnameinfo env#net sockaddr in
let service = if service = "www" then "http" else service in (* OpenBSD *)
(host, service)
- : string * string = ("localhost", "http") - : string * string = ("localhost", "http")
``` ```

View File

@ -145,24 +145,24 @@ If a command fails, we get shown the arguments (quoted if necessary):
```ocaml ```ocaml
# run @@ fun mgr env -> # run @@ fun mgr env ->
Process.run mgr ["bash"; "-c"; "exit 3"; ""; "foo"; "\"bar\""];; Process.run mgr ["sh"; "-c"; "exit 3"; ""; "foo"; "\"bar\""];;
Exception: Exception:
Eio.Io Process Child_error Exited (code 3), Eio.Io Process Child_error Exited (code 3),
running command: bash -c "exit 3" "" foo "\"bar\"" running command: sh -c "exit 3" "" foo "\"bar\""
``` ```
Exit code success can be determined by is_success (Process.run): Exit code success can be determined by is_success (Process.run):
```ocaml ```ocaml
# run @@ fun mgr env -> # run @@ fun mgr env ->
Process.run ~is_success:(Int.equal 3) mgr ["bash"; "-c"; "exit 3"];; Process.run ~is_success:(Int.equal 3) mgr ["sh"; "-c"; "exit 3"];;
- : unit = () - : unit = ()
# run @@ fun mgr env -> # run @@ fun mgr env ->
Process.run ~is_success:(Int.equal 3) mgr ["bash"; "-c"; "exit 0"];; Process.run ~is_success:(Int.equal 3) mgr ["sh"; "-c"; "exit 0"];;
Exception: Exception:
Eio.Io Process Child_error Exited (code 0), Eio.Io Process Child_error Exited (code 0),
running command: bash -c "exit 0" running command: sh -c "exit 0"
``` ```
Exit code success can be determined by is_success (Process.parse_out): Exit code success can be determined by is_success (Process.parse_out):