itoap-1.0.1/.cargo_vcs_info.json0000644000000001121401351532000121710ustar { "git": { "sha1": "901b4975e716380aef5b3d45dd3d3a192ca57bd0" } } itoap-1.0.1/.github/workflows/coverage.yml010066400017500001751000000026531377332000200167270ustar 00000000000000name: Coverage on: [push, pull_request] jobs: coverage: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install toolchain uses: actions-rs/toolchain@v1 with: profile: minimal target: x86_64-unknown-linux-gnu toolchain: nightly override: true - name: Install grcov run: curl -L https://github.com/mozilla/grcov/releases/download/v0.6.1/grcov-linux-x86_64.tar.bz2 | tar jxf - - name: Install rust-covfix run: | curl -L https://github.com/Kogia-sima/rust-covfix/releases/download/v0.2.2/rust-covfix-linux-x86_64.tar.xz |tar Jxf - mv rust-covfix-linux-x86_64/rust-covfix ./ - name: Test all crates env: CARGO_INCREMENTAL: 0 RUSTFLAGS: -Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -C panic=abort RUSTDOCFLAGS: -C panic=abort run: | cargo test --no-default-features --features std cargo test --all-features - name: collect coverages run: | zip -0 ccov.zip `find . -name "itoap*.gc*" -print` ./grcov ccov.zip --llvm --branch -t lcov -o lcov.info --ignore "/*" - name: fix coverages run: ./rust-covfix -o lcov.info lcov.info - name: upload coverage uses: codecov/codecov-action@v1 with: file: ./lcov.info itoap-1.0.1/.github/workflows/test.yml010066400017500001751000000054041400402621700161070ustar 00000000000000name: Tests on: [push, pull_request] jobs: test: runs-on: ${{ matrix.os }} strategy: fail-fast: true matrix: include: - os: ubuntu-latest target: x86_64-unknown-linux-gnu toolchain: stable - os: macos-latest target: x86_64-apple-darwin toolchain: stable - os: windows-latest target: x86_64-pc-windows-gnu toolchain: stable - os: windows-latest target: x86_64-pc-windows-msvc toolchain: beta - os: ubuntu-latest target: x86_64-unknown-linux-gnu toolchain: 1.36.0 # MSRV - os: ubuntu-latest deps: sudo apt update ; sudo apt install gcc-multilib target: i686-unknown-linux-gnu toolchain: nightly steps: - uses: actions/checkout@v2 - name: Install toolchain uses: actions-rs/toolchain@v1 with: profile: minimal target: ${{ matrix.target }} toolchain: ${{ matrix.toolchain }} override: true - run: ${{ matrix.deps }} - name: Test itoap run: | cargo test --no-default-features cargo test --no-default-features --features std cargo test --no-default-features --all-features test-miri: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install toolchain run: | MIRI_NIGHTLY=nightly-$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri) rustup default "$MIRI_NIGHTLY" rustup component add miri - name: Test itoap run: | cargo miri test --no-default-features cargo miri test --no-default-features --features std cargo miri test --no-default-features --all-features test-cross: runs-on: ubuntu-latest strategy: fail-fast: true matrix: target: - aarch64-unknown-linux-gnu - armv7-unknown-linux-gnueabihf - powerpc-unknown-linux-gnu - riscv64gc-unknown-linux-gnu - mips-unknown-linux-gnu # 32-bit big endian - mipsel-unknown-linux-musl # 32-bit little endian - mips64-unknown-linux-gnuabi64 # 64-bit big endian - mips64el-unknown-linux-gnuabi64 # 64-bit little endian steps: - uses: actions/checkout@v2 - name: Install toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - name: Cross test uses: actions-rs/cargo@v1 with: command: test args: --no-default-features --target ${{ matrix.target }} use-cross: true itoap-1.0.1/.gitignore010066400017500001751000000000311377533260500130060ustar 00000000000000/target *.old Cargo.lock itoap-1.0.1/Cargo.toml0000644000000023121401351532000101730ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "itoap" version = "1.0.1" authors = ["Ryohei Machida "] description = "Even faster functions for printing integers with decimal format" homepage = "https://github.com/Kogia-sima/itoap" readme = "README.md" categories = ["value-formatting"] license = "MIT" repository = "https://github.com/Kogia-sima/itoap" [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] [dev-dependencies.itoa] version = "0.4.6" features = ["i128"] default-features = false [dev-dependencies.rand] version = "0.8.2" features = ["small_rng"] default-features = false [features] alloc = [] default = ["simd", "std"] simd = [] std = ["alloc"] itoap-1.0.1/Cargo.toml.orig010066400017500001751000000012731401351525600137050ustar 00000000000000[package] name = "itoap" version = "1.0.1" authors = ["Ryohei Machida "] description = "Even faster functions for printing integers with decimal format" homepage = "https://github.com/Kogia-sima/itoap" repository = "https://github.com/Kogia-sima/itoap" readme = "README.md" categories = ["value-formatting"] license = "MIT" edition = "2018" [features] default = ["simd", "std"] alloc = [] simd = [] std = ["alloc"] [dev-dependencies] itoa = { version = "0.4.6", features = ["i128"], default-features = false } rand = { version = "0.8.2", features = ["small_rng"], default-features = false } [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] itoap-1.0.1/LICENSE010066400017500001751000000021421370647460300120270ustar 00000000000000 The MIT License (MIT) Copyright (c) 2014-2016 Milo Yip, 2020 Ryohei Machida Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. itoap-1.0.1/README.md010066400017500001751000000050701400662405300122720ustar 00000000000000# Welcome to itoap 👋 [![Version](https://img.shields.io/crates/v/itoap)](https://crates.io/crates/itoap) [![docs](https://docs.rs/itoap/badge.svg)](https://docs.rs/itoap) [![Tests](https://github.com/Kogia-sima/itoap/workflows/Tests/badge.svg)](https://github.com/Kogia-sima/itoap/actions) [![codecov](https://codecov.io/gh/Kogia-sima/itoap/branch/master/graph/badge.svg?token=76KRG3QI6U)](https://codecov.io/gh/Kogia-sima/itoap) [![Rust 1.36](https://img.shields.io/badge/rust-1.36+-lightgray.svg)](https://blog.rust-lang.org/2019/07/04/Rust-1.36.0.html) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/Kogia-sima/itoap/blob/master/LICENSE) This crate provides even faster functions for printing integers with default format than [itoa](https://crates.io/crates/itoa) crate. ## Comparison with `itoa` crate If you want to write integers in decimal format to `String`, `Vec` or any other contiguous buffer, then this crate is the best choice. If you want to write integers to a `std::io::Write` or `std::fmt::Write`, [itoa](https://github.com/dtolnay/itoa) crate and `itoap` crate shows almost same performance for some types, but `itoap` is generally faster. The implementation is based on the `sse2` algorithm from [itoa-benchmark](https://github.com/miloyip/itoa-benchmark) repository. While `itoa` crate writes integers from **last** digits, this algorithm writes from **first** digits. It allows integers to be written directly to the buffer. That's why `itoap` is faster than `itoa`. ## Benchmark result Benchmark program was executed under the following environment: |Hardware/Software|Version| |--|--| |CPU model name|Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz| |CPU micro architecture|Sky Lake| |Standard libc implementation|glibc 2.31.0| |rustc|1.51.0-nightly (368275062 2021-02-02)| ![Benchmark result](./bench.png) :warning: performance of `itoa` crate highly depends on the CPU architecture and libc implementation. ## Author 👤 **Ryohei Machida** * Github: [@Kogia-sima](https://github.com/Kogia-sima) ## 🤝 Contributing Contributions, issues and feature requests are welcome! Feel free to check [issues page](https://github.com/Kogia-sima/itoap/issues). ## Show your support Give a ⭐️ if this project helped you! ## 📝 License Copyright © 2014-2016 Milo Yip, 2020 [Ryohei Machida](https://github.com/Kogia-sima). This project is [MIT](https://github.com/Kogia-sima/itoap/blob/master/LICENSE) licensed. *** _This README was generated with ❤️ by [readme-md-generator](https://github.com/kefranabg/readme-md-generator)_ itoap-1.0.1/bench.png010066400017500001751000002275421400662405300126120ustar 00000000000000PNG  IHDRNِgAMA a cHRMz&u0`:pQ<bKGD pHYsnu>tIME9fuIDATxwxևߙ9z H"\T! ^av^`Eޮ(RDPPA"HoINN?gf̐53g3ٿYk!D"H$D"9Pu$D"H$DrbP"H$D"HBH$D"H$)%D"H$(DAD"H$D"9 bP"H$D"HBlDr4P:(9G(kpp8^O=fk^e1GS_%EH$G2a`>EAUoȅ0vt߭ $9zIr ŠD"91 UyD"먪8>H&DQ4MCQ4Mnfs^A2r !D"糎B!TUv!qbd>ׂ chrI1(9*{%i /xy'0a&L`ҤI̙3`0;15k0rH:wLƍ53֭ݺu~zZNyG(..\q[.ݻwsμe>aٓiӦYq ȑ#47;q28R UUI&\s5\.46lx#Hc?|9c <Wk=M>}ƍy@-yTUs)s<f.OQQPC;uTΝ?L:u+**뮣k׮g?'x"9⑖ADrDFzz:ǏgڵXe˖1vX֬Yõ^Xu]LXVu$ZԘo,Yѣ;v,^{-{}l0Uf5I&gcL&cnRU~/n2gW~/g)i k^1Uu4_Y=۴WtMs7vgСvt]GQ*فcgn9{gРAZ#ئXjӦMcȑx<zl6kϕ][]I$k1HEc"̱Xx15*s:"JaE{eTY|rfΜI CO>aٲeVst_]K.᭷bݺu"H0 \p?O.B~gΝ `- 4M{vi4MUU:$ nݺ"Y,y&bl_7`isS*Onws1(]7(]N7]{Y]GӢk0PX9XQ+"nC{wvاƊKEHU222HOOzvkl|>RmΜ/*O?d2ɻ[H$D"9*(ݻ[.Y>~ 0ΦUVy晖͛7{|r(5btڵ؛}ټy3ӧO~  RV-v駟NFFF|ԩS.]XTUM6Yu& 5ki׮];v_w޴k׎g}~9眃-3yW_g\. 4W^r)eQzk4h4̙3ڵ+M4ڹ)B"`̙3;vI޽?ܹs1byh"G=uMزe *'xbdYf1w\v؁W^ <Wf+\tE̘1O?ݻws 7Э[}9s|r.R233XN}~W8hт38MVR4 W_e!С^x!999qg9_}U[TUeڵ+5_`ժUlѧOW_%KEQW_-8 ѱcG1dѶm[ԩSիE۶m/.Ѽys-{=!DB!Ċ+Dű+8 q^z41h sNkgC{「W^gϞ .ZiiibB! ðxbѭ[7)?x1dq)/ƍW^ԫWO4o\uY⬳M4M63gB2cUxwEffxw7 u|衇D˖-ũ*>lQ~}ѠA1k֬2co\ѬY31x`q>}4s !5fϞ-E/Zj%rrr3<|ǢM6VZ⤓NC }bԩeEu]WEDvDZ)">lѰaCQ~}Xtp:b-[\pڵzbСp8,..z) ",E˖-E0dYDffxg1lɒ%VZymcڵ8묳|7ѣG4Ѿ}{1dѭ[7Ő!CĮ]2&N(u].N9aÆԩS`[aqLѽ{w1dqI' q駋m۶;wݻw7phڴ8묳D߾}_fkFn믿Z͙3G4iD4lP 4H 2D 0@~qVzMB!/."1rHѴiSq犾}4ѻwov20 JP($z)7n,Zl)DEff3f6m]-ZM6yyyQFbV[x QNѲeKq93YYYbʔ)B!ծ#G41qDP($F-<%*"~ >{A_VZ&M{I'$ڴi#c !_Ev '`cˢE,1c~*j׮-Ǝ++v^|EѨQ#ѪU+[o{"//Olذ:uVѥKѬY3K.Ν;wW^yسguަMDE˖-͛˔Q84/-[f}ŢiӦNhTtYZ^Z'N9~zxqq+DZ… +W͚5;v|M/_n-J9`+Mfgѽ{wg-bO>nݺVBߋc=V4nX\s5\H&{G<DZZx˴/EH$ה4y嗅z+u?~p[o-3z!xĿ/5kֈ {:vZѱcGѽ{wѭ[7{n볫J9v׊,oiߌ3DNN?i-13p@i&핍ٯ;C4iD[O۷Y-QVc+..]t 4gyB^zIdff.L;&NXgy[n 4WBĺ9w'F-H=#Ӆ7x(**\v8EŞ={6ڵ^a9o_yZy.)H1(HH?>?ĉc=&~ѽ{wz_/"mۊK/Tw!do2oz.^2V6+W׷PB]DmٲiHDp "//OX:… Ezą^hg.l&N(\.e=*nMԭ[Zm [C=zD:_G}w̺_x!>C'|Rx^ϗ83_F.]TI&oEs@be^5J4nXB!v%4ibgEh~hԨظquK$9~7t]g՜x@*IZZݺu'_3+(**⤓NAݺuٳ'3g~ q7nL-TU%ZJGRpXm,u),,,e˖K/߲sN+i|>YYYwl6:u7ԫWEQسg՞ŋ'lČi۷oaÆ<2>Du쏒L&ȠsVYai 4 QTTD ʢK.dggsiѲeK+G+LwߑδiʌoMXf5/^Lfݻ>}*}̽[s̡Gdgg[~|>'p]ݻO,t( h]먪x^?x&MĚ5kɡSNʘ1c=zt@ʬ' bۭo^5kְyf.b222K$vO?D&Mp8wqlڴ<jժϣ>_'ʕ+ٸq#Æ +3$7ndĉDQky<֮]K<_~!H$ԭ[vY}سfrٳ'SN[nai R=i޼91]ױlݛ7|~:?TYZ:fD4֭~u<3b1.M(((f]pl6`pJ"#ŠD"9QUp8SO=yG(ncQN;vf߲hѢ2e!x<#v;~L4: ldڵbH&==ñOf[*̌rf=]408Shܸ1N~ƍfxiSwZ}/yΝ;Qs]v;> Ngjt:#fEᡇfҤI<3ѣG © @,P(c=Va}e"عs' 6Q|s,UUe/ >ܺ{񐓓S{͛I&uݺC4BՋ &裏rbhܸ1 /q-ІFEJUŬ1*oφaX)@׮]8q"K.%///~ԩ ,02BcΝh'|2Iڵ<~oͫsΜwyyUdڵk[)AUU۫,YYܾ};zjVXOX ._YŒ<*.H EDţ餰nP(TFh LQeG֛mD"gZJ/K^tgyD"3ӲY:eǟŌtO2f-[ҥK裏1b7x#s~ƫ˨+x|ժUn+qzѶm[-[lwq(G}Ċ+ҥ ~ǦMZVnZ~x<~0gz8p wu~)z_gǎ 41oFqq1Pv^/jr(YFHOODUŜyƍ4ͺǤHEADrDS>pEϧW^|ե6F,]Ҽys{=6l`-TUe,ZN8UP93Mzl߾"i?s=p@$'O&LZV҉ qgxb^{5`E~$5EGQ"> :uPV-ee;3ٶm&M,נAعs''O&6b [d`ӧN__^{[ҿ2A… Y`A\K.eΜ9tګj24ިQ#v{֣lC,s54mڴ)]v>cѢEVrwիiӦ :tWQzO?믿αK hժ 6gΝѣ؜x3uT֮]k,͞={ʈ𪾜u}ܩөSDVzj~zz-`֭4mw6m,5zk5fSD޽HT2ժUN;W >  V6f˗/'''c=y%)H7QDrDe} }hر\x\tE\ytZ,YٳgoXoMwQ/](JOOo+dذa\s5ԫWG}2e~n]Y=6Z&vrs3|pv;}| Zb֭}J$tI5"."j׮M0>M6ꫯfp O `ʕ;׵oa`\q 0;I4eڴi,\nJ~_㏳a9 ì__~ .뮻]2d}O=۶ms!##BON߾}:t(}X (%wΈ#x'K6l.O>'ү_?"ڼysnfNXn=Dn:„ ,\!CаaCK$<dddXt@r+G,\H$.Sg{Ǽof/^ȑ#hݺ56l`„ [Vp.]0eVZŭji%}U}1sWҦMTUe|gY?;\BW^уgfڴiwyԫWxV4p͛3ϰi&N>dvų>˪Ux'UdVgi.]bq 4X,F֭9S|tڕ?[nML&93hܸ17x#K.e|wKaa!˗/g<#VP%왥( |WՋlp^rD#ŠD"9qdff Rsںuk}]Ə /)S,8͵gB썱n'33L`?p8qvwM6mqIKK"VeYˤن!=33 `ԨQر_i999<|'{eD鬴pҰaC&O̵^k-Nܫ/3ay}]+X㏧M65<Yw].ծ]͛+ /ix3f 7pCyZjԩS?wPUU˂RzN0f͚+0w\k`VV\r ,QFtرs=dgg / >;kgUU  4Mr뭷:Hƍ3e?x:0c +Q" ??zc=_y7l_… 9䓭hP1uڕ^{x{I,Yf˜zV?K.4nܘ]vYgϞlْ֭[[6_r)ֳ塇*cܿfPR~.{<:tϛoi?6/nGe vԽ{w:,n^~e8<\r%Vd>Kԩc})CoOg?|y0vX<f͢d2Iiܸ1 47G7ްo߾e=]#s,X-[p9.(BH$G0xX,>`T$woF<VZVTDQ$^…T2$rK2]B֭KƍD41S){J:pڗVQ*96ZnڴtZhn'X)̶% hWp8̺udffҬY2/}=YnP44iRFW)MƨQh{1H$xK7jjK9s0x`|M T%ttH$GW~_gch/`skEʌFVZ >;wZ)#t}K,:u,=^uJz쯍tY+:-]viG,ZU0 }Q GZ%D"gL+ȪU83=z4׿D")AJH$aXIau3ewUUk9Lz$G.+]%)%D"H$(D:H$(FH$)%D"9$EAD"H$D"9 bP"H$D"HBH$D"H$)%D"H$(DAD"H$D"9 bP"H$D"HBl2PbFPr(Pr8 p@Ód bPRenTkI$Bnnn(ECလ9Ox.\<)%B t]GQHrP0 a&!VBrPr8 p@j,ҥT E,(Ij#9y(9Pr8 Iu2HPPz9(9Ty(9Pr8 ᑁD"H$(RHw-MT"H$Dr)0JRӤ1u j]AD"H$a(zBbf.EǡǼ*ɤSc*N?M$R JH$DR!سgd ' MӤ80EͨEQ@uDL)%0nRDH$D"+Ph4D"%+.WV(@UUTU!4H,*QÞ=["\^0pyR J"uC׉FiD"HdEp8躎Bz'6( )%5IWp(Z=S9T {2 FjrE,nD"HBkuo si))QEv} 0 l6)CKܟ y$UF:<m[cQ6F(2KD"H$[#eσf#xܸ\ us7M"Wja$b(F4H&us$D"( 0Կ5SROuB\nf)S&[^knno{$:lj<4M1oM]EAU_ Jh*_w D"H$?|>!?(Q"xa\.;cɒoay v)l6K !,ҔPs^UX«2w,Lf6f RBu˕0 $.e˾'1tŸVgy-!a۫ngxreWЮ]GHeVʆa~xUƎ}5j7-\!Ҽ9W&H$D")jģlIKIUj6vIIVZdggLjb8~px<^ .L'HnT 9ѣ! t4q5QDEQQHqݸT55`Uy"p:8,ǥ^BVDA Eu</n EhT' .y8:Ν[꫅ԮA$$y4`0Bz]85s,Šl6^6[W&H$D"B2eѣєR Ah.ON{(Brضm+g}'?G `ŊyP\ѱcgE72wlvEӦy|4(FE\#`zѯrsq\_ٳ?eȐ,^5_l HݺDhV.nwXдV\.b=(={]!_1c:'t&'O駟Ul6֭[O?իظq 4dƌL:gB~~klܸ!Coˮ (&1pI{s7aoit Y>&*s.nfW_}G( X,֭[0 [p:SD\.7O?=矟DVmHKK孷^'Ot-زe3}6bL&q8?^z;p8:9N;meŭtuT:O=5nݺsNM2`Æ߈Ddeer̛7>g! i6tƱǶ#J1({Q[Q^}4McʔI|L})_Z BD"H$BuBJQaC0K### 1cWڵ;Gqq*Eoe1tփ ty\x9w/&M% ѧO?<BEQ9r==B"^z ٳgÆ ߪB ]z??3"0r叜tR?֮]C lw*YYYQN]-B߾} C^v;kXZq-wp-N8vf瞛OO>QyĉO2v YG7ᮻsOop8̚5f̚ #G] >J0~xܸ{:u.6niĉSPUH$B,l۶/嗏&LE 5AUUwGl(ڵ0`I,['Л`0(ŠD"H$#Tp8a$ Q(**"++ŋQp6dĈ+?lٲ:4jԈ5k~wdǎx^0ׯ\kᗑ;S'.;Ѣ1ԫWٳp8(.Np:̜6`0ߟFyWضm+.5k~odziFk?PՔ +.p ۷/E%LFQIp8T2.aBA >}Z8ڵs⋯H&ujΥk,X9`qEQuD(.N r0{sLK9 ӿs~:!CW) u֒Ӂh4bTU%LDԫ׀CFH$$¬EUU0[ ~: ի_D"H$ɑJjJnl*۷op[h4f#IR~}l6;[nY+<}4hА:ut:ٶmDQ( ^!77H$3IPN]۶m%?h&> Ei׮=jJ|>ҷo?N̊?Dbtԅh4FSMqSWqQMBv.|}ED"Ann]bnQUMپ}M4-#@ФI 'YQ>+^hCn^{]OS"\C&bPRcrcz#[na׮r1-),,D"H$ɑ) #eB"vn~@ Ą 3x9}qjSyqVS`qRI}2UFd2INNmK4k֜tZl?|ݻ];F)|+4 I"G?qTYF B`i*Np8)LAq:]xPU0Ls/qv)99V\.? o5nëĆ Y=D"H$j(QkǬCS@]+%V*.Wjan݆h47|EV׋ǣ HKKI< ڵ+~zh0{ll6 sSi׋HOwxm|B͚ H H͛ -XJ<:ٳeѢ/֭'ݺ`ٲ|Lw+e$S,1dz8n:u 7oHnn]4OӦ HO82%S9SӶm;~)v ++LB }A۶z%'*VEv{p88@R$2(QE) f3|̚1W^9={b:(H$( A0\.!-StkaFM7oDU5rsбcg<{]Or1̝;7|{7n!xчq}\.OիWvy*=zo1y3\dggH$ؼy~a̘سgɤN]2Yn7ݻ$2ҥ?Ȏ>`\ ]Obz( a&Iիa8Nb袋=.\F:uR3,GffG_K4ԪIj׮f#+h|9|y-F7ނdg v%ǁƸv\a&Miܸ)NvZsP#ŠF㤥v4Xz={&==\D"H$UDC`s?tr׫cDp|> %*|~nz0z?;/\&O'x<9!8.?w/wu+SOs-7Qq:\wݿik\M9p8RUU!Ѻu[4hHffM4% Sv.۷grСcIJrKLY'>EQI$Ԯ7ᥗg~^z jeʔyyG8Njժ#"YYjD"AZZ:c0jԥn&MzM/0a}4l؈{քásUc3=D~ o&<̑C)'"BYl݂`Й4|;wR1 ĉOlw"2<5j#]CQÁ# TU#( ,"7VBv;\ĽND"*_E!n!KKI06̖EQi*dh4-#j"Hc*v@ @"fv̉C󥓖auiBqU*KZ%5(DQ 8 4k}O. +K$D":EX,0JDI= bR]tX,eAq!%N Э{EUSt˽ FbX)I+Q3ElQF !DM| Nvs.prʜ08ֽxjLaTxoTnhJqq16!C." Zt͌syz}eĬ,Ç ;;{16[OjB )%5V&bW+jEUH$D"9Zlo[U=9pD"tԅi>r@ϵt>Ϝ/DJH|>Jsrg*q^[ C'--f2BSUUsp:oߑޛ=>,(fPD"H$I#D2p5]࣪*hJ}>T:BeG"zD"H$GLQl2UGK1(:JSO'YD$['t 㐄yH$D"H$GAIQTz!J+Fh/B1D"H$D"9bPRe oFS6mFK#H$DL# ay)OIWAR-t$C$AO"H$CJR JI WylC!Aaan)%DAEQJ~ЬD"H$5`86M''AìYef.`(B+8IG:ax曯Xt _|YSzWSe ȕ7PGO"H$_ÆDsќupت,, Crٙ?o%x`,o*Nx<^f]$@u$WXa=\o !0 ceUX[E9󘉮uD"egٲxI$I'k_Ś5*DZ}ll6lu]Ŋ4[%e'ڴC~2(9h!ԪF8#g$D"TF,Yg?އnc'A4 {8E!goq:9p3DD18N<  L&-!*SEU!`0B"GUup\6tBxLͶfYBRQ3 cb1~p8N</Wg]dfI$ ~7 AF q:hUK"ᢨ(a0pݸT55`jWKt:p:,X0K/,7@ j5NjmCQ խZBNN&PjLS.^l6;@ 0܇J;iinv!RF#5n{j@TՆj#/$ OTzbM<hMRJpذ5n(EC0E%,-mHQ5!((4kJr?_V+/hїDQ֭'|Av6m->6oDFz$ TU%׋̚55k~Ndddq:_^{-Y[^{5~iu#33aPд6P(%Vv;xɓ'p8_믿sx4lkkТEK^/4P(套^`ܹtЙUV0ctԅM61iD6o/aEk݆ay3g6۷o'/v]+aŋԩSy p8\8<ӎ^57j&*9(C/x]T"H$"@C-Ϡ~R8 )Yf2eDTUe-DQ"֯_|; }-~m=&gظq6 wҥKXNʕ+p\l6֭[O?=# sgj NfcM"Ӧ 1pIL4^λ&vɬYs}w3}(fy8Nx}!nZ-_~c=ab1n݂alݺuֲqt]rӏ3|r>k׮aq9@Ȳ^V<7[lFuokٸ7=GˈCoXb9\s%wq B }0z}L.SLnwX@?˖}GEQQueٲx)ۗ`0ߟHkhժr^rtމ@ ag֬Ox<wL$c@hf6n܄s={ST+38I|aY0rpڵ#(**gE!L%'y&233q:]||E]ǣsUU۷SNfŊDb1:uB4ڏ:k 5EQ4P(DQQ!k׮ˇ[WH$%v+eQTMӈl߾&MZ[٤I 'Y{*+dg:[ntR~B( D$}I֐bPRcAZZ >oUPP"H$EAAIrT( vg %HY㩈[c~@ Ą 3x9}qjSyqV2,)xnbQ%ꓙn<$v͝w7a-̙3I^8(\"H$ɑB%T:Bw=hQJT6\>0hݺ ho,^G/N&y ӵkW~{`lbs ׋HOwxm|B͚"`$eA:NzżysYKu @n=Xl)~:{,ДEJ !YB` /Yb,pvԩ }]OмyCrsҠA}6m@zzlJHSӶm;~)v ++LB }A۶z%>+VD̙3;мy 0n{}$Ӿ}G8%+˃󑙙Q{MePR#ΛrŗDJt:llD"H$ASSQ+f?BQ{<5j¼ys? [;3p{zcgYk{q=!oţ>r1}{^ mutOUUѣ|ɓo&;;D"͛2,&. b+a̘سgɤN]2Yn7ݻ$2ҥ?Ȏ>2RI!aekKӝ5LR^} poE]ٟp2zԩSp8̂bkF#Zu=Iڵ|{de"9;ŨQ#[p8L @a.'9T!SN%rwbہ>šC/fΜ0jb!Nxb0^'TH1of#/aQDMa373pUkN?kj6+'MsbIlݏRu1}{ F \0>]CQÁ#cE6,tvÆj"ЉǓ$U}( X]7pS N'S\\arl6TUvSTTD,xwiYYYńa\.7~}6nߞN˖lݺMZZHڗfha]TdOfe#sa$q8^!s)#H$x<ֹ.P(D$ev\.'줧 `Ef0Ҹ\.0pEٛvt*!HO(|R"~60 G}VG0dddL&كi΃K'--*wv۫4?I cd2I4~\~`ɒ%1šH$u#;Ee)-(dҪ`Qz_0lp$D"HN*QdP3Wտ!r[k6S*﷎ !,p8p:]FMEG-F3K4!2rJEL*[DB2QFS.Ӆ\TMJ[8r9?[Aaq0{xu\(wDg,,u#eTzSX,fSm8ȎMt,?.1Wԇ@M\9f}5s}<B!3f z*c1,=e˖1yd.bl6Wfڵo3+d2f۲e ƍcо}J) ("D"H$j7e@oy0^kRj L&K~/^!%eKw͆ߟ٬*[{ cW !>jLXxoTnWiJqq16!C." Z[J]~Layz}e><9|:[~\JSQJ cUΚF)kjnV:,&MĈ#9s&͚5PK.K.Ě$;wDQ?LqqSXXСC'v[~kUD"H$ʄBU=9D"tԅi>r϶t>Ϝ/Dʽ󑗗,cSՔfVVVdb:ii5v ȴzsj4'x?o}v1XSEPPQmot)C=D"H$G'B,GnphikUUFSwYwU@8 T-2"BϞѿJbB#M@}˗/+Rkn7</*bo'dС6R +TqL7O?aÆh"^u5jyGӦMOKIbS"MwrC(Jo$D"H$bI{':k0)"fڵk{}srrصk$,+n˔(JF̞=;pB^}Ux ڴiS= *"(ph^lNU@7 DH$D"qT!X5H{Bfũ"̷<թ/z2e 4~SVA(*{cw˶a $5nb F*H$D"H1x1S:esB!n~ab:{&33p8NcԩZ"+CWn QbT¨Jq"1~ !PFѿ$D"H =# )":uv2W^Mf̀?vUfQt4nSbM?r&A\QڒYyRNMQ $0@D"H$seN0i©ۑ#S?Ǟ={p:[sr쓬|9YUUn[;3Oٵk.x<{G=PUպI(hZi H$DW( G l~o[S ͗2 ]B$z8#G*2x0rW\q_|gq=z>c{@D({n͛7s]wH$Xno6~-=zk/端bРAp ,][n#B\mM*J򇭛D"H!p|?$| FDUbdeepm7dbc o"--[H$ VbRUT\OiGC{QxSkBի2,{?@ …_CӦ*wWAb aW_}?_SO=Ӽ9vu]grGuwN2 Z9NN̙3Yb^z)gqiii_(x>N EDjan|>zb&ړD"H$G;BIH x 6EzT!MKлw_Z8!vqKn<K&͆e:WW[L)iZJ3SL13ބ($ l6ŕW^CÆH&lv8lk ;w&p!aZ<֦GR Drq[oްڵ]vlngoK܀BQQ!dۃߟF8*cXHGahF `qIz6,.69>[mF$ bl6;YY5$ 8PEQI$F)**aPUGqq`MZZp[$)2@uݺL}v>9u]';;7-fSQpsBp8_/UsLK0|wߝr@me ''ߊH$"D"H$G<(N^թ:{]|R^ymn__]v z]\pvɄ 3g2tŜwb dصkpgňu(+8<]ױ|tYgGFFl=Bl9n݆'رcCE]<_|ȑxXv wy/:usӫ \{(v'3F#o<@ ?$i6N=u5YNI#!\> YyM(,,;dȐ!ŪMY x啷lsΠD$a„\x0n_q9O?=?]H$DrPk2. Ap5U=>`Q믿Һu[F6lH2sw3gΧqǽsL>s~ʝwނ :EQ(**}Nunիbqhֲ=äsI&v^~y*+V,3AUU6̘1۷טP~ZŖ-iݺ%f-6mR˗E]Bff7" ,[oO%d4n0x|\n5kƍx)..{  J1xb0bܸq$W+֢`>N0XL" Vp8pɧkW)#\.7D"H$Bje|njeөS "8N.]G}ȸq>B |Wvͳ>řgfrgCt֕۷1m;\|eha\wM8ʖH$>RٍiD"1t˿e|BzǶm[Yd1ڵdɒ4n܄ L֢dn=; %PU?ݎ*jNڹtЊݻø.k>xo]t OлwOkqM70bԭ[xjAz$)#<ϡn!ek?xΝJFF%33bA</xJ$D"k?_,0 IhKUVv֭'{D"8N873fLgÆh׮=6w<_7_-^޽T (,,0R $tܕvF(T&۷ѩS-LތTOs]gZ U"0 6*wJ]iРahE9#o:aatc)H$AQ@AAPsI-_5,d 7x۷Qv.H֭I&So7|Ÿq1dȹ";ۻU+kh饢ر3 ~Sn}sh۶=/<ӧ]bQ\.)(5c C됛[ѣW/DBXB1%XE "/JE!+ONNvt?N6mFff&s";ۇ׫駟ЪU233I&$D"H.%^j#U3gVbMvٳgdf"֭;<$qj6 ѣG/֭r+W _[^DymTUb3|t^x904h4J4_N:t:iР9ꋴlOڹ%ֺ%S*')h UU0 aسg76de"ӻw_vmlZ@88ͻ3g`q8"-EQڵ70y$fΜɠA9ejrxb>Aw$D"(VOj8x%T{)XTU),,(qs)y2t0.`(ǩ{3f>&MHOOK/(,,mv,\ѧ(~}J<ɒ%Nzz͛cȐ|^v سg!Rzݻw'rq)fm(°a#۷7=41cn⬳Nyc0 ƍzy׀/'EQp8|>FH$B$ʟ F0T֭ǬYєFȤD"-<=vH5<9zlBX*HO>6mEQB^8  HKK#qI>f{.z>OO#i_>֭wr4L"m۶rbBF--4M+Wԩ32`@86p8 '&77i4jes дi^IИ]v͚5gժbQ~P]{;sY~6ѭ[\.3xQH$Ѕ+nJHܠ48;<E!#Ë)$7i @XɃr90 b1) C9xJ$9%GQU5M(HP("9}K AD"H xܴLS4VM EE=~Z6ceh*/H `X `]Yecg0eF 13jh(! [eC2kWAu@ vO&35JAFAaŁ6 K(H$M*%)¡KekEQzHf+[VEeTVnEm}叙BQ2b@uVGAIP*n6Pw@"H$D"R J@PP( | 4E(& 꺭H$D"HnTt!))( 1=A~Z2\` %T0x\'Х H$D"T )%UFUzg n㨊Wb:xRnH$D"T )%UF~}%wjRPRIae"%et5^*ED"H$G:6C7#ŠZ$H*ًT5 W8H$aVʅaF4 㐵׌*6DQdjJ w9yLD"=Au6UUx*=UUBDeDZ2"Wf+'Ĕ =ah MбcgF(8~}#_O~}O>!JofW}'>_Rأ iHLH$xYW^/}#?D!.M~G_ңqavN"H$ %zjZML0t ),," J1(**pRmB`J#F-7LbpEnTsq:]l6KB!\Ա`Nff&$ n7;vKn$qPMBD%݁aD"AZC(*de"uEAC]udim{,xz cy啩vN:7!ŠD"HLKD?0 \.7}-=7op"s/O_07>|~\.=yyYt 71lإl6瞛Hԩ3Xd2Ʉ ѪUz>" ȑ`ڴw3..l xөU+I]6oxw})S&2wX;!wy BW";; ~@uҹќv Gui<H1(!p=w]|~^|y@` {D"!LgҤYg!#I$0Eao@;U!jn ضel!v#͚dݺ5b1֭7w嗏snD"j7p՗Sn=Gb,]Nꏮlvv17sr=b$ nF*ϗFӦy|.d$db˖}G$W %;;X,j7゚x<'ƈFcsL>^/iժ X ώ۹KU+1c&--rt8,Csv/[n~Zo~b1n7W̴i0q qD"HoJդ?-fgΝl߾xOK2ӧB1@%?ЬY t@aa˔)R\\{=GzPhݺ5zB%Q=dgӣG/8vQZnK(Уql6^oU1P\"H$/B4C=W ?`1W\qW^yucuuX,JwH)Aun݉n۷,cجYs3Xj.lZjMÆXb9pN۶ǖ@(gZ-iKmw;m:HOMI$(J*"jÆ ήmEUTv;2EAG tؑ`0Iqq1/qغu s%##:PwA"H$}QR]P9nKje3jU\rHbO<> HY4M0RNuX{26mMaK[7gh9%D#F?Np\nڷ :tbٲظq[%+V)1X5|Z}dddcށaaSRZ Xf̘acIr!%# rhժ _}>u3eC^L6ǢINgI$|K$L"%2zԴ ܹ+`dgiڴB$q|>?X@߆N>y?|04?>{իWuÊ&tw\.+t0 MS޽'K.ѳ:t~kʂѣNJiZ9Q DJlZx>b|>EcѢ/x B1Z/\*$/W_-Jr'E]w7|W_}Y̘1#Q4iCCQЯBwe g) %Dr`wpS) fYΒ +۶mk~Ԧ+3lإ4n]8ƍY>fq-wpɧp%q73}{jhђ۷*F0?D1:tDAAӾ}lUUtԙx >0wP4p8i$ 6lm݅#==D"nfӦomR[D" T5/Iеk=ZI <g̦Mp\hF4GӻI|ٳҡCgшei"\{D!*avUn= 0excq@4s Ӊ*y$ GHKL& M7>LIDAT|N<vus$G)rJy(PU@`܆eVUu Q/EttѴT$poNySKBz{^\. À@ @U(JJt{3 [0 C1׃ݮQT,㢙#L EJ]tP\DU _:iiZDW,iHTU%kWC͛@aaEQ9D"V( $:xAV!pPHZE784IAA%l6% a]..NYL7J]ٹu!`׮ S(e0b+Mi Y(B8! pحcd={ >jk,+[(Jo5M$5Qq D"HWRe9:yf^T_l*9jm:P  r%BU&zQJ$D"H$CAI@0$J= D"H$j"Šʨ*5`hgIB`TN =8͑H$/"7=*ƁC{$R J( VP7CRBQ!hJ$ Le z!KȵEQT #iO&XbPRe={B$z*nH> D"TӍ@`iiYoa\, #I =gbPR-RJ~uc$,H$#T;(*M4*w[&)MJ,Y!1\sz Fqd+_9Y}hTA4".}Yke8t aZ@ )jjrB l Zl)-YX[oSOT׮]N @#V`P .i^2͚N?$-YXz: Y^xY%I=܃DJ$21b_,t)Hu.š2faQ&oJ&zWp ͚:v]vMti@2f8b3N}= k]te]cǎ1Frƍso5 >TVV &'G>H^{⋍nQX8yd7ѝwީ.icu벟|iH*319lűvɤ[0@/u]4h2BƎ'xB ,6l#uT>vu]b1?^<~_O>ӧ>Í&cLzxip8IA飇0RA' _ȩWU j.G+`5 T{/tӧOL _1IJڵ 7:C׬O*t<#եK͜9Seee\Z_-R~BPb nΛV{3'Ny+n4#W^ٳ$?֌3JO?GyN` כ2entAAkod8Z.꺮?|oW_}UgjC8r#9qSC͇iQYYrI.2ZJ%Iwz믿^tNPXL&>}*q=3ykV`P'uQ\m[URҊ(h %{2YkU^U4Z̡ۈ´B#uWվ_{ ^*k,Xx~ּ_ Z*kB+xuر~-ÎnJR zd#ꬹPap#jD{zV|.R }Ri͘qva'5\?ZW^yml6F{ӛonY{5Ds漣_zN{[X\cnNλk.I'޽{ni6kѐ|~EM|JJJ4y~AzGեKWi=P,uDhѢ•zAR(ҥKgԭ[{Y-YXhT;F;jzケdիP& `1(#լY/䓏՞{9sf+#U*%gKs9_;vSO=)S[o%"  lQ:׈#5r5`v繁3U^Z_7:L8@c2`2(J{:sHTST*T P"6_,ǑIO551>0`3Q6ʕbgPqd#kQ&ъ}4. fa>thLA4oXZ> J%sg :AŷV/x-a8ҁ{wWe,ck*:]yls@D9h̐= ZRV|h7kU~Sĺ֊-a bL[s-IZ>ֈvAh " @D60ma AAh " @D60ma ̘ @hu}+%cd!afTZ[B" Aل k/Id2Aa z>(WmaA4K$KZ3 8Fq{~ all֗8rR$QYYB!GHX,N cH5k֋^C~+}B!̘Fy9kWav-fx>n֭;A@M"Oʕ+ Ϫ4Z܃EeM*ɨ]vFr]OҪU+վ}ClbZ/k1MFZ+$+qs#8B:uR:V~4j(=䓒)\Ntmiʔ)Tl裏C$}2dƎŋkʕ uP:|=Cy^GƸ2ƕDuhM$Hhɒ%߿ /BRjTJXL9jjjk?uE?tkѺ\kԩ4in;PJgkV*PKJ<}~sjݛ[5T 4&H$<^ZZd2\.F28\ו1FlVGх^뮻N?xE"]wurG?/^xMehǞ٬$Ij_V"ɬNh%b 2H _A.[>kZjX8fyuZK^xnM6MXLwy~_81uOD=?LLNxӮ>]Dɤi&RZZ-[L=/_.]n(B`aZ:GŮpX[o$g￯Owy1Zdy-[LF׹źxM= :IfU j.G+ͳ!a`#+ n_bW7xC{キ$mKYY$}ik$i'|"Ijߗ_~Kշo_ 8PT(R4pbkH.k}eV~N)/[*,TB-&tg訣Ҍ34ydqZ`N:$Ia5}?яtM7i̙8qzꩧԭ[b<餓]W^y~ӟ>-ܢs=W/s_~YÆ 9S\95F32CQ2ǑVÆ G=#}tj޼y:ꨣ_Jw;r\fUIIIئwXwPw}_\N筷*a,La-^.S*RIII1nRlVM p}kᰂ& p8Y_KT ndڧo"nPzB頪bv3֪TѨ+ߗjjRd?),oUVVIIH+VĚ{8h%{adUyyThu0M Է B BP1-, קve%D"zW曯\=UUU/SN< l(Y9d23AvdteWs.4c+%IѢEKH$i8T[[(cl֪B~n7*++S7|@ R=:ct G}Ÿ+L`A`edWRSSVW_}~aTU0=c_)u*ZR-VQYY={ousϽ|((kuA+Q  lLӰahٲZ;mmwy^VsV~dԩvm/IURR" f` F` 8՘1ughq;w:wꪫ.S~UZZJgSS$V<ߌDA` dt{վORΝL&u'W"׎;λP P" l0 9d}?P׮4a!d2F__N:{wmd@-8J:3cOT01Q:vm;=8|M/i8%%%r)LʯձZ@ h4T*l6CZ- ZZkڧ1FZy¡65GDcц70}r*%y9yW<[}\k@G7Q(ZEE"RYY:w.U:*.ct)W(dձc{u֪UrEz" }_z啗/j޼`wԫ6:SJt啿VUU/^[ow?Z;찣4 Zk`0$kO@@VT(_O Ԯ];sǔd=tU! pGdBƎ_+eVUU1a]sU2(3FJQ0xM {VΣ7{,ڍM. Z`0h4`(%ѨdS< POA8ZEh9/շo?7AqUWW'|,k} GfhcWZP(|@Cq亮8~Yԩi}t#V[mL&ÔQ` *))HdR}Rխ[79%KV:Νuy^N[la-y}U3(ZUV4qDBd4J P Z"_1z?^&LЁ:J?Η,Y .@Ǐ5j(M>]x\ .ox8/\_IR.+W~OhĈZpa1ctu裏.u/$W|57AYyn.c[nرc5qD5 /_~Y;.ijZB˗Whɒ ZU%c/[CiPk]]k?y~{ 2Dn٬nr)zԽ{w͟?_XL^zn;}:sUQQQ g;}jE"=3:81Z`x */TyC/b%?߮]?Mjϳl\~M?={N>^z%IqGhnN;kU"_IҰat]wifmt誫O?bI$d2}_xN2Ji„ ٳa'P߾}뮻QO~fϞ[oUT8v馛nK/~Zt# f[nѯ~+=JuUtꗿn6;k]J?Su]uŋuwJ[zuT>}¿Gz_c|J$1Ƒ1&ݜ7cV~&l~ :Θ1Cz'%}R*++|ru]}_ $_gjr]欵8qnk6l~ctZ{cjĉ ŀW󩧞RH{キ}QI8<ӎ;tW+骫*W[=+>}>S}w>}N=4ӕ+W裏߯˗kΜ9ך[|7u~/*IO?]wU]~+oWؓ7[o+B555?`U:[dRlR %Ued/߷=+ϷNݛ-)kJF:uRiii:tPyy| z4m4r9i͜9S}-VjO_d29sFUN}Êp衇ϗQ˖-ӄ ׿X+L|4vXIuyiɒ%ѣGqLRo;?;󼵦y}bL&ѣG>h=쳊bz׋ωD"|ϑHD]tX0T׮]|r}7:쳵FwMSN9E}M>]gytϯ!ю='*1rBn\8F@eR즽&l4  k Sx.JR /hS{-%\M:=Suzzx\C 1Fo|M.СCq$͙3G:餓$IFR޽Okʔ)ujEE*++ 6yZ2͝;WO=V\/$WVG?^}]+Xy͍5ꫯ7߬ÇK oQz:uꤛoY=zЉ'(I BGݺ'_ eW?J%$ee"onHQNgEh?x5+KZL]uU:tO>8mu]]{zO[nuE:.lSkgjс[K.駟^Km?***E)(gє)SSX1:uڵkO>Y]t^z%Eѵi1W\#FhmQ4U0,}W_?P4iN8a뺦E( Bӧ VZ;V~kIҽޫ+R$iʕb7o@&飇0RA' MOfN=*qTMC40lR M-l }VjAVr\UYYw^ 'ZtM裏jW&`}JW#?pp0`@q8qqKy_(Nqmv>H;ѬYO'.!C跿{)̙3ug/V.S ;S ?;0UTT_*_Zi\-nOQ8ŔO>z7|6Jm(^lv}O>ds=e]g]WG)/ͯ,TpJi={,v Bz窱LR>.[XsGo͛=SR~w߭oz駵{Kmw^{Kjƌo[J;׿~_֞fohѢE3g?V[mzJtkʔ)m8~_鮻_3*@ W^yEoFYl8:w:JpX/}.>}B+G}4JlQZZkz /h֬Yzw4j(z7; 6ȑc\cl@5hhaZQG]*Hk׮ZbSLрԹsgZJoN:$M8QR>s92d~a͜9Su]]|j M4I#F$}W:տ[TQQINjʕmN_z*l6`0ѣG֥^+B.=ZƍәgYf)jrJ} ݻ.]*Izg5c :T`Pvi:UN=zN:$;V˖-j; /P?OJg~;EQ_ dR ÛV-n^~js,TX \(@[N6mN*Lի8% C_|~{ 8PSǎ뮻* u][?tI'S"P߾}<+L{Wq [.]4|pmž}jj߾$)k=P߾}%}C~$ѐ!C6hŊ:#6AaZkϞ=ձcGC+VYgm٦NC}K;v,~.Jޮ[x\e]h߿ NR:qD]ve*++[kbQV[)kҤI:աCm3 CBnNCcplQ0N;vک_+זlRi0ՠvcmO[% C*++UvG U^d~hJP@D"/lh %{rAol~5t+ϓeDK֘>\s]lx/=+ d5 M5L?.߫ZdΝR}Ӹqh4%K'ӪU+5tiP&p&oUVVIIH+VĚ{8h%{a˔oU4 &ZYfY{\aZ^a*^!9%x>ynyucۘu=g]ch-%~TRzNoW^yYȆasLuEw}B!iO:EOP~G DE())Hdru4_MMۯC%I~L;B}w":[: kO ݻ~-y޽?҂ߩJ_":5x(;X:O~r:4r;}֭;AiTE}_͋VZ#G)HMma*--+_~Q} \={7|wJUz睷uSյkW@EDg d)hժ #޽G_޺ =Ama8J&w}5v|8t])T'pAX,x 0Vx}ݯ9sQeev Q.k)A}E *HT$P"ш_U~4e6mn}^yec۷Ʉ^~-\Bf;j]vS:QIIGJ&} &QhQ,V1c>ѹ瞡޽7|.LU*WwT4# FѨ{zu 7khΜwtMR~4bHUUUм& Xk+hCURRɓSO=`Е Zy^N5 Fe tRu^xV/VveDHee% #ܹT:k)Lh1J&3:c5kK:c{iΜيc:∣e[oW^ҼyhtW:SXG4qN4p =sOkҥ9?QݺuW*Q8VÇ@ UV*6# FeQ*T=t(r9&L&#Iw}4vRוY$oM ht8fZ2-k1rXSI#u{ma@PsGC*DD8fצ8BQoVR,Suz <!B!5ZH @GDcU(փ>w*++Եk7Mp<ϓ O?ֳ>#1AiA:(JSԩt3+/gFC=H`մiȑ{*ɡI 8~*))HdN1Zd*+zjC8:m ay}s82Qyy^}uqhŌ1в?qjTn]fY+0V}r}':ÔjAJZEE"RYY:w.UjgR߾뮻+L0EXnhu|Wii^y%7-X."['xB.\oKH$nshh>|?ZRpHRhMMN9t3N8UA6 ZqL&jyZѼZUUŕdԫW/g?Suu\^0Vx:$I7M?WuԱ@ƯaZZEQ)eYyh4>}7_7,?T;B` 4*hլ=:ğWmv'?9M-[`0(ۄ%-a1F:k j߾\ۇ 8<ӷ~"믿V P(&Mc`=Æ֙MyƮkmL&_:X\~fyra:4n]uեT:s=tuIxh1N6]pW-!~PHeee:sL&L&f5uuz՗W?jذ}L& h`-D"!IF R\NTJ%%% nPL&JJJֺ~6U*R$Q0\kdRPH@jE"Bb}ԨU7R)'x饗ur9q򷚚uaHMfK#Zr>VO;8;Vƍq+Vϩ)S4x`Tn]kӬu9hРAzG$=\ 4H?%I}ÔG}T}]wUS͞=[pXx\R>*z>zk=3d2r]x)S(뮓$W^yE=~S| unߓ8 rSC/BO?]wUIŋuC״iӴk8pvygwqҥKZ?׃>S* 2Ly &:i$-X@~a1҇~o]˗/믿^<~_x I%\SO=U{キ|op82Ƒ1n1?@isa>73o/KR)UTT?_:tkժUN6m;0)Aq4rHO*GÆ Ӈ~X|,Nk4hqIR&ы/C9ޯ{x'ԫW/vyg=c9#^{Nu饗* K/)n%Jf+j[2SDBY/_a|+[[Y@i d"HW եKB!}Wr]W7t:uꤝwY_K?:w,ck:qMZWz衚:uJ?Q2Ԙ1ct뭷x`P멧ұ+I:蠃4}tUWW]vu:^y:C׿U>ѨhTm6 nLa-˵{$uY>|]r%Zlf̘_8u!^y:]@~[H89r͛^xACUYYYiW^Q. ' I:Ct뭷gQGUg hEEjjj %m8e?j v=?LLN5J$ILկSk*'eѨQ$uQ#GԜ9s$Izս{w}o>S 0@=zp]W%%%kv! >\v]ves=jqf J$0XPct嗫o߾g}t_ܤF*uGkabN@VM;[]VPM%kau]yWl8b;FN:I]tN8XBZiIUW]D"qK.ѡCOڵSYYᰖ/_^ n/=1|G:_[rbXqgyFQb R)Iҟ'ԩSqkl6+qt.袋ԩS']p c 2Dgyf16—ْ;tZ5X.TrhiӦMkAlnFV[mxLRջwbhTCV[m uQz뭋;K:tP=TUUs9Gݺu+gck/eYGuƿMV-P٤\Ӳ;p\}Wt&DԮ]ˣD"<_\:t(Wv\~J0 i6/kP(`U"6C|=l᠂-3 9ַoC WK ky]^ż]bB[uP:Xba_oKK?~޺Ѩ-[gyJK,[յkWt(ݜ|ߪ,V5pF=DK-ÖZ5Pl͠0(4y=Ef.\CyΚjй]s]]8k\UMMR}{D=?:ģ4oj׮[vډzY*++ӬY/OҒ% g֭5V7ڍZwΆw5[w|C:S-)l!uҶV8c9Rw=Sw'³J$zHd4bľzY:STQnpGV%hA]Z!u]5Zp$S|oJ&zWp ͚:v]vMtihvymVlVk׮zͷ4k֋ƌ?ӹ瞡޽7|.LU* " _?:SYu^ tRu^XV/V^406)|@:#ybO^LFבG#V{̙x<:NdZCNAYrY{}_V8f3Aԛ#GK٤\H-2h%@Hqщʐ6Z+2O٬G ،7[KjixVo ݖXڕ$ayVm.O(pZEr+ᠫSvR!lnA4H.'eZtߟBa 8 ZlP#1AfBDXZ*hl܀r\Yk }_X8 1A-Vڕĕ1R(R:8 *-*Wi_XN`ZH$W_wyK;wҁ[ol6+G_… dN;u]yG XZ-h|W4wQW\q\_iO?Hۗ?믿l6+I/X@e@dU(ւ ?ߩSGO$yY1V͘q GFVi+c=\~>#J$rE*hѢ eTY$ 6L?UUU( F}>=sڵv\և \.={*Ji9i#%IsWU*--׊uEiٲ?Cl, A-1FLZ={)oNbj}'ԩ<ϗԾ}{3{_瞙aԾ}{4Q-8J&OՍ7ެ@ cSPIIڷo/k$h4;jʔ_W_VII=&*Z4k2ƍ%##ԡCXB:uP8uH55j׮|ߧ*A-*))}ݣv[͝;[@D?<{?s ^~E1R#FT<(zX&2*e2){oђ&N_U˗/S(~F_Z*@bcfN;kȐ=81Ҋ1'c駝vA+Y+Ӿ񸬵A hь1JR*- 4\'e2iRIIv!la@g r]w0A4o6hК>yQoVR,Suh%h`.!@y;IR&! af=Ov d=ON8]F JZc(0id?h5l~A4qdM <̮.Zk\*ɯfGfcU0TnURɇI͂0}a-ZPӦMoh4Jh&fQrwIgAp~|9;6;~y/GÎ3EhNA4)O>գS]srY͌0&eQ /9]'U<$y^shh2﫼\=tup8ҰB!v@MZ+uUSSwt:ŋ_R]ua7P60&QN:iy^NpX@@@P_?kM&_ OQ `d[Sŋ+@3W8UEEo%Ắ>8豕14 ſ[+p‰rJ$RAY#UVVCa]J!Ai bGq%Ǒq]בLs @CQDXec5VU es9yo @+CDG=\uV&**"afG~l |y׫:Pos 84J:A4T>O]tϕe 43 1F@@tMx VxLy=:" r=*++Ӆ'וJK {@E4 k\UMMO޽j/^_|I:taC$ D{:)oy9aA|¯b3quk|/nξT1rw|) rQС{kʔxqh k+kZalScYW=B\:9΍|J3>N=zl4{ ̈́0 aj޼yz 4qD۷^Ak]ҥK{O>QyyN8ykܹzwTQQ렃Ryyy1|cTSS{N:uꤱc{vV:H@3&}_=#ۚ8qzbPېu0cL1r9viu뷿r\$͜9SG}fϞ+Vnĉ7#cVX?\7tVXgyFƍӫZq66cjT.T05JSNg;Ot饗?^{59ʛVDBHN,H( * iӦi=Пg͘1xn}GcƌQ~$ITJGo[z뭒|P_~x u]t '覛n~,M} 6Pꫯ*/;dzD"1cwquGG-뮻\.'텀뮻_~*(hԨQϊԩS'u޽8+50Ƒ19ƕ1B&*cǎСCX߾} 5:KUUlǪNzI4 Jx xG\wq,X_~Y:5?]*Q2[T*gJ}+߳|[צ߬|ߪ@1M FuYB!bmCa.5Es6 r9r-/uwH<Ӏt)?>-\P'NԑGYzrcωJge+Y+ (*Uc$_2Yj# T{ϿB#<+W{C#<_׺;5`?~_뮻õxbwy AEqԭ~*[M.!)+ͮv@$)IhhMԽ{wUVV*TVV*˩[n VaCB|u97:b[#Gرcf5`bӛ.L[mUg}ZX]]tL5U j.G+`Dl´ʽ[4x`Iw߽ιk  ﮹h"-YOXX|ߗzugoԱ[||VRZ_Zg{ c$U>~70@pni袋tF\s&O=zX!a $֮bgٵKIIQ&Q&y2+몴sɤB&_{H%V*Tk`cU ]W(TU,AEaFX,Q|eeQUg(xw*++Եk7Mp<ϓ O?ֳ>#1&UF- a*B:wVrرzyz^k8RN=LM:M>CqVKi:Ѓy ]M 9JᄃbXElDկ_?]s5=-^~L&tX,jO@PUzwaRTߧ{VvD@ApD!ʢ(Q낸IT5br1jB1+&j4AT,(HDaa:?n?js8;L!""""&VfBx;tygmqq1x(ZEDDDDPl}z]8d#OcTVnQ&oO*Ev t:m}|Vz&|D"ժ _֒G^ Ѯ]!ee%`|z8$V(̜ż|gLr=;w墋.! Q^-SH($V _EOSTTD:N8a{bDqDwpƌ9h4BDgf"@u CE_3%oX}7@]@u][("""¨ePDvq'pcǞD2&tsq:tO 1mڏ!0NCaPDq p0L&p! J6p:x]/("""zi0q0PRRkD"'J$ZKIIÇyTWWq W_?@^^>{""""Qˠ4J0w3wp:wBUUӿ r,OSF=j%i!2(" f%//5k>୷ŷuvypu`*?B>ck5?HKAi0)( >H$2"LRRRL(tT -IDATauN*TH 0(" b<6mdΜGIy!//~=CEEĥ7={" kDQDaPDqjj9?[_N$J3f,TVnM̜  &*0 " ۷k&B2eT*E>}9ꨁ.X hH$""""- 41Jٴu (~2c[etIbT7EDDDDI) J8mEGLjH0(fUp}yݪo Z184NГͽ""""" "{(,, i5ֆ}ui۶M9}R)pso HcYkq]yyy̜/0gcq@:vť\t%TTlb),}ag9Ayx\#H0(o_Jv|٧l\Aii% Q^^?:gq66p6{}@DE}կ~&}F,3Gy cƌR~_x ~CaP1b1ƌ`~ÆW_ȑ'8Th4F] EETDDDDZ AF2ƐJҥ+_q0T c$_O8 (""""- 0ƐH$bd4ql3j0GSIHˢ0(1/P HK0(bF4SotH0(`I֐ڂ n*u1Kt, ԍNstCSǑƸnkl8tւMX/""""( 41k-ee%CUU-.EE@,6*DDDDdQiBZBO?֭墋.!ʣ?y1VC2bР!xHR+HS4&N)**bٲW[=A0ee<̓h5=${s7HPյ O|,^ܛ vm9T[nOyv #:p]wCDDDDr C,3iebQ .a!??ČwrJ[Au$N0Hhir9;wZ!ϳ7ѐmlv[e!1`@;n0֦ \.\’%֩;_~%sfek}\C|ՉB7 wwZcaAڶUW]KUUɣ[L: kr3J[>ۈH0 kxd2IAAA xTh4J^^ WhuoXkD"[2ۘdT*{߶R]]MAAۯd2I$Dc^ӫWo @uu-X1c0~i[q1.""""Zw,rO2aΨQH|'|SOeر=3fǁ/[0~xƌѣ?|5pxӉ'1&7'F7R~%}(-*غTrq(k>+PZա-},i'K6of*W ӘSꫯ& rM7u,NRRRADAAo.z(ZwβeB 8s4qg0g*++Yh&L^Wӭ[7F_s=[oDQmGcбNf⇴dK'ԪzZx & jJK:@u(-AKCZ Ǔhliap{_5ஞ#3P 7?OϟOii)dxGyYt)p>,\s  gϞXk)((ڞth4J޽;wu555|N[-XH$OS(RYY/ @D")S8x:u*r~]I[zHpw""""15: NC]UZet]7;ł8Cmm-Tjq~1w\ϟO׮]I$`o3p@88P#FPTTĿlYu 0~x&MW_MYv-1o>w+W}BGq-bʔ)ٹ^"wq.~:K,alIXk=X:InZUݻSYYɻ1s=G*!xG071{l̙KK+{`&ӇyQ^^NN>9Cزe O=cƌW^XW_}|;ى3S ;3gfcm߂ 8y衇1cưtRFŊ+5kfu] _-°akt c 1C""""lcpQk^hUa0ׯ_?N>d&M)ƍD8Y~=]wyyy|g\5{*1B!nfڴi/̸q8+W\q} 0qFSOeŋ4iR6 nܸ1;}۶mꫳ-Z6lؐχzɓ'uTtԉ=z#0j(&Oرc3fL?/7x#/lRᶗmՍcm]ؾg_f<kmiw~{so)5zh: 0gq]teee 4<***cpiӆ>:D8Cիtb)..fȐ!B!0a]v%psuqye#еkWN۶m(((`{B+**bСk ;`VZZʰaòg}6eeecc޽;%%%yW]uEEEn1x`jjj8p`66-Wxdhi>1$}K:sxI‘n[ STTupWTTHIIEEG"Ȟc(((|R)WB!kإ`ƶ d=y `!~[f>g 9>4/8T'#y(gu> _T|E ukK򨭭xU!L1hNN 3?4@:ih-ZS\\̙2p1-[*q:tH0dÆ lѶm[<#I'L&]*0("""""dxukAtkS瑟C~Gpab[kY~Zm7Q]#*+7Ch߾h5=dv:/ Bu%<,_fŬ][N*[r9~xm/#Cb1\Z立fLkC,3iebQ .a!??ČwrJ[Au$ z#ڶ-ŔxRART^1EDDDDqx<ƀ9X&pYp K,[RVV~KG\~̙5558aܸoq衽?MO?a֬_`&L8#^Wˠc Dh4J Cmmju]Mv0&sLaPDDDDDr1@8ns]7I( J8zS7Qi.qpis `KM2ʖDhs}ȾdlMo) J98oؼZ<㦛n1/cΜG1bTs~KaPDD@ y֐O% 矱r O_7|ч1O k}>|X͛7Gk8唱SJDDd/49k-tm͌*ab(3gg_@*?Lt_|ݻ+!5"""{HaPDD6q1W_}g?HEByrYSs{Z~ka=0(""立Nܶm[9cز%ƍu8ƍ<B z4#G'#ns~IaPDD1mС (--41:{l٫ѱc ˗ 8ڕҡCq]W-"""{HȈH2ƐH$`Ӧ/O5,Xq`ذ!!//^W|+OGC((>`}86EDDK""1AӷḮK0$qqxi׮= /Ė-5tڍo|} x ~HDAPDDdePDDTfF\אJݪ[rH/c ID0yM&L8c p8 (""EDYkٴj\:5!F8N6YkbS~3 ""OnOc.Q) J8z~"q cМ"""'Ai0 FST=GaP$9*$H7Ps8ilMp ʂYحטIs34{"""_Qsa̐{3DJ&z G͛äRiQˠ;}Kaa agL]EaPŘmnͽ1Sq갥Phn:*; """"""9HaPDDDDD$) A0(e_ˌ`"PZաÖiOZBZ[e6;}f}Mu(-PZadmz2jv(iX,JF%p \bdso,աCi T- 0("""""t͠HRA """"""9HaPDDDDD$) A0("""""EDDDDDr HRAic 6&I+}18ƘI:";ՏPҶu8S{3]>HCejigua`l#ͻe"M)AeSi םP5U5uDjWa0(ySGQ͛|G)B Fez|g[|t҅>:+Bǎ93h׮]Yx1o=z`$HvZʕ+Ylt֍N;l%I-[ƪUO>^z;bܹs3bĈtÆ ̝;-[0j(FSO=5jTZuPʼQ#'Nd :? 2|7Qc Ì5T*EEE{f…u'? ?0cƌalٲ'xݻ>p׳h"N>d/_NAAs̡M6JӸ˜9s>}:CCXp8c=F>})djg\s5xos=0a֭[Dž^׏ rg3mڴl梋.SNtܙE1ekNV.2~aZ~R@@ĊD*Zӟ0VTTXk]fѣ_o==H$oavڴivر2b ۵kWx}Ǐ]vY خ]wyZkmmm=ci=ȶ|߷Z;> s16LZk2e5jTve˖.]إKf϶\pA?Gv͚5Zkts華p{G췿m{뮻.{Xkϛ7s9mٳ'Gf޼yQyuQ@]+Mfe_[n=T*^ȫ͛?>C_~$ 9Y`nD22J//.cƌ>֜PxWoYqq1pqHR,Y'ydc=3|ʕ+8q"P׺3~xڶm/]&+skSH&utyv}{|Ϟ=ٲeKIdW2#ٓW^y}fVJS}YMUW]? 7܀8|'$ zYq}a|8Ssu]w]G]dpڴi9#8z_xغ( .E"|ߧBh LMMM3RP]]CacE2=3|#<›o5\]Gu(JMM |;{`MM ƘleR]] @8Z:Tٕ1^wᦛnv[d@2R抑}&C*m:2^dEXp!z+{/ T2q_?׏ /P(t:%;άpٙLp8̙3(..jQu 8ر#PWۮ~z8zD]ho&O=_|1^z)k֬aҥL1p@ҥK7L^ӧ}!//RzM `РA:&=L_{5۵kWunۮ^z"{wޱV'OÇo׿J=o|vĉO>?֭}dzϱvZۿ{ 7r裏Ν;%KXk5\j*ۧO{WM6?ЮY~guTT2u /ؙ3gUV5k_|uQ뿲Ν;v>S?W\q4ḫC9.\~3ϴzj3SNV9{gxغhy%[y1sL"mڴ?!Æ 䵲׬\r%>t^|k֬[o?&psW`Z8L>/ { mod bench_itoap_write_to_vec { use test::{Bencher, black_box}; $( $(#[$attr])* #[bench] pub fn $name(b: &mut Bencher) { let mut buf = Vec::with_capacity(40); b.iter(|| { buf.clear(); itoap::write_to_vec(&mut buf, black_box($value)); black_box(&mut buf); }); } )* } mod bench_itoap_write_to_ptr { use test::{Bencher, black_box}; $( $(#[$attr])* #[bench] pub fn $name(b: &mut Bencher) { let mut buf = Vec::::with_capacity(40); b.iter(|| unsafe { itoap::write_to_ptr(buf.as_mut_ptr(), black_box($value)); black_box(&mut buf); }); } )* } mod bench_itoa_write { use test::{Bencher, black_box}; $( $(#[$attr])* #[bench] pub fn $name(b: &mut Bencher) { let mut buf = Vec::with_capacity(40); b.iter(|| { buf.clear(); let _ = itoa::write(&mut buf, black_box($value)); black_box(&mut buf); }); } )* } mod bench_std_fmt { use test::{Bencher, black_box}; $( $(#[$attr])* #[bench] pub fn $name(b: &mut Bencher) { use std::io::Write; let mut buf = Vec::with_capacity(40); b.iter(|| { buf.clear(); let _ = write!(&mut buf, "{}", black_box($value)); black_box(&mut buf); }); } )* } } } benches! { bench_u64_0(0u64), bench_u64_half(::max_value() as u64), bench_u64_max(::max_value()), bench_i16_0(0i16), bench_i16_min(::min_value()), bench_u128_0(0u128), bench_u128_max(::max_value()) } #[bench] fn noop(b: &mut Bencher) { let mut buf = Vec::::with_capacity(40); b.iter(|| { buf.clear(); black_box(0i16); black_box(&mut buf); }) } itoap-1.0.1/codecov.yml010066400017500001751000000003651377332021500131650ustar 00000000000000coverage: status: project: default: threshold: 3% branches: - master only_pulls: false patch: default: target: auto branches: - master only_pulls: true itoap-1.0.1/rustfmt.toml010066400017500001751000000002001370134121700134010ustar 00000000000000max_width = 90 hard_tabs = false use_field_init_shorthand = true edition = "2018" reorder_imports = true reorder_modules = true itoap-1.0.1/src/common.rs010066400017500001751000000163171401276171500134520ustar 00000000000000use core::ops::{Div, Mul, Sub}; use core::ptr; const DEC_DIGITS_LUT: &[u8] = b"\ 0001020304050607080910111213141516171819\ 2021222324252627282930313233343536373839\ 4041424344454647484950515253545556575859\ 6061626364656667686970717273747576777879\ 8081828384858687888990919293949596979899"; #[inline] pub fn divmod + Mul + Div>( x: T, y: T, ) -> (T, T) { // https://bugs.llvm.org/show_bug.cgi?id=38217 let quot = x / y; let rem = x - quot * y; (quot, rem) } #[inline] pub unsafe fn lookup>(idx: T) -> *const u8 { DEC_DIGITS_LUT.as_ptr().add((idx.into() as usize) << 1) } /// write integer smaller than 10000 #[inline] pub unsafe fn write4(n: u32, buf: *mut u8) -> usize { debug_assert!(n < 10000); if n < 100 { if n < 10 { *buf = n as u8 + 0x30; 1 } else { ptr::copy_nonoverlapping(lookup(n), buf, 2); 2 } } else { let (n1, n2) = divmod(n, 100); if n < 1000 { *buf = n1 as u8 + 0x30; ptr::copy_nonoverlapping(lookup(n2), buf.add(1), 2); 3 } else { ptr::copy_nonoverlapping(lookup(n1), buf.add(0), 2); ptr::copy_nonoverlapping(lookup(n2), buf.add(2), 2); 4 } } } /// write integer smaller than 10000 with 0 padding #[inline] pub unsafe fn write4_pad(n: u32, buf: *mut u8) { debug_assert!(n < 10000); let (n1, n2) = divmod(n, 100); ptr::copy_nonoverlapping(lookup(n1), buf, 2); ptr::copy_nonoverlapping(lookup(n2), buf.add(2), 2); } #[inline] pub unsafe fn write8(n: u32, buf: *mut u8) -> usize { debug_assert!(n < 100_000_000); if n < 10000 { write4(n as u32, buf) } else { let (n1, n2) = divmod(n, 10000); let l = if n1 < 100 { if n1 < 10 { *buf = n1 as u8 + 0x30; 5 } else { ptr::copy_nonoverlapping(lookup(n1), buf, 2); 6 } } else { let (n11, n12) = divmod(n1, 100); if n1 < 1000 { *buf = n11 as u8 + 0x30; ptr::copy_nonoverlapping(lookup(n12), buf.add(1), 2); 7 } else { ptr::copy_nonoverlapping(lookup(n11), buf.add(0), 2); ptr::copy_nonoverlapping(lookup(n12), buf.add(2), 2); 8 } }; let (n21, n22) = divmod(n2, 100); ptr::copy_nonoverlapping(lookup(n21), buf.add(l - 4), 2); ptr::copy_nonoverlapping(lookup(n22), buf.add(l - 2), 2); l } } #[inline] pub unsafe fn write8_pad(n: u32, buf: *mut u8) { debug_assert!(n < 100_000_000); let (n1, n2) = divmod(n, 10000); let (n11, n12) = divmod(n1, 100); let (n21, n22) = divmod(n2, 100); ptr::copy_nonoverlapping(lookup(n11), buf, 2); ptr::copy_nonoverlapping(lookup(n12), buf.add(2), 2); ptr::copy_nonoverlapping(lookup(n21), buf.add(4), 2); ptr::copy_nonoverlapping(lookup(n22), buf.add(6), 2); } pub unsafe fn write_u8(n: u8, buf: *mut u8) -> usize { if n < 10 { *buf = n + 0x30; 1 } else if n < 100 { ptr::copy_nonoverlapping(lookup(n), buf, 2); 2 } else { let (n1, n2) = divmod(n, 100); *buf = n1 + 0x30; ptr::copy_nonoverlapping(lookup(n2), buf.add(1), 2); 3 } } pub unsafe fn write_u16(n: u16, buf: *mut u8) -> usize { if n < 100 { if n < 10 { *buf = n as u8 + 0x30; 1 } else { ptr::copy_nonoverlapping(lookup(n), buf, 2); 2 } } else if n < 10000 { if n < 1000 { let (a1, a2) = divmod(n, 100); *buf = a1 as u8 + 0x30; ptr::copy_nonoverlapping(lookup(a2), buf.add(1), 2); 3 } else { let (a1, a2) = divmod(n, 100); ptr::copy_nonoverlapping(lookup(a1), buf, 2); ptr::copy_nonoverlapping(lookup(a2), buf.add(2), 2); 4 } } else { let (a1, a2) = divmod(n, 10000); let (b1, b2) = divmod(a2, 100); *buf = a1 as u8 + 0x30; ptr::copy_nonoverlapping(lookup(b1), buf.add(1), 2); ptr::copy_nonoverlapping(lookup(b2), buf.add(3), 2); 5 } } /// Multiply unsigned 128 bit integers, return upper 128 bits of the result #[inline] fn u128_mulhi(x: u128, y: u128) -> u128 { let x_lo = x as u64; let x_hi = (x >> 64) as u64; let y_lo = y as u64; let y_hi = (y >> 64) as u64; // handle possibility of overflow let carry = (x_lo as u128 * y_lo as u128) >> 64; let m = x_lo as u128 * y_hi as u128 + carry; let high1 = m >> 64; let m_lo = m as u64; let high2 = x_hi as u128 * y_lo as u128 + m_lo as u128 >> 64; x_hi as u128 * y_hi as u128 + high1 + high2 } /// Write u128 in decimal format /// /// Integer division algorithm is based on the following paper: /// /// T. Granlund and P. Montgomery, “Division by Invariant IntegersUsing Multiplication,” /// in Proc. of the SIGPLAN94 Conference onProgramming Language Design and /// Implementation, 1994, pp. 61–72 /// unsafe fn write_u128_big(mut n: u128, mut buf: *mut u8) -> usize { const DIV_FACTOR: u128 = 76624777043294442917917351357515459181; const DIV_SHIFT: u32 = 51; const POW_10_8: u64 = 100000000; const POW_10_16: u64 = 10000000000000000; debug_assert!(n > core::u64::MAX as u128); // hold per-8-digits results // i.e. result[0] holds n % 10^8, result[1] holds (n / 10^8) % 10^8, ... let mut result = [0u32; 5]; { // performs n /= 10^16 let quot = u128_mulhi(n, DIV_FACTOR) >> DIV_SHIFT; let rem = (n - quot * POW_10_16 as u128) as u64; debug_assert_eq!(quot, n / POW_10_16 as u128); debug_assert_eq!(rem as u128, n % POW_10_16 as u128); n = quot; result[1] = (rem / POW_10_8) as u32; result[0] = (rem % POW_10_8) as u32; debug_assert_ne!(n, 0); debug_assert!(n <= core::u128::MAX / POW_10_16 as u128); } let result_len = if n >= POW_10_16 as u128 { // performs n /= 10^16 let quot = (n >> 16) as u64 / (POW_10_16 >> 16); let rem = (n - POW_10_16 as u128 * quot as u128) as u64; debug_assert_eq!(quot as u128, n / POW_10_16 as u128); debug_assert_eq!(rem as u128, n % POW_10_16 as u128); debug_assert!(quot <= 3402823); result[3] = (rem / POW_10_8) as u32; result[2] = (rem % POW_10_8) as u32; result[4] = quot as u32; 4 } else if (n as u64) >= POW_10_8 { result[3] = ((n as u64) / POW_10_8) as u32; result[2] = ((n as u64) % POW_10_8) as u32; 3 } else { result[2] = n as u32; 2 }; let l = write8(*result.get_unchecked(result_len), buf); buf = buf.add(l); for i in (0..result_len).rev() { write8_pad(*result.get_unchecked(i), buf); buf = buf.add(8); } l + result_len * 8 } #[inline] pub unsafe fn write_u128(n: u128, buf: *mut u8) -> usize { if n <= core::u64::MAX as u128 { crate::write_u64(n as u64, buf) } else { write_u128_big(n, buf) } } itoap-1.0.1/src/fallback.rs010066400017500001751000000031231400662405300137040ustar 00000000000000use core::ptr; use crate::common::{divmod, lookup, write4, write4_pad, write8_pad}; pub unsafe fn write_u32(n: u32, buf: *mut u8) -> usize { if n < 10000 { write4(n, buf) } else if n < 100_000_000 { let (n1, n2) = divmod(n, 10000); let l = write4(n1, buf); write4_pad(n2, buf.add(l)); l + 4 } else { let (n1, n2) = divmod(n, 100_000_000); let l = if n1 >= 10 { ptr::copy_nonoverlapping(lookup(n1), buf, 2); 2 } else { *buf = n1 as u8 + 0x30; 1 }; write8_pad(n2, buf.add(l)); l + 8 } } pub unsafe fn write_u64(n: u64, buf: *mut u8) -> usize { if n < 10000 { write4(n as u32, buf) } else if n < 100_000_000 { let (n1, n2) = divmod(n, 10000); let l = write4(n1 as u32, buf); write4_pad(n2 as u32, buf.add(l)); l + 4 } else if n < 10_000_000_000_000_000 { let (n1, n2) = divmod(n, 100_000_000); let (n1, n2) = (n1 as u32, n2 as u32); let l = if n1 < 10000 { write4(n1, buf) } else { let (n11, n12) = divmod(n1, 10000); let l = write4(n11, buf); write4_pad(n12, buf.add(l)); l + 4 }; write8_pad(n2, buf.add(l)); l + 8 } else { let (n1, n2) = divmod(n, 10_000_000_000_000_000); let (n21, n22) = divmod(n2, 100_000_000); let l = write4(n1 as u32, buf); write8_pad(n21 as u32, buf.add(l)); write8_pad(n22 as u32, buf.add(l + 8)); l + 16 } } itoap-1.0.1/src/lib.rs010066400017500001751000000305451401351240200127130ustar 00000000000000//! This crate provides even faster functions for printing integers with decimal format //! than [itoa](https://crates.io/crates/itoa) crate. //! //! If you want to write integers in decimal format to `String`, `Vec` or any other //! contiguous buffer, then this crate is the best choice. //! //! If you want to write integers to a `std::io::Write` or `std::fmt::Write`, //! [itoa](https://github.com/dtolnay/itoa) crate and `itoap` crate shows almost same //! performance. //! //! The implementation is based on the `sse2` algorithm from //! [itoa-benchmark](https://github.com/miloyip/itoa-benchmark) repository. //! While `itoa` crate writes integers from **last** digits, this algorithm writes //! from **first** digits. It allows integers to be written directly to the buffer. //! That's why `itoap` is faster than `itoa`. //! //! # Feature Flags //! //! - `alloc`: use [alloc](https://doc.rust-lang.org/alloc/) crate (enabled by default) //! - `std`: use [std](https://doc.rust-lang.org/std/) crate (enabled by default) //! - `simd`: use SIMD intrinsics if available //! //! # Examples //! //! ``` //! # #[cfg(feature = "std")] { //! let value = 17u64; //! //! let mut buf = String::new(); //! buf.push_str("value: "); //! itoap::write_to_string(&mut buf, value); //! //! assert_eq!(buf, "value: 17"); //! # } //! ``` //! //! ``` //! use core::mem::{MaybeUninit, transmute}; //! use itoap::Integer; //! //! unsafe { //! let mut buf = [MaybeUninit::::uninit(); i32::MAX_LEN]; //! let len = itoap::write_to_ptr(buf.as_mut_ptr() as *mut u8, -2953); //! let result: &[u8] = transmute(&buf[..len]); //! assert_eq!(result, b"-2953"); //! } //! ``` #![allow(clippy::many_single_char_names, clippy::needless_range_loop)] #![cfg_attr(docsrs, feature(doc_cfg))] #![no_std] #[cfg(feature = "alloc")] extern crate alloc; #[cfg(feature = "alloc")] use alloc::string::String; #[cfg(feature = "alloc")] use alloc::vec::Vec; #[cfg(feature = "std")] extern crate std; mod common; use common::*; #[cfg(not(all( any(target_arch = "x86_64", target_arch = "x86"), target_feature = "sse2", feature = "simd", not(miri), )))] mod fallback; #[cfg(not(all( any(target_arch = "x86_64", target_arch = "x86"), target_feature = "sse2", feature = "simd", not(miri), )))] use fallback::{write_u32, write_u64}; #[cfg(all( any(target_arch = "x86_64", target_arch = "x86"), target_feature = "sse2", feature = "simd", not(miri), ))] mod sse2; #[cfg(all( any(target_arch = "x86_64", target_arch = "x86"), target_feature = "sse2", feature = "simd", not(miri), ))] use sse2::{write_u32, write_u64}; mod private { pub trait Sealed {} } /// An integer that can be written to pointer. pub trait Integer: private::Sealed { /// Maximum digits of the integer const MAX_LEN: usize; #[doc(hidden)] unsafe fn write_to(self, buf: *mut u8) -> usize; } macro_rules! impl_integer { ($unsigned:ty, $signed:ty, $conv:ty, $func:ident, $max_len:expr) => { impl private::Sealed for $unsigned {} impl private::Sealed for $signed {} impl Integer for $unsigned { const MAX_LEN: usize = $max_len; #[inline] unsafe fn write_to(self, buf: *mut u8) -> usize { $func(self as $conv, buf) } } impl Integer for $signed { const MAX_LEN: usize = $max_len + 1; #[inline] unsafe fn write_to(self, mut buf: *mut u8) -> usize { let mut n = self as $conv; if self < 0 { *buf = b'-'; buf = buf.add(1); n = (!n).wrapping_add(1); } $func(n, buf) + (self < 0) as usize } } }; } impl_integer!(u8, i8, u8, write_u8, 3); impl_integer!(u16, i16, u16, write_u16, 5); impl_integer!(u32, i32, u32, write_u32, 10); impl_integer!(u64, i64, u64, write_u64, 20); impl_integer!(u128, i128, u128, write_u128, 39); #[cfg(target_pointer_width = "16")] impl_integer!(usize, isize, u16, write_u16, 5); #[cfg(target_pointer_width = "32")] impl_integer!(usize, isize, u32, write_u32, 10); #[cfg(target_pointer_width = "64")] impl_integer!(usize, isize, u64, write_u64, 20); /// Write integer to the buffer pointer directly. /// /// This is fast operation, but does not check any safety. /// /// # Safety /// /// Behaviour is undefined if any of the following conditions are violated: /// /// - `buf` must point to sufficient /// [valid](https://doc.rust-lang.org/core/ptr/index.html#safety) bytes of memory to /// write `value` /// - `buf` must be aligned with `core::mem::align_of::()` bytes #[inline] pub unsafe fn write_to_ptr(buf: *mut u8, value: V) -> usize { value.write_to(buf) } /// Write integer to `Vec`. /// /// Note that this function is safe because it checks the capacity of `Vec` and calls /// `Vec::reserve()` if the `Vec` doesn't have enough capacity. #[cfg(feature = "alloc")] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] #[inline] pub fn write_to_vec(buf: &mut Vec, value: V) { debug_assert!(buf.len() <= core::isize::MAX as usize); // benchmark result suggests that we gain more speed by manually checking the // buffer capacity and limits `reserve()` call if buf.len().wrapping_add(V::MAX_LEN) > buf.capacity() { buf.reserve(V::MAX_LEN); } unsafe { let l = value.write_to(buf.as_mut_ptr().add(buf.len())); buf.set_len(buf.len() + l); } } /// Write integer to `String`. /// /// Note that this function is safe because it checks the capacity of `String` and calls /// `String::reserve()` if the `String` doesn't have enough capacity. #[cfg(feature = "alloc")] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] #[inline] pub fn write_to_string(buf: &mut String, value: V) { unsafe { write_to_vec(buf.as_mut_vec(), value) }; } /// Write integer to an `fmt::Write` /// /// Note that this operation may be slow because it writes the `value` to stack memory, /// and then copy the result into `writer`. /// /// This function is for compatibility with [itoa](https://docs.rs/itoa) crate and you /// should use `write_to_vec` or `write_to_string` if possible. #[inline] pub fn fmt( mut writer: W, value: V, ) -> core::fmt::Result { use core::mem::MaybeUninit; unsafe { let mut buf = [MaybeUninit::::uninit(); 40]; let l = value.write_to(buf.as_mut_ptr() as *mut u8); let slc = core::slice::from_raw_parts(buf.as_ptr() as *const u8, l); writer.write_str(core::str::from_utf8_unchecked(slc)) } } /// Write integer to an `io::Write` /// /// Note that this operation may be slow because it writes the `value` to stack memory, /// and then copy the result into `writer`. /// You should use `write_to_vec` or `write_to_string` if possible. /// /// This function is for compatibility with [itoa](https://docs.rs/itoa) crate and you /// should use `write_to_vec` or `write_to_string` if possible. #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[inline] pub fn write( mut writer: W, value: V, ) -> std::io::Result { use core::mem::MaybeUninit; unsafe { let mut buf = [MaybeUninit::::uninit(); 40]; let l = value.write_to(buf.as_mut_ptr() as *mut u8); let slc = core::slice::from_raw_parts(buf.as_ptr() as *const u8, l); writer.write(slc) } } #[cfg(test)] mod tests { use core::cmp::PartialEq; use core::fmt; use rand::rngs::SmallRng; use rand::{Rng, SeedableRng}; struct ArrayStr { buf: [u8; 40], len: usize, } impl ArrayStr { fn new() -> Self { Self { buf: [0u8; 40], len: 0, } } fn as_str(&self) -> &str { core::str::from_utf8(&self.buf[..self.len]).unwrap() } } impl fmt::Write for ArrayStr { fn write_str(&mut self, s: &str) -> fmt::Result { self.buf[self.len..self.len + s.len()].copy_from_slice(s.as_bytes()); self.len += s.len(); Ok(()) } } impl fmt::Debug for ArrayStr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.as_str().fmt(f) } } impl PartialEq for ArrayStr { fn eq(&self, rhs: &ArrayStr) -> bool { self.as_str().eq(rhs.as_str()) } } fn itoap_fmt(value: I) -> ArrayStr { let mut buf = ArrayStr::new(); let _ = super::fmt(&mut buf, value); buf } fn std_fmt(value: I) -> ArrayStr { use core::fmt::Write; let mut buf = ArrayStr::new(); let _ = write!(buf, "{}", value); buf } // comprehenisive test #[test] fn test_i8_all() { for n in core::i8::MIN..=core::i8::MAX { assert_eq!(itoap_fmt(n), std_fmt(n)); } } // random test #[test] #[cfg(not(miri))] fn test_u64_random() { let mut rng = SmallRng::seed_from_u64(0xb0d39604298743d0); for _ in 0..1000 { let value = rng.gen::(); assert_eq!(itoap_fmt(value), std_fmt(value)); } } // random test #[test] #[cfg(not(miri))] fn test_u128_random() { let mut rng = SmallRng::seed_from_u64(0x73cdb9a66816e721); for _ in 0..1000 { let value = rng.gen::(); assert_eq!(itoap_fmt(value), std_fmt(value)); } } // random digits test #[test] #[cfg(not(miri))] fn test_u64_random_digits() { let mut rng = SmallRng::seed_from_u64(0xe6f827f2dce6fae4); for _ in 0..1000 { let value = rng.gen::() >> (rng.gen::() % 64); assert_eq!(itoap_fmt(value), std_fmt(value)); } } // random digits test #[test] #[cfg(not(miri))] fn test_u128_random_digits() { let mut rng = SmallRng::seed_from_u64(0xd7b31256794c1406); for _ in 0..1000 { let value = rng.gen::() >> (rng.gen::() % 128); assert_eq!(itoap_fmt(value), std_fmt(value)); } } // cov:begin-ignore macro_rules! boundary_test { ($name:ident, $type:ident) => { #[test] fn $name() { let mut current = 1; loop { assert_eq!(itoap_fmt(current - 1), std_fmt(current - 1)); assert_eq!(itoap_fmt(current), std_fmt(current)); assert_eq!(itoap_fmt(current + 1), std_fmt(current + 1)); if current > core::$type::MAX / 10 { break; } current *= 10; } assert_eq!(itoap_fmt(core::$type::MIN), std_fmt(core::$type::MIN)); assert_eq!(itoap_fmt(core::$type::MAX), std_fmt(core::$type::MAX)); } }; } // cov:end-ignore // boundary tests boundary_test!(test_u8, u8); boundary_test!(test_u16, u16); boundary_test!(test_u32, u32); boundary_test!(test_u64, u64); boundary_test!(test_u128, u128); boundary_test!(test_usize, usize); boundary_test!(test_i8, i8); boundary_test!(test_i16, i16); boundary_test!(test_i32, i32); boundary_test!(test_i64, i64); boundary_test!(test_i128, i128); boundary_test!(test_isize, isize); #[test] #[cfg(feature = "alloc")] #[cfg(not(miri))] fn write_to_string_test() { use alloc::string::{String, ToString}; let mut buf = String::new(); let mut rng = SmallRng::seed_from_u64(0xa0983844f42abf9d); for _ in 0..1000 { let value = rng.gen::(); buf.clear(); super::write_to_string(&mut buf, value); assert_eq!(buf, value.to_string()); } } #[test] #[cfg(feature = "std")] #[cfg(not(miri))] fn io_test() { use alloc::string::ToString; use alloc::vec::Vec; let mut buf = Vec::new(); let mut rng = SmallRng::seed_from_u64(0x36f09d2f9acc29b8); for _ in 0..1000 { // xorshift let value = rng.gen::(); buf.clear(); super::write(&mut buf, value).unwrap(); assert_eq!(std::str::from_utf8(&*buf).unwrap(), value.to_string()); } } } itoap-1.0.1/src/sse2.rs010066400017500001751000000104411400662021300130140ustar 00000000000000#![allow(non_upper_case_globals)] #[cfg(target_arch = "x86")] use core::arch::x86::*; #[cfg(target_arch = "x86_64")] use core::arch::x86_64::*; use crate::common::{divmod, lookup, write4, write4_pad}; use core::ptr; #[repr(align(16))] struct Aligned(T); impl std::ops::Deref for Aligned { type Target = T; #[inline] fn deref(&self) -> &T { &self.0 } } const kDiv10000: u32 = 0xd1b71759; const kDivPowersVector: Aligned<[u16; 8]> = Aligned([8389, 5243, 13108, 32768, 8389, 5243, 13108, 32768]); const kShiftPowersVector: Aligned<[u16; 8]> = Aligned([ 1 << (16 - (23 + 2 - 16)), 1 << (16 - (19 + 2 - 16)), 1 << (16 - 1 - 2), 1 << (15), 1 << (16 - (23 + 2 - 16)), 1 << (16 - (19 + 2 - 16)), 1 << (16 - 1 - 2), 1 << (15), ]); #[inline] unsafe fn convert_8digits_sse2(value: u32) -> __m128i { debug_assert!(value <= 99999999); // abcd, efgh = abcdefgh divmod 10000 let abcdefgh = _mm_cvtsi32_si128(value as i32); let abcd = _mm_srli_epi64( _mm_mul_epu32(abcdefgh, _mm_set1_epi32(kDiv10000 as i32)), 45, ); let efgh = _mm_sub_epi32(abcdefgh, _mm_mul_epu32(abcd, _mm_set1_epi32(10000))); // v1 = [ abcd, efgh, 0, 0, 0, 0, 0, 0 ] let v1 = _mm_unpacklo_epi16(abcd, efgh); // v1a = v1 * 4 = [ abcd*4, efgh*4, 0, 0, 0, 0, 0, 0 ] let v1a = _mm_slli_epi64(v1, 2); // v2 = [abcd*4, abcd*4, abcd*4, abcd*4, efgh*4, efgh*4, efgh*4, efgh*4] let v2a = _mm_unpacklo_epi16(v1a, v1a); let v2 = _mm_unpacklo_epi32(v2a, v2a); // v4 = v2 div 10^3, 10^2, 10^1, 10^0 = [ a, ab, abc, abcd, e, ef, efg, efgh ] let v3 = _mm_mulhi_epu16( v2, _mm_load_si128(kDivPowersVector.as_ptr() as *const __m128i), ); let v4 = _mm_mulhi_epu16( v3, _mm_load_si128(kShiftPowersVector.as_ptr() as *const __m128i), ); // v5 = v4 * 10 = [ a0, ab0, abc0, abcd0, e0, ef0, efg0, efgh0 ] let v5 = _mm_mullo_epi16(v4, _mm_set1_epi16(10)); // v6 = v5 << 16 = [ 0, a0, ab0, abc0, 0, e0, ef0, efg0 ] let v6 = _mm_slli_epi64(v5, 16); // v4 - v6 = { a, b, c, d, e, f, g, h } _mm_sub_epi16(v4, v6) } pub unsafe fn write_u32(n: u32, buf: *mut u8) -> usize { if n < 10000 { write4(n, buf) } else if n < 100_000_000 { let (n1, n2) = divmod(n, 10000); let l = write4(n1, buf); write4_pad(n2, buf.add(l)); l + 4 } else { let (n1, n2) = divmod(n, 100_000_000); let l = if n1 >= 10 { ptr::copy_nonoverlapping(lookup(n1), buf, 2); 2 } else { *buf = n1 as u8 + 0x30; 1 }; let b = convert_8digits_sse2(n2); let ba = _mm_add_epi8( _mm_packus_epi16(_mm_setzero_si128(), b), _mm_set1_epi8(b'0' as i8), ); let result = _mm_srli_si128(ba, 8); _mm_storel_epi64(buf.add(l) as *mut __m128i, result); l + 8 } } pub unsafe fn write_u64(n: u64, buf: *mut u8) -> usize { if n < 10000 { write4(n as u32, buf) } else if n < 100_000_000 { let (n1, n2) = divmod(n as u32, 10000); let l = write4(n1, buf); write4_pad(n2, buf.add(l)); l + 4 } else if n < 10_000_000_000_000_000 { let (n1, n2) = divmod(n, 100_000_000); let (n1, n2) = (n1 as u32, n2 as u32); let l = if n1 < 10000 { write4(n1, buf) } else { let (n11, n12) = divmod(n1, 10000); let l = write4(n11, buf); write4_pad(n12, buf.add(l)); l + 4 }; let b = convert_8digits_sse2(n2); let ba = _mm_add_epi8( _mm_packus_epi16(_mm_setzero_si128(), b), _mm_set1_epi8(b'0' as i8), ); let result = _mm_srli_si128(ba, 8); _mm_storel_epi64(buf.add(l) as *mut __m128i, result); l + 8 } else { let (n1, n2) = divmod(n, 10_000_000_000_000_000); let l = write4(n1 as u32, buf); let (n21, n22) = divmod(n2, 100_000_000); let a0 = convert_8digits_sse2(n21 as u32); let a1 = convert_8digits_sse2(n22 as u32); // Convert to bytes, add '0' let va = _mm_add_epi8(_mm_packus_epi16(a0, a1), _mm_set1_epi8(b'0' as i8)); _mm_storeu_si128(buf.add(l) as *mut __m128i, va); l + 16 } }