pax_global_header00006660000000000000000000000064133566737610014533gustar00rootroot0000000000000052 comment=c0f5649a2c7c75325c4eb0aa92fc43e1a103db96 okio-okio-parent-1.16.0/000077500000000000000000000000001335667376100150075ustar00rootroot00000000000000okio-okio-parent-1.16.0/.buildscript/000077500000000000000000000000001335667376100174115ustar00rootroot00000000000000okio-okio-parent-1.16.0/.buildscript/deploy_snapshot.sh000077500000000000000000000017541335667376100231720ustar00rootroot00000000000000#!/bin/bash # # Deploy a jar, source jar, and javadoc jar to Sonatype's snapshot repo. # # Adapted from https://coderwall.com/p/9b_lfq and # http://benlimmer.com/2013/12/26/automatically-publish-javadoc-to-gh-pages-with-travis-ci/ SLUG="square/okio" JDK="oraclejdk8" BRANCH="master" set -e if [ "$TRAVIS_REPO_SLUG" != "$SLUG" ]; then echo "Skipping snapshot deployment: wrong repository. Expected '$SLUG' but was '$TRAVIS_REPO_SLUG'." elif [ "$TRAVIS_JDK_VERSION" != "$JDK" ]; then echo "Skipping snapshot deployment: wrong JDK. Expected '$JDK' but was '$TRAVIS_JDK_VERSION'." elif [ "$TRAVIS_PULL_REQUEST" != "false" ]; then echo "Skipping snapshot deployment: was pull request." elif [ "$TRAVIS_BRANCH" != "$BRANCH" ]; then echo "Skipping snapshot deployment: wrong branch. Expected '$BRANCH' but was '$TRAVIS_BRANCH'." else echo "Deploying snapshot..." mvn clean source:jar javadoc:jar deploy --settings=".buildscript/settings.xml" -Dmaven.test.skip=true echo "Snapshot deployed!" fi okio-okio-parent-1.16.0/.buildscript/settings.xml000066400000000000000000000003351335667376100217740ustar00rootroot00000000000000 sonatype-nexus-snapshots ${env.CI_DEPLOY_USERNAME} ${env.CI_DEPLOY_PASSWORD} okio-okio-parent-1.16.0/.gitignore000066400000000000000000000002261335667376100167770ustar00rootroot00000000000000.classpath .project .settings eclipsebin bin gen build out lib target pom.xml.* release.properties .idea *.iml *.ipr *.iws classes obj .DS_Store okio-okio-parent-1.16.0/.travis.yml000066400000000000000000000012771335667376100171270ustar00rootroot00000000000000language: java jdk: - oraclejdk8 addons: apt: packages: - oracle-java8-installer # Updates JDK 8 to the latest available. after_success: - .buildscript/deploy_snapshot.sh env: global: - secure: "gpdzVacMwUxhoHU1Ettfowgx0axV/L12bjoR8O4iKbRskE3Wr8AgM2GXmFMjoMVDr7vy45YhtOatuSlSKkwZRfgNIcAcTv8axjaFFt7xnPozXXPTU+pkIfaw5DnHzCwJlOo29mmY767Hz4CLomJi8znqKl5VguPAqXo/I8BqKwc=" - secure: "YMh3xjsPik4U9DORSFzyTlGMTGc5kXMeuhyu0/757g7fhG4fgwcuCFMm0hBJTUtBu5dV8Ao3EDqZj/hC7muiy7MODHPd3C/Q3LXolZViPVvu6cqaYFN4NGZpZY/fnxF7tz59lODE+OhvomI0RJHS34MdUDscnCAdYWjdkgmH8Y4=" branches: except: - gh-pages notifications: email: false sudo: false cache: directories: - $HOME/.m2 okio-okio-parent-1.16.0/BUG-BOUNTY.md000066400000000000000000000005541335667376100167700ustar00rootroot00000000000000Serious about security ====================== Square recognizes the important contributions the security research community can make. We therefore encourage reporting security issues with the code contained in this repository. If you believe you have discovered a security vulnerability, please follow the guidelines at https://hackerone.com/square-open-source okio-okio-parent-1.16.0/CHANGELOG.md000066400000000000000000000257121335667376100166270ustar00rootroot00000000000000Change Log ========== ## Version 1.15.0 _2018-07-18_ * New: Trie-based `Buffer.select()`. This improves performance when selecting among large lists of options. * Fix: Retain interrupted state when throwing `InterruptedIOException`. ## Version 1.14.0 _2018-02-11_ * New: `Buffer.UnsafeCursor` provides direct access to Okio internals. This API is like Okio's version of Java reflection: it's a very powerful API that can be used for great things and dangerous things alike. The documentation is extensive and anyone using it should review it carefully before proceeding! * New: Change `BufferedSource` to implement `java.nio.ReadableByteChannel` and `BufferedSink` to implement `java.nio.WritableByteChannel`. Now it's a little easier to interop between Okio and NIO. * New: Automatic module name of `okio` for use with the Java Platform Module System. * New: Optimize `Buffer.getByte()` to search backwards when doing so will be more efficient. * Fix: Honor the requested byte count in `InflaterSource`. Previously this class could return more bytes than requested. * Fix: Improve a performance bug in `AsyncTimeout.sink().write()`. ## Version 1.13.0 _2017-05-12_ * **Okio now uses `@Nullable` to annotate all possibly-null values.** We've added a compile-time dependency on the JSR 305 annotations. This is a [provided][maven_provided] dependency and does not need to be included in your build configuration, `.jar` file, or `.apk`. We use `@ParametersAreNonnullByDefault` and all parameters and return types are never null unless explicitly annotated `@Nullable`. * **Warning: this release is source-incompatible for Kotlin users.** Nullability was previously ambiguous and lenient but now the compiler will enforce strict null checks. ## Version 1.12.0 _2017-04-11_ * **Fix: Change Pipe's sink.flush() to not block.** Previously closing a pipe's sink would block until the source had been exhausted. In practice this blocked the caller for no benefit. * **Fix: Change `writeUtf8CodePoint()` to emit `?` for partial surrogates.** The previous behavior was inconsistent: given a malformed string with a partial surrogate, `writeUtf8()` emitted `?` but `writeUtf8CodePoint()` threw an `IllegalArgumentException`. Most applications will never encounter partial surrogates, but for those that do this behavior was unexpected. * New: Allow length of `readUtf8LineStrict()` to be limited. * New: `Utf8.size()` method to get the number of bytes required to encode a string as UTF-8. This may be useful for length-prefixed encodings. * New: SHA-512 hash and HMAC APIs. ## Version 1.11.0 _2016-10-11_ * **Fix: The four-argument overload of `Buffer.writeString()` had a major bug where it didn't respect offsets if the specified charset was UTF-8.** This was because our short-circuit optimization omitted necessary offset parameters. * New: HMAC support in `HashingSource`, `HashingSink`, `ByteString`, and `Buffer`. This makes it easy to create a keyed-hash message authentication code (HMAC) wherever your data is. Unlike the other hashes, HMAC uses a `ByteString` secret key for authentication. * New: `ByteString.of(ByteBuffer)` makes it easier to mix NIO with Okio. ## Version 1.10.0 _2016-08-28_ * Fix: Support reading files larger than 2 GiB with `GzipSource`. Previously attempting to decompress such files would fail due to an overflow when validating the total length. * Fix: Exit the watchdog thread after being idle for 60 seconds. This should make it possible for class unloaders to fully unload Okio. * New: `Okio.blackhole()` returns a sink where all bytes written are discarded. This is Okio's equivalent of `/dev/null`. * New: Encode a string with any charset using `ByteString.encodeString()` and decode strings in any charset using `ByteString.string()`. Most applications should prefer `ByteString.encodeUtf8()` and `ByteString.utf8()` unless it's necessary to support a legacy charset. * New: `GzipSink.deflater()` makes it possible to configure the compression level. ## Version 1.9.0 _2016-07-01_ * New: `Pipe` makes it easy to connect a producer thread to a consumer thread. Reads block until data is available to read. Writes block if the pipe's is full. Both sources and sinks support timeouts. * New: `BufferedSource.rangeEquals()` makes it easy to compare a range in a stream to an expected value. This does the right thing: it blocks to load the data required return a definitive result. But it won't block unnecessarily. * New: `Timeout.waitUntilNotified()` makes it possible to use nice timeout abstractions on Java's built-in wait/notify primitives. * Fix: Don't return incorrect results when `HashingSource` does large reads. There was a bug where it wasn't traversing through the segments of the buffer being hashed. This means that `HashingSource` was returning incorrect answers for any writes that spanned multiple segment boundaries. ## Version 1.8.0 _2016-05-02_ * New: `BufferedSource.select(Options)` API for reading one of a set of expected values. * New: Make `ByteString.toString()` and `Buffer.toString()` friendlier. These methods return text if the byte string is valid UTF-8. * New: APIs to match byte strings: `indexOf()`, `startsWith()`, and `endsWith()`. ## Version 1.7.0 _2016-04-10_ * New: Change the segment size to 8 KiB. This has been reported to dramatically improve performance in some applications. * New: `md5()`, `sha1()`, and `sha256()` methods on `Buffer`. Also add a `sha1()` method on `ByteString` for symmetry. * New: `HashingSource` and `HashingSink`. These classes are Okio’s equivalent to the JDK’s `DigestInputStream` and `DigestOutputStream`. They offer convenient `md5()`, `sha1()`, and `sha256()` factory methods to avoid an impossible `NoSuchAlgorithmException`. * New: `ByteString.asByteBuffer()`. * Fix: Limit snapshot byte strings to requested size. * Fix: Change write timeouts to have a maximum write size. Previously large writes could easly suffer timeouts because the entire write was subject to a single timeout. * Fix: Recover from EBADF failures, which could be triggered by asynchronously closing a stream on older versions of Android. * Fix: Don't share segments if doing so only saves a small copy. This should improve performance for all applications. * Fix: Optimize `BufferedSource.indexOfElement()` and `indexOf(ByteString)`. Previously this method had a bug that caused it to be very slow on large buffers. ## Version 1.6.0 _2015-08-25_ * New: `BufferedSource.indexOf(ByteString)` searches a source for the next occurrence of a byte string. * Fix: Recover from unexpected `AssertionError` thrown on Android 4.2.2 and earlier when asynchronously closing a socket. ## Version 1.5.0 _2015-06-19_ * Sockets streams now throw `SocketTimeoutException`. This builds on new extension point in `AsyncTimeout` to customize the exception when a timeout occurs. * New: `ByteString` now implements `Comparable`. The comparison sorts bytes as unsigned: {@code ff} sorts after {@code 00}. ## Version 1.4.0 _2015-05-16_ * **Timeout exception changed.** Previously `Timeout.throwIfReached()` would throw `InterruptedIOException` on thread interruption, and `IOException` if the deadline was reached. Now it throws `InterruptedIOException` in both cases. * Fix: throw `EOFException` when attempting to read digits from an empty source. Previously this would crash with an unchecked exception. * New: APIs to read and write UTF-8 code points without allocating strings. * New: `BufferedSink` can now write substrings directly, potentially saving an allocation for some callers. * New: `ForwardingTimeout` class. ## Version 1.3.0 _2015-03-16_ * New: Read and write signed decimal and unsigned hexadecimal values in `BufferedSource` and `BufferedSink`. Unlike the alternatives, these methods don’t do any memory allocations! * New: Segment sharing. This improves the runtime of operations like `Buffer.clone()` and `Buffer.copyTo()` by sharing underlying segments between buffers. * New: `Buffer.snapshot()` returns an immutable snapshot of a buffer as a `ByteString`. This builds on segment sharing so that snapshots are shallow, immutable copies. * New: `ByteString.rangeEquals()`. * New: `ByteString.md5()` and `ByteString.sha256()`. * New: `ByteString.base64Url()` returns URL-safe Base64. The existing decoding method has been extended to support URL-safe Base64 input. * New: `ByteString.substring()` returns a prefix, infix, or suffix. * New: `Sink` now implements `java.io.Flushable`. * Fix: `Buffer.write(Source, long)` now always writes fully. The previous behavior would return as soon as any data had been written; this was inconsistent with all other _write()_ methods in the API. * Fix: don't leak empty segments in DeflaterSink and InflaterSource. (This was unlikely to cause problems in practice.) ## Version 1.2.0 _2014-12-30_ * Fix: `Okio.buffer()` _always_ buffers for better predictability. * Fix: Provide context when `readUtf8LineStrict()` throws. * Fix: Buffers do not call through the `Source` on zero-byte writes. ## Version 1.1.0 _2014-12-11_ * Do UTF-8 encoding natively for a performance increase, particularly on Android. * New APIs: `BufferedSink.emit()`, `BufferedSource.request()` and `BufferedSink.indexOfElement()`. * Fixed a performance bug in `Buffer.indexOf()` ## Version 1.0.1 _2014-08-08_ * Added `read(byte[])`, `read(byte[], offset, byteCount)`, and `void readFully(byte[])` to `BufferedSource`. * Refined declared checked exceptions on `Buffer` methods. ## Version 1.0.0 _2014-05-23_ * Bumped release version. No other changes! ## Version 0.9.0 _2014-05-03_ * Use 0 as a sentinel for no timeout. * Make AsyncTimeout public. * Remove checked exception from Buffer.readByteArray. ## Version 0.8.0 _2014-04-24_ * Eagerly verify preconditions on public APIs. * Quick return on Buffer instance equivalence. * Add delegate types for Sink and Source. * Small changes to the way deadlines are managed. * Add append variant of Okio.sink for File. * Methods to exhaust BufferedSource to byte[] and ByteString. ## Version 0.7.0 _2014-04-18_ * Don't use getters in timeout. * Use the watchdog to interrupt sockets that have reached deadlines. * Add java.io and java.nio file source/sink helpers. ## Version 0.6.1 _2014-04-17_ * Methods to read a buffered source fully in UTF-8 or supplied charset. * API to read a byte[] directly. * New methods to move all data from a source to a sink. * Fix a bug on input stream exhaustion. ## Version 0.6.0 _2014-04-15_ * Make ByteString serializable. * New API: `ByteString.of(byte[] data, int offset, int byteCount)` * New API: stream-based copy, write, and read helpers. ## Version 0.5.0 _2014-04-08_ * Initial public release. * Imported from OkHttp. [maven_provided]: https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html okio-okio-parent-1.16.0/CONTRIBUTING.md000066400000000000000000000013321335667376100172370ustar00rootroot00000000000000Contributing ============ If you would like to contribute code to Okio you can do so through GitHub by forking the repository and sending a pull request. When submitting code, please make every effort to follow existing conventions and style in order to keep the code as readable as possible. Please also make sure your code compiles by running `mvn clean verify`. Checkstyle failures during compilation indicate errors in your style and can be viewed in the `checkstyle-result.xml` file. Before your code can be accepted into the project you must also sign the [Individual Contributor License Agreement (CLA)][1]. [1]: https://spreadsheets.google.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ&ndplr=1 okio-okio-parent-1.16.0/LICENSE.txt000066400000000000000000000261361335667376100166420ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. okio-okio-parent-1.16.0/README.md000066400000000000000000000135661335667376100163010ustar00rootroot00000000000000Okio ==== Okio is a library that complements `java.io` and `java.nio` to make it much easier to access, store, and process your data. ByteStrings and Buffers ----------------------- Okio is built around two types that pack a lot of capability into a straightforward API: * [**ByteString**][3] is an immutable sequence of bytes. For character data, `String` is fundamental. `ByteString` is String's long-lost brother, making it easy to treat binary data as a value. This class is ergonomic: it knows how to encode and decode itself as hex, base64, and UTF-8. * [**Buffer**][4] is a mutable sequence of bytes. Like `ArrayList`, you don't need to size your buffer in advance. You read and write buffers as a queue: write data to the end and read it from the front. There's no obligation to manage positions, limits, or capacities. Internally, `ByteString` and `Buffer` do some clever things to save CPU and memory. If you encode a UTF-8 string as a `ByteString`, it caches a reference to that string so that if you decode it later, there's no work to do. `Buffer` is implemented as a linked list of segments. When you move data from one buffer to another, it _reassigns ownership_ of the segments rather than copying the data across. This approach is particularly helpful for multithreaded programs: a thread that talks to the network can exchange data with a worker thread without any copying or ceremony. Sources and Sinks ----------------- An elegant part of the `java.io` design is how streams can be layered for transformations like encryption and compression. Okio includes its own stream types called [`Source`][5] and [`Sink`][6] that work like `InputStream` and `OutputStream`, but with some key differences: * **Timeouts.** The streams provide access to the timeouts of the underlying I/O mechanism. Unlike the `java.io` socket streams, both `read()` and `write()` calls honor timeouts. * **Easy to implement.** `Source` declares three methods: `read()`, `close()`, and `timeout()`. There are no hazards like `available()` or single-byte reads that cause correctness and performance surprises. * **Easy to use.** Although _implementations_ of `Source` and `Sink` have only three methods to write, _callers_ are given a rich API with the [`BufferedSource`][7] and [`BufferedSink`][8] interfaces. These interfaces give you everything you need in one place. * **No artificial distinction between byte streams and char streams.** It's all data. Read and write it as bytes, UTF-8 strings, big-endian 32-bit integers, little-endian shorts; whatever you want. No more `InputStreamReader`! * **Easy to test.** The `Buffer` class implements both `BufferedSource` and `BufferedSink` so your test code is simple and clear. Sources and sinks interoperate with `InputStream` and `OutputStream`. You can view any `Source` as an `InputStream`, and you can view any `InputStream` as a `Source`. Similarly for `Sink` and `OutputStream`. Dependable ---------- Okio started as a component of [OkHttp][1], the capable HTTP+SPDY client included in Android. It's well-exercised and ready to solve new problems. Example: a PNG decoder ---------------------- Decoding the chunks of a PNG file demonstrates Okio in practice. ```java private static final ByteString PNG_HEADER = ByteString.decodeHex("89504e470d0a1a0a"); public void decodePng(InputStream in) throws IOException { try (BufferedSource pngSource = Okio.buffer(Okio.source(in))) { ByteString header = pngSource.readByteString(PNG_HEADER.size()); if (!header.equals(PNG_HEADER)) { throw new IOException("Not a PNG."); } while (true) { Buffer chunk = new Buffer(); // Each chunk is a length, type, data, and CRC offset. int length = pngSource.readInt(); String type = pngSource.readUtf8(4); pngSource.readFully(chunk, length); int crc = pngSource.readInt(); decodeChunk(type, chunk); if (type.equals("IEND")) break; } } } private void decodeChunk(String type, Buffer chunk) { if (type.equals("IHDR")) { int width = chunk.readInt(); int height = chunk.readInt(); System.out.printf("%08x: %s %d x %d%n", chunk.size(), type, width, height); } else { System.out.printf("%08x: %s%n", chunk.size(), type); } } ``` Download -------- Download [the latest JAR][2] or grab via Maven: ```xml com.squareup.okio okio 1.15.0 ``` or Gradle: ```groovy compile 'com.squareup.okio:okio:1.15.0' ``` Snapshots of the development version are available in [Sonatype's `snapshots` repository][snap]. ProGuard -------- If you are using ProGuard you might need to add the following option: ``` -dontwarn okio.** ``` License -------- Copyright 2013 Square, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. [1]: https://github.com/square/okhttp [2]: https://search.maven.org/remote_content?g=com.squareup.okio&a=okio&v=LATEST [3]: http://square.github.io/okio/1.x/okio/okio/ByteString.html [4]: http://square.github.io/okio/1.x/okio/okio/Buffer.html [5]: http://square.github.io/okio/1.x/okio/okio/Source.html [6]: http://square.github.io/okio/1.x/okio/okio/Sink.html [7]: http://square.github.io/okio/1.x/okio/okio/BufferedSource.html [8]: http://square.github.io/okio/1.x/okio/okio/BufferedSink.html [snap]: https://oss.sonatype.org/content/repositories/snapshots/ okio-okio-parent-1.16.0/benchmarks/000077500000000000000000000000001335667376100171245ustar00rootroot00000000000000okio-okio-parent-1.16.0/benchmarks/README.md000066400000000000000000000030601335667376100204020ustar00rootroot00000000000000Okio Benchmarks ------------ This module contains microbenchmarks that can be used to measure various aspects of performance for Okio buffers. Okio benchmarks are written using JMH (version 1.4.1 at this time) and require Java 7. Running Locally ------------- To run benchmarks locally, first build and package the project modules: ``` $ mvn clean package ``` This should create a `benchmarks.jar` file in the `target` directory, which is a typical JMH benchmark JAR: ``` $ java -jar benchmarks/target/benchmarks.jar -l Benchmarks: com.squareup.okio.benchmarks.BufferPerformanceBench.cold com.squareup.okio.benchmarks.BufferPerformanceBench.threads16hot com.squareup.okio.benchmarks.BufferPerformanceBench.threads1hot com.squareup.okio.benchmarks.BufferPerformanceBench.threads2hot com.squareup.okio.benchmarks.BufferPerformanceBench.threads32hot com.squareup.okio.benchmarks.BufferPerformanceBench.threads4hot com.squareup.okio.benchmarks.BufferPerformanceBench.threads8hot ``` More help is available using the `-h` option. A typical run on Mac OS X looks like: ``` $ /usr/libexec/java_home -v 1.7 --exec java -jar benchmarks/target/benchmarks.jar \ "cold" -prof gc,hs_rt,stack -r 60 -t 4 \ -jvmArgsPrepend "-Xms1G -Xmx1G -XX:+HeapDumpOnOutOfMemoryError" ``` This executes the "cold" buffer usage benchmark, using the default number of measurement and warm-up iterations, forks, and threads; it adjusts the thread count to 4, iteration time to 60 seconds, fixes the heap size at 1GB and profiles the benchmark using JMH's GC, Hotspot runtime and stack sampling profilers. okio-okio-parent-1.16.0/benchmarks/pom.xml000066400000000000000000000050101335667376100204350ustar00rootroot00000000000000 okio-parent com.squareup.okio 1.16.0 4.0.0 benchmarks jar Okio Performance Benchmarks org.openjdk.jmh jmh-core org.openjdk.jmh jmh-generator-annprocess provided com.squareup.okio okio ${project.version} benchmarks org.apache.maven.plugins maven-compiler-plugin org.apache.maven.plugins maven-shade-plugin 2.2 package shade ${uberjar.name} org.openjdk.jmh.Main *:* META-INF/*.SF META-INF/*.DSA META-INF/*.RSA okio-okio-parent-1.16.0/benchmarks/src/000077500000000000000000000000001335667376100177135ustar00rootroot00000000000000okio-okio-parent-1.16.0/benchmarks/src/main/000077500000000000000000000000001335667376100206375ustar00rootroot00000000000000okio-okio-parent-1.16.0/benchmarks/src/main/java/000077500000000000000000000000001335667376100215605ustar00rootroot00000000000000okio-okio-parent-1.16.0/benchmarks/src/main/java/com/000077500000000000000000000000001335667376100223365ustar00rootroot00000000000000okio-okio-parent-1.16.0/benchmarks/src/main/java/com/squareup/000077500000000000000000000000001335667376100242035ustar00rootroot00000000000000okio-okio-parent-1.16.0/benchmarks/src/main/java/com/squareup/okio/000077500000000000000000000000001335667376100251445ustar00rootroot00000000000000okio-okio-parent-1.16.0/benchmarks/src/main/java/com/squareup/okio/benchmarks/000077500000000000000000000000001335667376100272615ustar00rootroot00000000000000BufferCursorSeekBenchmark.java000066400000000000000000000053011335667376100350760ustar00rootroot00000000000000okio-okio-parent-1.16.0/benchmarks/src/main/java/com/squareup/okio/benchmarks/* * Copyright (C) 2018 Square, Inc. and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.squareup.okio.benchmarks; import java.io.IOException; import java.util.concurrent.TimeUnit; import okio.Buffer; import org.openjdk.jmh.Main; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.runner.RunnerException; @Fork(1) @Warmup(iterations = 5, time = 2) @Measurement(iterations = 5, time = 2) @State(Scope.Benchmark) @BenchmarkMode(Mode.SampleTime) @OutputTimeUnit(TimeUnit.MICROSECONDS) public class BufferCursorSeekBenchmark { Buffer buffer; Buffer.UnsafeCursor cursor; @Param({ "2097152" }) int bufferSize; // 2 MB = 256 Segments @Setup public void setup() throws IOException { byte[] source = new byte[8192]; buffer = new Buffer(); while (buffer.size() < bufferSize) { buffer.write(source); } cursor = new Buffer.UnsafeCursor(); } @Benchmark public void seekBeginning() { buffer.readUnsafe(cursor); try { cursor.seek(0); } finally { cursor.close(); } } @Benchmark public void seekEnd() { buffer.readUnsafe(cursor); try { cursor.seek(buffer.size() - 1); } finally { cursor.close(); } } @Benchmark public void seekForward() { buffer.readUnsafe(cursor); try { cursor.seek(0); cursor.seek(1); } finally { cursor.close(); } } @Benchmark public void seekBackward() { buffer.readUnsafe(cursor); try { cursor.seek(buffer.size() - 1); cursor.seek(buffer.size() - 2); } finally { cursor.close(); } } public static void main(String[] args) throws IOException, RunnerException { Main.main(new String[] { BufferCursorSeekBenchmark.class.getName() }); } } BufferPerformanceBenchmark.java000066400000000000000000000235431335667376100352620ustar00rootroot00000000000000okio-okio-parent-1.16.0/benchmarks/src/main/java/com/squareup/okio/benchmarks/* * Copyright (C) 2014 Square, Inc. and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.squareup.okio.benchmarks; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.TimeUnit; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Group; import org.openjdk.jmh.annotations.GroupThreads; import org.openjdk.jmh.annotations.Level; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.annotations.Threads; import org.openjdk.jmh.annotations.Warmup; import okio.Buffer; import okio.BufferedSource; import okio.Okio; import okio.Sink; import okio.Timeout; import static java.util.Objects.requireNonNull; @Fork(1) @Warmup(iterations = 10, time = 10) @Measurement(iterations = 10, time = 10) @State(Scope.Benchmark) @BenchmarkMode(Mode.Throughput) @OutputTimeUnit(TimeUnit.SECONDS) public class BufferPerformanceBenchmark { public static final File OriginPath = new File(System.getProperty("okio.bench.origin.path", "/dev/urandom")); /* Test Workload * * Each benchmark thread maintains three buffers; a receive buffer, a process buffer * and a send buffer. At every operation: * * - We fill up the receive buffer using the origin, write the request to the process * buffer, and consume the process buffer. * - We fill up the process buffer using the origin, write the response to the send * buffer, and consume the send buffer. * * We use an "origin" source that serves as a preexisting sequence of bytes we can read * from the file system. The request and response bytes are initialized in the beginning * and reused throughout the benchmark in order to eliminate GC effects. * * Typically, we simulate the usage of small reads and large writes. Requests and * responses are satisfied with precomputed buffers to eliminate GC effects on * results. * * There are two types of benchmark tests; hot tests are "pedal to the metal" and * use all CPU they can take. These are useful to magnify performance effects of * changes but are not realistic use cases that should drive optimization efforts. * Cold tests introduce think time between the receiving of the request and sending * of the response. They are more useful as a reasonably realistic workload where * buffers can be read from and written to during request/response handling but * may hide subtle effects of most changes on performance. Prefer to look at the cold * benchmarks first to decide if a bottleneck is worth pursuing, then use the hot * benchmarks to fine tune optimization efforts. * * Benchmark threads do not explicitly communicate between each other (except to sync * iterations as needed by JMH). * * We simulate think time for each benchmark thread by parking the thread for a * configurable number of microseconds (1000 by default). */ @Benchmark @Threads(1) public void threads1hot(HotBuffers buffers) throws IOException { readWriteRecycle(buffers); } @Benchmark @Threads(2) public void threads2hot(HotBuffers buffers) throws IOException { readWriteRecycle(buffers); } @Benchmark @Threads(4) public void threads4hot(HotBuffers buffers) throws IOException { readWriteRecycle(buffers); } @Benchmark @Threads(8) public void threads8hot(HotBuffers buffers) throws IOException { readWriteRecycle(buffers); } @Benchmark @Threads(16) public void threads16hot(HotBuffers buffers) throws IOException { readWriteRecycle(buffers); } @Benchmark @Threads(32) public void threads32hot(HotBuffers buffers) throws IOException { readWriteRecycle(buffers); } @Benchmark @GroupThreads(1) @Group("cold") public void thinkReadHot(HotBuffers buffers) throws IOException { buffers.receive(requestBytes).readAll(NullSink); } @Benchmark @GroupThreads(3) @Group("cold") public void thinkWriteCold(ColdBuffers buffers) throws IOException { buffers.transmit(responseBytes).readAll(NullSink); } private void readWriteRecycle(HotBuffers buffers) throws IOException { buffers.receive(requestBytes).readAll(NullSink); buffers.transmit(responseBytes).readAll(NullSink); } @Param({ "1000" }) int maxThinkMicros = 1000; @Param({ "1024" }) int maxReadBytes = 1024; @Param({ "1024" }) int maxWriteBytes = 1024; @Param({ "2048" }) int requestSize = 2048; @Param({ "1" }) int responseFactor = 1; byte[] requestBytes; byte[] responseBytes; @Setup(Level.Trial) public void storeRequestResponseData() throws IOException { checkOrigin(OriginPath); requestBytes = storeSourceData(new byte[requestSize]); responseBytes = storeSourceData(new byte[requestSize * responseFactor]); } private byte[] storeSourceData(byte[] dest) throws IOException { requireNonNull(dest, "dest == null"); try (BufferedSource source = Okio.buffer(Okio.source(OriginPath))) { source.readFully(dest); } return dest; } private void checkOrigin(File path) throws IOException { requireNonNull(path, "path == null"); if (!path.canRead()) { throw new IllegalArgumentException("can not access: " + path); } try (InputStream in = new FileInputStream(path)) { int available = in.read(); if (available < 0) { throw new IllegalArgumentException("can not read: " + path); } } } /* * The state class hierarchy is larger than it needs to be due to a JMH * issue where states inheriting setup methods depending on another state * do not get initialized correctly from benchmark methods making use * of groups. To work around, we leave the common setup and teardown code * in superclasses and move the setup method depending on the bench state * to subclasses. Without the workaround, it would have been enough for * `ColdBuffers` to inherit from `HotBuffers`. */ @State(Scope.Thread) public static class ColdBuffers extends BufferSetup { @Setup(Level.Trial) public void setupBench(BufferPerformanceBenchmark bench) { super.bench = bench; } @Setup(Level.Invocation) public void lag() throws InterruptedException { TimeUnit.MICROSECONDS.sleep(bench.maxThinkMicros); } } @State(Scope.Thread) public static class HotBuffers extends BufferSetup { @Setup(Level.Trial) public void setupBench(BufferPerformanceBenchmark bench) { super.bench = bench; } } @State(Scope.Thread) public abstract static class BufferSetup extends BufferState { BufferPerformanceBenchmark bench; public BufferedSource receive(byte[] bytes) throws IOException { return super.receive(bytes, bench.maxReadBytes); } public BufferedSource transmit(byte[] bytes) throws IOException { return super.transmit(bytes, bench.maxWriteBytes); } @TearDown public void dispose() throws IOException { releaseBuffers(); } } public static class BufferState { @SuppressWarnings("resource") final Buffer received = new Buffer(); @SuppressWarnings("resource") final Buffer sent = new Buffer(); @SuppressWarnings("resource") final Buffer process = new Buffer(); public void releaseBuffers() throws IOException { received.clear(); sent.clear(); process.clear(); } /** * Fills up the receive buffer, hands off to process buffer and returns it for consuming. * Expects receive and process buffers to be empty. Leaves the receive buffer empty and * process buffer full. */ protected Buffer receive(byte[] bytes, int maxChunkSize) throws IOException { writeChunked(received, bytes, maxChunkSize).readAll(process); return process; } /** * Fills up the process buffer, hands off to send buffer and returns it for consuming. * Expects process and sent buffers to be empty. Leaves the process buffer empty and * sent buffer full. */ protected BufferedSource transmit(byte[] bytes, int maxChunkSize) throws IOException { writeChunked(process, bytes, maxChunkSize).readAll(sent); return sent; } private BufferedSource writeChunked(Buffer buffer, byte[] bytes, final int chunkSize) { int remaining = bytes.length; int offset = 0; while (remaining > 0) { int bytesToWrite = Math.min(remaining, chunkSize); buffer.write(bytes, offset, bytesToWrite); remaining -= bytesToWrite; offset += bytesToWrite; } return buffer; } } @SuppressWarnings("resource") private static final Sink NullSink = new Sink() { @Override public void write(Buffer source, long byteCount) throws EOFException { source.skip(byteCount); } @Override public void flush() { // nothing } @Override public Timeout timeout() { return Timeout.NONE; } @Override public void close() { // nothing } @Override public String toString() { return "NullSink{}"; } }; } okio-okio-parent-1.16.0/benchmarks/src/main/java/com/squareup/okio/benchmarks/GetByteBenchmark.java000066400000000000000000000042011335667376100332770ustar00rootroot00000000000000/* * Copyright (C) 2018 Square, Inc. and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.squareup.okio.benchmarks; import java.io.IOException; import java.util.concurrent.TimeUnit; import okio.Buffer; import org.openjdk.jmh.Main; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.runner.RunnerException; @Fork(1) @Warmup(iterations = 5, time = 2) @Measurement(iterations = 5, time = 2) @State(Scope.Benchmark) @BenchmarkMode(Mode.SampleTime) @OutputTimeUnit(TimeUnit.MICROSECONDS) public class GetByteBenchmark { Buffer buffer; @Param({ "2097152" }) int bufferSize; // 2 MB = 256 Segments @Setup public void setup() throws IOException { buffer = new Buffer(); while (buffer.size() < bufferSize) { buffer.write(new byte[8192]); } } @Benchmark public void getByteBeginning() { buffer.getByte(0); } @Benchmark public void getByteEnd() { buffer.getByte(buffer.size() - 1); } @Benchmark public void getByteMiddle() { buffer.getByte(buffer.size() / 2); } public static void main(String[] args) throws IOException, RunnerException { Main.main(new String[] { GetByteBenchmark.class.getName() }); } } IndexOfElementBenchmark.java000066400000000000000000000045011335667376100345260ustar00rootroot00000000000000okio-okio-parent-1.16.0/benchmarks/src/main/java/com/squareup/okio/benchmarks/* * Copyright (C) 2016 Square, Inc. and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.squareup.okio.benchmarks; import java.io.IOException; import java.util.concurrent.TimeUnit; import okio.Buffer; import okio.ByteString; import org.openjdk.jmh.Main; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.runner.RunnerException; @Fork(1) @Warmup(iterations = 5, time = 2) @Measurement(iterations = 5, time = 2) @State(Scope.Benchmark) @BenchmarkMode(Mode.SampleTime) @OutputTimeUnit(TimeUnit.MICROSECONDS) public class IndexOfElementBenchmark { ByteString byteString = ByteString.encodeUtf8("abcd"); Buffer buffer; @Param({ "32768" }) int bufferSize; @Setup public void setup() throws IOException { buffer = new Buffer() .write(new byte[bufferSize / 2]) .write(byteString) .write(new byte[(bufferSize / 2) - byteString.size()]); } @Benchmark public void indexOfByte() throws IOException { buffer.indexOf((byte) 'b', 0L); } @Benchmark public void indexOfByteString() throws IOException { buffer.indexOf(byteString, 0L); } @Benchmark public void indexOfElement() throws IOException { buffer.indexOfElement(byteString, 0L); } public static void main(String[] args) throws IOException, RunnerException { Main.main(new String[] { IndexOfElementBenchmark.class.getName() }); } } okio-okio-parent-1.16.0/checkstyle.xml000066400000000000000000000131161335667376100176710ustar00rootroot00000000000000 okio-okio-parent-1.16.0/deploy_javadoc.sh000077500000000000000000000014161335667376100203330ustar00rootroot00000000000000#!/bin/bash set -ex REPO="git@github.com:square/okio.git" GROUP_ID="com.squareup.okio" ARTIFACT_ID="okio" DIR=temp-clone # Delete any existing temporary website clone rm -rf $DIR # Clone the current repo into temp folder git clone $REPO $DIR # Move working directory into temp folder cd $DIR # Checkout and track the gh-pages branch git checkout -t origin/gh-pages # Delete everything rm -rf * # Download the latest javadoc curl -L "https://search.maven.org/remote_content?g=$GROUP_ID&a=$ARTIFACT_ID&v=LATEST&c=javadoc" > javadoc.zip unzip javadoc.zip rm javadoc.zip # Stage all files in git and create a commit git add . git add -u git commit -m "Website at $(date)" # Push the new files up to GitHub git push origin gh-pages # Delete our temp folder cd .. rm -rf $DIR okio-okio-parent-1.16.0/okio/000077500000000000000000000000001335667376100157505ustar00rootroot00000000000000okio-okio-parent-1.16.0/okio/pom.xml000066400000000000000000000036761335667376100173010ustar00rootroot00000000000000 4.0.0 com.squareup.okio okio-parent 1.16.0 okio Okio com.google.code.findbugs jsr305 provided org.codehaus.mojo animal-sniffer-annotations true junit junit test org.apache.maven.plugins maven-jar-plugin okio org.codehaus.mojo animal-sniffer-maven-plugin ${animal.sniffer.version} test check org.codehaus.mojo.signature java16 1.1 okio-okio-parent-1.16.0/okio/src/000077500000000000000000000000001335667376100165375ustar00rootroot00000000000000okio-okio-parent-1.16.0/okio/src/main/000077500000000000000000000000001335667376100174635ustar00rootroot00000000000000okio-okio-parent-1.16.0/okio/src/main/java/000077500000000000000000000000001335667376100204045ustar00rootroot00000000000000okio-okio-parent-1.16.0/okio/src/main/java/okio/000077500000000000000000000000001335667376100213455ustar00rootroot00000000000000okio-okio-parent-1.16.0/okio/src/main/java/okio/AsyncTimeout.java000066400000000000000000000311071335667376100246360ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; import java.io.InterruptedIOException; import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; import static okio.Util.checkOffsetAndCount; /** * This timeout uses a background thread to take action exactly when the timeout occurs. Use this to * implement timeouts where they aren't supported natively, such as to sockets that are blocked on * writing. * *

Subclasses should override {@link #timedOut} to take action when a timeout occurs. This method * will be invoked by the shared watchdog thread so it should not do any long-running operations. * Otherwise we risk starving other timeouts from being triggered. * *

Use {@link #sink} and {@link #source} to apply this timeout to a stream. The returned value * will apply the timeout to each operation on the wrapped stream. * *

Callers should call {@link #enter} before doing work that is subject to timeouts, and {@link * #exit} afterwards. The return value of {@link #exit} indicates whether a timeout was triggered. * Note that the call to {@link #timedOut} is asynchronous, and may be called after {@link #exit}. */ public class AsyncTimeout extends Timeout { /** * Don't write more than 64 KiB of data at a time, give or take a segment. Otherwise slow * connections may suffer timeouts even when they're making (slow) progress. Without this, writing * a single 1 MiB buffer may never succeed on a sufficiently slow connection. */ private static final int TIMEOUT_WRITE_SIZE = 64 * 1024; /** Duration for the watchdog thread to be idle before it shuts itself down. */ private static final long IDLE_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(60); private static final long IDLE_TIMEOUT_NANOS = TimeUnit.MILLISECONDS.toNanos(IDLE_TIMEOUT_MILLIS); /** * The watchdog thread processes a linked list of pending timeouts, sorted in the order to be * triggered. This class synchronizes on AsyncTimeout.class. This lock guards the queue. * *

Head's 'next' points to the first element of the linked list. The first element is the next * node to time out, or null if the queue is empty. The head is null until the watchdog thread is * started and also after being idle for {@link #IDLE_TIMEOUT_MILLIS}. */ static @Nullable AsyncTimeout head; /** True if this node is currently in the queue. */ private boolean inQueue; /** The next node in the linked list. */ private @Nullable AsyncTimeout next; /** If scheduled, this is the time that the watchdog should time this out. */ private long timeoutAt; public final void enter() { if (inQueue) throw new IllegalStateException("Unbalanced enter/exit"); long timeoutNanos = timeoutNanos(); boolean hasDeadline = hasDeadline(); if (timeoutNanos == 0 && !hasDeadline) { return; // No timeout and no deadline? Don't bother with the queue. } inQueue = true; scheduleTimeout(this, timeoutNanos, hasDeadline); } private static synchronized void scheduleTimeout( AsyncTimeout node, long timeoutNanos, boolean hasDeadline) { // Start the watchdog thread and create the head node when the first timeout is scheduled. if (head == null) { head = new AsyncTimeout(); new Watchdog().start(); } long now = System.nanoTime(); if (timeoutNanos != 0 && hasDeadline) { // Compute the earliest event; either timeout or deadline. Because nanoTime can wrap around, // Math.min() is undefined for absolute values, but meaningful for relative ones. node.timeoutAt = now + Math.min(timeoutNanos, node.deadlineNanoTime() - now); } else if (timeoutNanos != 0) { node.timeoutAt = now + timeoutNanos; } else if (hasDeadline) { node.timeoutAt = node.deadlineNanoTime(); } else { throw new AssertionError(); } // Insert the node in sorted order. long remainingNanos = node.remainingNanos(now); for (AsyncTimeout prev = head; true; prev = prev.next) { if (prev.next == null || remainingNanos < prev.next.remainingNanos(now)) { node.next = prev.next; prev.next = node; if (prev == head) { AsyncTimeout.class.notify(); // Wake up the watchdog when inserting at the front. } break; } } } /** Returns true if the timeout occurred. */ public final boolean exit() { if (!inQueue) return false; inQueue = false; return cancelScheduledTimeout(this); } /** Returns true if the timeout occurred. */ private static synchronized boolean cancelScheduledTimeout(AsyncTimeout node) { // Remove the node from the linked list. for (AsyncTimeout prev = head; prev != null; prev = prev.next) { if (prev.next == node) { prev.next = node.next; node.next = null; return false; } } // The node wasn't found in the linked list: it must have timed out! return true; } /** * Returns the amount of time left until the time out. This will be negative if the timeout has * elapsed and the timeout should occur immediately. */ private long remainingNanos(long now) { return timeoutAt - now; } /** * Invoked by the watchdog thread when the time between calls to {@link #enter()} and {@link * #exit()} has exceeded the timeout. */ protected void timedOut() { } /** * Returns a new sink that delegates to {@code sink}, using this to implement timeouts. This works * best if {@link #timedOut} is overridden to interrupt {@code sink}'s current operation. */ public final Sink sink(final Sink sink) { return new Sink() { @Override public void write(Buffer source, long byteCount) throws IOException { checkOffsetAndCount(source.size, 0, byteCount); while (byteCount > 0L) { // Count how many bytes to write. This loop guarantees we split on a segment boundary. long toWrite = 0L; for (Segment s = source.head; toWrite < TIMEOUT_WRITE_SIZE; s = s.next) { int segmentSize = s.limit - s.pos; toWrite += segmentSize; if (toWrite >= byteCount) { toWrite = byteCount; break; } } // Emit one write. Only this section is subject to the timeout. boolean throwOnTimeout = false; enter(); try { sink.write(source, toWrite); byteCount -= toWrite; throwOnTimeout = true; } catch (IOException e) { throw exit(e); } finally { exit(throwOnTimeout); } } } @Override public void flush() throws IOException { boolean throwOnTimeout = false; enter(); try { sink.flush(); throwOnTimeout = true; } catch (IOException e) { throw exit(e); } finally { exit(throwOnTimeout); } } @Override public void close() throws IOException { boolean throwOnTimeout = false; enter(); try { sink.close(); throwOnTimeout = true; } catch (IOException e) { throw exit(e); } finally { exit(throwOnTimeout); } } @Override public Timeout timeout() { return AsyncTimeout.this; } @Override public String toString() { return "AsyncTimeout.sink(" + sink + ")"; } }; } /** * Returns a new source that delegates to {@code source}, using this to implement timeouts. This * works best if {@link #timedOut} is overridden to interrupt {@code sink}'s current operation. */ public final Source source(final Source source) { return new Source() { @Override public long read(Buffer sink, long byteCount) throws IOException { boolean throwOnTimeout = false; enter(); try { long result = source.read(sink, byteCount); throwOnTimeout = true; return result; } catch (IOException e) { throw exit(e); } finally { exit(throwOnTimeout); } } @Override public void close() throws IOException { boolean throwOnTimeout = false; enter(); try { source.close(); throwOnTimeout = true; } catch (IOException e) { throw exit(e); } finally { exit(throwOnTimeout); } } @Override public Timeout timeout() { return AsyncTimeout.this; } @Override public String toString() { return "AsyncTimeout.source(" + source + ")"; } }; } /** * Throws an IOException if {@code throwOnTimeout} is {@code true} and a timeout occurred. See * {@link #newTimeoutException(java.io.IOException)} for the type of exception thrown. */ final void exit(boolean throwOnTimeout) throws IOException { boolean timedOut = exit(); if (timedOut && throwOnTimeout) throw newTimeoutException(null); } /** * Returns either {@code cause} or an IOException that's caused by {@code cause} if a timeout * occurred. See {@link #newTimeoutException(java.io.IOException)} for the type of exception * returned. */ final IOException exit(IOException cause) throws IOException { if (!exit()) return cause; return newTimeoutException(cause); } /** * Returns an {@link IOException} to represent a timeout. By default this method returns {@link * java.io.InterruptedIOException}. If {@code cause} is non-null it is set as the cause of the * returned exception. */ protected IOException newTimeoutException(@Nullable IOException cause) { InterruptedIOException e = new InterruptedIOException("timeout"); if (cause != null) { e.initCause(cause); } return e; } private static final class Watchdog extends Thread { Watchdog() { super("Okio Watchdog"); setDaemon(true); } public void run() { while (true) { try { AsyncTimeout timedOut; synchronized (AsyncTimeout.class) { timedOut = awaitTimeout(); // Didn't find a node to interrupt. Try again. if (timedOut == null) continue; // The queue is completely empty. Let this thread exit and let another watchdog thread // get created on the next call to scheduleTimeout(). if (timedOut == head) { head = null; return; } } // Close the timed out node. timedOut.timedOut(); } catch (InterruptedException ignored) { } } } } /** * Removes and returns the node at the head of the list, waiting for it to time out if necessary. * This returns {@link #head} if there was no node at the head of the list when starting, and * there continues to be no node after waiting {@code IDLE_TIMEOUT_NANOS}. It returns null if a * new node was inserted while waiting. Otherwise this returns the node being waited on that has * been removed. */ static @Nullable AsyncTimeout awaitTimeout() throws InterruptedException { // Get the next eligible node. AsyncTimeout node = head.next; // The queue is empty. Wait until either something is enqueued or the idle timeout elapses. if (node == null) { long startNanos = System.nanoTime(); AsyncTimeout.class.wait(IDLE_TIMEOUT_MILLIS); return head.next == null && (System.nanoTime() - startNanos) >= IDLE_TIMEOUT_NANOS ? head // The idle timeout elapsed. : null; // The situation has changed. } long waitNanos = node.remainingNanos(System.nanoTime()); // The head of the queue hasn't timed out yet. Await that. if (waitNanos > 0) { // Waiting is made complicated by the fact that we work in nanoseconds, // but the API wants (millis, nanos) in two arguments. long waitMillis = waitNanos / 1000000L; waitNanos -= (waitMillis * 1000000L); AsyncTimeout.class.wait(waitMillis, (int) waitNanos); return null; } // The head of the queue has timed out. Remove it. head.next = node.next; node.next = null; return node; } } okio-okio-parent-1.16.0/okio/src/main/java/okio/Base64.java000066400000000000000000000127051335667376100232410ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @author Alexander Y. Kleymenov */ package okio; import java.io.UnsupportedEncodingException; final class Base64 { private Base64() { } public static byte[] decode(String in) { // Ignore trailing '=' padding and whitespace from the input. int limit = in.length(); for (; limit > 0; limit--) { char c = in.charAt(limit - 1); if (c != '=' && c != '\n' && c != '\r' && c != ' ' && c != '\t') { break; } } // If the input includes whitespace, this output array will be longer than necessary. byte[] out = new byte[(int) (limit * 6L / 8L)]; int outCount = 0; int inCount = 0; int word = 0; for (int pos = 0; pos < limit; pos++) { char c = in.charAt(pos); int bits; if (c >= 'A' && c <= 'Z') { // char ASCII value // A 65 0 // Z 90 25 (ASCII - 65) bits = c - 65; } else if (c >= 'a' && c <= 'z') { // char ASCII value // a 97 26 // z 122 51 (ASCII - 71) bits = c - 71; } else if (c >= '0' && c <= '9') { // char ASCII value // 0 48 52 // 9 57 61 (ASCII + 4) bits = c + 4; } else if (c == '+' || c == '-') { bits = 62; } else if (c == '/' || c == '_') { bits = 63; } else if (c == '\n' || c == '\r' || c == ' ' || c == '\t') { continue; } else { return null; } // Append this char's 6 bits to the word. word = (word << 6) | (byte) bits; // For every 4 chars of input, we accumulate 24 bits of output. Emit 3 bytes. inCount++; if (inCount % 4 == 0) { out[outCount++] = (byte) (word >> 16); out[outCount++] = (byte) (word >> 8); out[outCount++] = (byte) word; } } int lastWordChars = inCount % 4; if (lastWordChars == 1) { // We read 1 char followed by "===". But 6 bits is a truncated byte! Fail. return null; } else if (lastWordChars == 2) { // We read 2 chars followed by "==". Emit 1 byte with 8 of those 12 bits. word = word << 12; out[outCount++] = (byte) (word >> 16); } else if (lastWordChars == 3) { // We read 3 chars, followed by "=". Emit 2 bytes for 16 of those 18 bits. word = word << 6; out[outCount++] = (byte) (word >> 16); out[outCount++] = (byte) (word >> 8); } // If we sized our out array perfectly, we're done. if (outCount == out.length) return out; // Copy the decoded bytes to a new, right-sized array. byte[] prefix = new byte[outCount]; System.arraycopy(out, 0, prefix, 0, outCount); return prefix; } private static final byte[] MAP = new byte[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; private static final byte[] URL_MAP = new byte[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' }; public static String encode(byte[] in) { return encode(in, MAP); } public static String encodeUrl(byte[] in) { return encode(in, URL_MAP); } private static String encode(byte[] in, byte[] map) { int length = (in.length + 2) / 3 * 4; byte[] out = new byte[length]; int index = 0, end = in.length - in.length % 3; for (int i = 0; i < end; i += 3) { out[index++] = map[(in[i] & 0xff) >> 2]; out[index++] = map[((in[i] & 0x03) << 4) | ((in[i + 1] & 0xff) >> 4)]; out[index++] = map[((in[i + 1] & 0x0f) << 2) | ((in[i + 2] & 0xff) >> 6)]; out[index++] = map[(in[i + 2] & 0x3f)]; } switch (in.length % 3) { case 1: out[index++] = map[(in[end] & 0xff) >> 2]; out[index++] = map[(in[end] & 0x03) << 4]; out[index++] = '='; out[index++] = '='; break; case 2: out[index++] = map[(in[end] & 0xff) >> 2]; out[index++] = map[((in[end] & 0x03) << 4) | ((in[end + 1] & 0xff) >> 4)]; out[index++] = map[((in[end + 1] & 0x0f) << 2)]; out[index++] = '='; break; } try { return new String(out, "US-ASCII"); } catch (UnsupportedEncodingException e) { throw new AssertionError(e); } } } okio-okio-parent-1.16.0/okio/src/main/java/okio/Buffer.java000066400000000000000000002256271335667376100234370ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.Closeable; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; import java.nio.charset.Charset; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.annotation.Nullable; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import static okio.Util.checkOffsetAndCount; import static okio.Util.reverseBytesLong; /** * A collection of bytes in memory. * *

Moving data from one buffer to another is fast. Instead * of copying bytes from one place in memory to another, this class just changes * ownership of the underlying byte arrays. * *

This buffer grows with your data. Just like ArrayList, * each buffer starts small. It consumes only the memory it needs to. * *

This buffer pools its byte arrays. When you allocate a * byte array in Java, the runtime must zero-fill the requested array before * returning it to you. Even if you're going to write over that space anyway. * This class avoids zero-fill and GC churn by pooling byte arrays. */ public final class Buffer implements BufferedSource, BufferedSink, Cloneable, ByteChannel { private static final byte[] DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; static final int REPLACEMENT_CHARACTER = '\ufffd'; @Nullable Segment head; long size; public Buffer() { } /** Returns the number of bytes currently in this buffer. */ public final long size() { return size; } @Override public Buffer buffer() { return this; } @Override public Buffer getBuffer() { return this; } @Override public OutputStream outputStream() { return new OutputStream() { @Override public void write(int b) { writeByte((byte) b); } @Override public void write(byte[] data, int offset, int byteCount) { Buffer.this.write(data, offset, byteCount); } @Override public void flush() { } @Override public void close() { } @Override public String toString() { return Buffer.this + ".outputStream()"; } }; } @Override public Buffer emitCompleteSegments() { return this; // Nowhere to emit to! } @Override public BufferedSink emit() { return this; // Nowhere to emit to! } @Override public boolean exhausted() { return size == 0; } @Override public void require(long byteCount) throws EOFException { if (size < byteCount) throw new EOFException(); } @Override public boolean request(long byteCount) { return size >= byteCount; } @Override public BufferedSource peek() { return Okio.buffer(new PeekSource(this)); } @Override public InputStream inputStream() { return new InputStream() { @Override public int read() { if (size > 0) return readByte() & 0xff; return -1; } @Override public int read(byte[] sink, int offset, int byteCount) { return Buffer.this.read(sink, offset, byteCount); } @Override public int available() { return (int) Math.min(size, Integer.MAX_VALUE); } @Override public void close() { } @Override public String toString() { return Buffer.this + ".inputStream()"; } }; } /** Copy the contents of this to {@code out}. */ public final Buffer copyTo(OutputStream out) throws IOException { return copyTo(out, 0, size); } /** * Copy {@code byteCount} bytes from this, starting at {@code offset}, to * {@code out}. */ public final Buffer copyTo(OutputStream out, long offset, long byteCount) throws IOException { if (out == null) throw new IllegalArgumentException("out == null"); checkOffsetAndCount(size, offset, byteCount); if (byteCount == 0) return this; // Skip segments that we aren't copying from. Segment s = head; for (; offset >= (s.limit - s.pos); s = s.next) { offset -= (s.limit - s.pos); } // Copy from one segment at a time. for (; byteCount > 0; s = s.next) { int pos = (int) (s.pos + offset); int toCopy = (int) Math.min(s.limit - pos, byteCount); out.write(s.data, pos, toCopy); byteCount -= toCopy; offset = 0; } return this; } /** Copy {@code byteCount} bytes from this, starting at {@code offset}, to {@code out}. */ public final Buffer copyTo(Buffer out, long offset, long byteCount) { if (out == null) throw new IllegalArgumentException("out == null"); checkOffsetAndCount(size, offset, byteCount); if (byteCount == 0) return this; out.size += byteCount; // Skip segments that we aren't copying from. Segment s = head; for (; offset >= (s.limit - s.pos); s = s.next) { offset -= (s.limit - s.pos); } // Copy one segment at a time. for (; byteCount > 0; s = s.next) { Segment copy = s.sharedCopy(); copy.pos += offset; copy.limit = Math.min(copy.pos + (int) byteCount, copy.limit); if (out.head == null) { out.head = copy.next = copy.prev = copy; } else { out.head.prev.push(copy); } byteCount -= copy.limit - copy.pos; offset = 0; } return this; } /** Write the contents of this to {@code out}. */ public final Buffer writeTo(OutputStream out) throws IOException { return writeTo(out, size); } /** Write {@code byteCount} bytes from this to {@code out}. */ public final Buffer writeTo(OutputStream out, long byteCount) throws IOException { if (out == null) throw new IllegalArgumentException("out == null"); checkOffsetAndCount(size, 0, byteCount); Segment s = head; while (byteCount > 0) { int toCopy = (int) Math.min(byteCount, s.limit - s.pos); out.write(s.data, s.pos, toCopy); s.pos += toCopy; size -= toCopy; byteCount -= toCopy; if (s.pos == s.limit) { Segment toRecycle = s; head = s = toRecycle.pop(); SegmentPool.recycle(toRecycle); } } return this; } /** Read and exhaust bytes from {@code in} to this. */ public final Buffer readFrom(InputStream in) throws IOException { readFrom(in, Long.MAX_VALUE, true); return this; } /** Read {@code byteCount} bytes from {@code in} to this. */ public final Buffer readFrom(InputStream in, long byteCount) throws IOException { if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); readFrom(in, byteCount, false); return this; } private void readFrom(InputStream in, long byteCount, boolean forever) throws IOException { if (in == null) throw new IllegalArgumentException("in == null"); while (byteCount > 0 || forever) { Segment tail = writableSegment(1); int maxToCopy = (int) Math.min(byteCount, Segment.SIZE - tail.limit); int bytesRead = in.read(tail.data, tail.limit, maxToCopy); if (bytesRead == -1) { if (forever) return; throw new EOFException(); } tail.limit += bytesRead; size += bytesRead; byteCount -= bytesRead; } } /** * Returns the number of bytes in segments that are not writable. This is the * number of bytes that can be flushed immediately to an underlying sink * without harming throughput. */ public final long completeSegmentByteCount() { long result = size; if (result == 0) return 0; // Omit the tail if it's still writable. Segment tail = head.prev; if (tail.limit < Segment.SIZE && tail.owner) { result -= tail.limit - tail.pos; } return result; } @Override public byte readByte() { if (size == 0) throw new IllegalStateException("size == 0"); Segment segment = head; int pos = segment.pos; int limit = segment.limit; byte[] data = segment.data; byte b = data[pos++]; size -= 1; if (pos == limit) { head = segment.pop(); SegmentPool.recycle(segment); } else { segment.pos = pos; } return b; } /** Returns the byte at {@code pos}. */ public final byte getByte(long pos) { checkOffsetAndCount(size, pos, 1); if (size - pos > pos) { for (Segment s = head; true; s = s.next) { int segmentByteCount = s.limit - s.pos; if (pos < segmentByteCount) return s.data[s.pos + (int) pos]; pos -= segmentByteCount; } } else { pos -= size; for (Segment s = head.prev; true; s = s.prev) { pos += s.limit - s.pos; if (pos >= 0) return s.data[s.pos + (int) pos]; } } } @Override public short readShort() { if (size < 2) throw new IllegalStateException("size < 2: " + size); Segment segment = head; int pos = segment.pos; int limit = segment.limit; // If the short is split across multiple segments, delegate to readByte(). if (limit - pos < 2) { int s = (readByte() & 0xff) << 8 | (readByte() & 0xff); return (short) s; } byte[] data = segment.data; int s = (data[pos++] & 0xff) << 8 | (data[pos++] & 0xff); size -= 2; if (pos == limit) { head = segment.pop(); SegmentPool.recycle(segment); } else { segment.pos = pos; } return (short) s; } @Override public int readInt() { if (size < 4) throw new IllegalStateException("size < 4: " + size); Segment segment = head; int pos = segment.pos; int limit = segment.limit; // If the int is split across multiple segments, delegate to readByte(). if (limit - pos < 4) { return (readByte() & 0xff) << 24 | (readByte() & 0xff) << 16 | (readByte() & 0xff) << 8 | (readByte() & 0xff); } byte[] data = segment.data; int i = (data[pos++] & 0xff) << 24 | (data[pos++] & 0xff) << 16 | (data[pos++] & 0xff) << 8 | (data[pos++] & 0xff); size -= 4; if (pos == limit) { head = segment.pop(); SegmentPool.recycle(segment); } else { segment.pos = pos; } return i; } @Override public long readLong() { if (size < 8) throw new IllegalStateException("size < 8: " + size); Segment segment = head; int pos = segment.pos; int limit = segment.limit; // If the long is split across multiple segments, delegate to readInt(). if (limit - pos < 8) { return (readInt() & 0xffffffffL) << 32 | (readInt() & 0xffffffffL); } byte[] data = segment.data; long v = (data[pos++] & 0xffL) << 56 | (data[pos++] & 0xffL) << 48 | (data[pos++] & 0xffL) << 40 | (data[pos++] & 0xffL) << 32 | (data[pos++] & 0xffL) << 24 | (data[pos++] & 0xffL) << 16 | (data[pos++] & 0xffL) << 8 | (data[pos++] & 0xffL); size -= 8; if (pos == limit) { head = segment.pop(); SegmentPool.recycle(segment); } else { segment.pos = pos; } return v; } @Override public short readShortLe() { return Util.reverseBytesShort(readShort()); } @Override public int readIntLe() { return Util.reverseBytesInt(readInt()); } @Override public long readLongLe() { return Util.reverseBytesLong(readLong()); } @Override public long readDecimalLong() { if (size == 0) throw new IllegalStateException("size == 0"); // This value is always built negatively in order to accommodate Long.MIN_VALUE. long value = 0; int seen = 0; boolean negative = false; boolean done = false; long overflowZone = Long.MIN_VALUE / 10; long overflowDigit = (Long.MIN_VALUE % 10) + 1; do { Segment segment = head; byte[] data = segment.data; int pos = segment.pos; int limit = segment.limit; for (; pos < limit; pos++, seen++) { byte b = data[pos]; if (b >= '0' && b <= '9') { int digit = '0' - b; // Detect when the digit would cause an overflow. if (value < overflowZone || value == overflowZone && digit < overflowDigit) { Buffer buffer = new Buffer().writeDecimalLong(value).writeByte(b); if (!negative) buffer.readByte(); // Skip negative sign. throw new NumberFormatException("Number too large: " + buffer.readUtf8()); } value *= 10; value += digit; } else if (b == '-' && seen == 0) { negative = true; overflowDigit -= 1; } else { if (seen == 0) { throw new NumberFormatException( "Expected leading [0-9] or '-' character but was 0x" + Integer.toHexString(b)); } // Set a flag to stop iteration. We still need to run through segment updating below. done = true; break; } } if (pos == limit) { head = segment.pop(); SegmentPool.recycle(segment); } else { segment.pos = pos; } } while (!done && head != null); size -= seen; return negative ? value : -value; } @Override public long readHexadecimalUnsignedLong() { if (size == 0) throw new IllegalStateException("size == 0"); long value = 0; int seen = 0; boolean done = false; do { Segment segment = head; byte[] data = segment.data; int pos = segment.pos; int limit = segment.limit; for (; pos < limit; pos++, seen++) { int digit; byte b = data[pos]; if (b >= '0' && b <= '9') { digit = b - '0'; } else if (b >= 'a' && b <= 'f') { digit = b - 'a' + 10; } else if (b >= 'A' && b <= 'F') { digit = b - 'A' + 10; // We never write uppercase, but we support reading it. } else { if (seen == 0) { throw new NumberFormatException( "Expected leading [0-9a-fA-F] character but was 0x" + Integer.toHexString(b)); } // Set a flag to stop iteration. We still need to run through segment updating below. done = true; break; } // Detect when the shift will overflow. if ((value & 0xf000000000000000L) != 0) { Buffer buffer = new Buffer().writeHexadecimalUnsignedLong(value).writeByte(b); throw new NumberFormatException("Number too large: " + buffer.readUtf8()); } value <<= 4; value |= digit; } if (pos == limit) { head = segment.pop(); SegmentPool.recycle(segment); } else { segment.pos = pos; } } while (!done && head != null); size -= seen; return value; } @Override public ByteString readByteString() { return new ByteString(readByteArray()); } @Override public ByteString readByteString(long byteCount) throws EOFException { return new ByteString(readByteArray(byteCount)); } @Override public int select(Options options) { int index = selectPrefix(options, false); if (index == -1) return -1; // If the prefix match actually matched a full byte string, consume it and return it. int selectedSize = options.byteStrings[index].size(); try { skip(selectedSize); } catch (EOFException e) { throw new AssertionError(); } return index; } /** * Returns the index of a value in options that is a prefix of this buffer. Returns -1 if no value * is found. This method does two simultaneous iterations: it iterates the trie and it iterates * this buffer. It returns when it reaches a result in the trie, when it mismatches in the trie, * and when the buffer is exhausted. * * @param selectTruncated true to return -2 if a possible result is present but truncated. For * example, this will return -2 if the buffer contains [ab] and the options are [abc, abd]. * Note that this is made complicated by the fact that options are listed in preference order, * and one option may be a prefix of another. For example, this returns -2 if the buffer * contains [ab] and the options are [abc, a]. */ int selectPrefix(Options options, boolean selectTruncated) { Segment head = this.head; if (head == null) { if (selectTruncated) return -2; // A result is present but truncated. return options.indexOf(ByteString.EMPTY); } Segment s = head; byte[] data = head.data; int pos = head.pos; int limit = head.limit; int[] trie = options.trie; int triePos = 0; int prefixIndex = -1; navigateTrie: while (true) { int scanOrSelect = trie[triePos++]; int possiblePrefixIndex = trie[triePos++]; if (possiblePrefixIndex != -1) { prefixIndex = possiblePrefixIndex; } int nextStep; if (s == null) { break; } else if (scanOrSelect < 0) { // Scan: take multiple bytes from the buffer and the trie, looking for any mismatch. int scanByteCount = -1 * scanOrSelect; int trieLimit = triePos + scanByteCount; while (true) { int b = data[pos++] & 0xff; if (b != trie[triePos++]) return prefixIndex; // Fail 'cause we found a mismatch. boolean scanComplete = (triePos == trieLimit); // Advance to the next buffer segment if this one is exhausted. if (pos == limit) { s = s.next; pos = s.pos; data = s.data; limit = s.limit; if (s == head) { if (!scanComplete) break navigateTrie; // We were exhausted before the scan completed. s = null; // We were exhausted at the end of the scan. } } if (scanComplete) { nextStep = trie[triePos]; break; } } } else { // Select: take one byte from the buffer and find a match in the trie. int selectChoiceCount = scanOrSelect; int b = data[pos++] & 0xff; int selectLimit = triePos + selectChoiceCount; while (true) { if (triePos == selectLimit) return prefixIndex; // Fail 'cause we didn't find a match. if (b == trie[triePos]) { nextStep = trie[triePos + selectChoiceCount]; break; } triePos++; } // Advance to the next buffer segment if this one is exhausted. if (pos == limit) { s = s.next; pos = s.pos; data = s.data; limit = s.limit; if (s == head) { s = null; // No more segments! The next trie node will be our last. } } } if (nextStep >= 0) return nextStep; // Found a matching option. triePos = -nextStep; // Found another node to continue the search. } // We break out of the loop above when we've exhausted the buffer without exhausting the trie. if (selectTruncated) return -2; // The buffer is a prefix of at least one option. return prefixIndex; // Return any matches we encountered while searching for a deeper match. } @Override public void readFully(Buffer sink, long byteCount) throws EOFException { if (size < byteCount) { sink.write(this, size); // Exhaust ourselves. throw new EOFException(); } sink.write(this, byteCount); } @Override public long readAll(Sink sink) throws IOException { long byteCount = size; if (byteCount > 0) { sink.write(this, byteCount); } return byteCount; } @Override public String readUtf8() { try { return readString(size, Util.UTF_8); } catch (EOFException e) { throw new AssertionError(e); } } @Override public String readUtf8(long byteCount) throws EOFException { return readString(byteCount, Util.UTF_8); } @Override public String readString(Charset charset) { try { return readString(size, charset); } catch (EOFException e) { throw new AssertionError(e); } } @Override public String readString(long byteCount, Charset charset) throws EOFException { checkOffsetAndCount(size, 0, byteCount); if (charset == null) throw new IllegalArgumentException("charset == null"); if (byteCount > Integer.MAX_VALUE) { throw new IllegalArgumentException("byteCount > Integer.MAX_VALUE: " + byteCount); } if (byteCount == 0) return ""; Segment s = head; if (s.pos + byteCount > s.limit) { // If the string spans multiple segments, delegate to readBytes(). return new String(readByteArray(byteCount), charset); } String result = new String(s.data, s.pos, (int) byteCount, charset); s.pos += byteCount; size -= byteCount; if (s.pos == s.limit) { head = s.pop(); SegmentPool.recycle(s); } return result; } @Override public @Nullable String readUtf8Line() throws EOFException { long newline = indexOf((byte) '\n'); if (newline == -1) { return size != 0 ? readUtf8(size) : null; } return readUtf8Line(newline); } @Override public String readUtf8LineStrict() throws EOFException { return readUtf8LineStrict(Long.MAX_VALUE); } @Override public String readUtf8LineStrict(long limit) throws EOFException { if (limit < 0) throw new IllegalArgumentException("limit < 0: " + limit); long scanLength = limit == Long.MAX_VALUE ? Long.MAX_VALUE : limit + 1; long newline = indexOf((byte) '\n', 0, scanLength); if (newline != -1) return readUtf8Line(newline); if (scanLength < size() && getByte(scanLength - 1) == '\r' && getByte(scanLength) == '\n') { return readUtf8Line(scanLength); // The line was 'limit' UTF-8 bytes followed by \r\n. } Buffer data = new Buffer(); copyTo(data, 0, Math.min(32, size())); throw new EOFException("\\n not found: limit=" + Math.min(size(), limit) + " content=" + data.readByteString().hex() + '…'); } String readUtf8Line(long newline) throws EOFException { if (newline > 0 && getByte(newline - 1) == '\r') { // Read everything until '\r\n', then skip the '\r\n'. String result = readUtf8((newline - 1)); skip(2); return result; } else { // Read everything until '\n', then skip the '\n'. String result = readUtf8(newline); skip(1); return result; } } @Override public int readUtf8CodePoint() throws EOFException { if (size == 0) throw new EOFException(); byte b0 = getByte(0); int codePoint; int byteCount; int min; if ((b0 & 0x80) == 0) { // 0xxxxxxx. codePoint = b0 & 0x7f; byteCount = 1; // 7 bits (ASCII). min = 0x0; } else if ((b0 & 0xe0) == 0xc0) { // 0x110xxxxx codePoint = b0 & 0x1f; byteCount = 2; // 11 bits (5 + 6). min = 0x80; } else if ((b0 & 0xf0) == 0xe0) { // 0x1110xxxx codePoint = b0 & 0x0f; byteCount = 3; // 16 bits (4 + 6 + 6). min = 0x800; } else if ((b0 & 0xf8) == 0xf0) { // 0x11110xxx codePoint = b0 & 0x07; byteCount = 4; // 21 bits (3 + 6 + 6 + 6). min = 0x10000; } else { // We expected the first byte of a code point but got something else. skip(1); return REPLACEMENT_CHARACTER; } if (size < byteCount) { throw new EOFException("size < " + byteCount + ": " + size + " (to read code point prefixed 0x" + Integer.toHexString(b0) + ")"); } // Read the continuation bytes. If we encounter a non-continuation byte, the sequence consumed // thus far is truncated and is decoded as the replacement character. That non-continuation byte // is left in the stream for processing by the next call to readUtf8CodePoint(). for (int i = 1; i < byteCount; i++) { byte b = getByte(i); if ((b & 0xc0) == 0x80) { // 0x10xxxxxx codePoint <<= 6; codePoint |= b & 0x3f; } else { skip(i); return REPLACEMENT_CHARACTER; } } skip(byteCount); if (codePoint > 0x10ffff) { return REPLACEMENT_CHARACTER; // Reject code points larger than the Unicode maximum. } if (codePoint >= 0xd800 && codePoint <= 0xdfff) { return REPLACEMENT_CHARACTER; // Reject partial surrogates. } if (codePoint < min) { return REPLACEMENT_CHARACTER; // Reject overlong code points. } return codePoint; } @Override public byte[] readByteArray() { try { return readByteArray(size); } catch (EOFException e) { throw new AssertionError(e); } } @Override public byte[] readByteArray(long byteCount) throws EOFException { checkOffsetAndCount(size, 0, byteCount); if (byteCount > Integer.MAX_VALUE) { throw new IllegalArgumentException("byteCount > Integer.MAX_VALUE: " + byteCount); } byte[] result = new byte[(int) byteCount]; readFully(result); return result; } @Override public int read(byte[] sink) { return read(sink, 0, sink.length); } @Override public void readFully(byte[] sink) throws EOFException { int offset = 0; while (offset < sink.length) { int read = read(sink, offset, sink.length - offset); if (read == -1) throw new EOFException(); offset += read; } } @Override public int read(byte[] sink, int offset, int byteCount) { checkOffsetAndCount(sink.length, offset, byteCount); Segment s = head; if (s == null) return -1; int toCopy = Math.min(byteCount, s.limit - s.pos); System.arraycopy(s.data, s.pos, sink, offset, toCopy); s.pos += toCopy; size -= toCopy; if (s.pos == s.limit) { head = s.pop(); SegmentPool.recycle(s); } return toCopy; } @Override public int read(ByteBuffer sink) throws IOException { Segment s = head; if (s == null) return -1; int toCopy = Math.min(sink.remaining(), s.limit - s.pos); sink.put(s.data, s.pos, toCopy); s.pos += toCopy; size -= toCopy; if (s.pos == s.limit) { head = s.pop(); SegmentPool.recycle(s); } return toCopy; } /** * Discards all bytes in this buffer. Calling this method when you're done * with a buffer will return its segments to the pool. */ public final void clear() { try { skip(size); } catch (EOFException e) { throw new AssertionError(e); } } /** Discards {@code byteCount} bytes from the head of this buffer. */ @Override public void skip(long byteCount) throws EOFException { while (byteCount > 0) { if (head == null) throw new EOFException(); int toSkip = (int) Math.min(byteCount, head.limit - head.pos); size -= toSkip; byteCount -= toSkip; head.pos += toSkip; if (head.pos == head.limit) { Segment toRecycle = head; head = toRecycle.pop(); SegmentPool.recycle(toRecycle); } } } @Override public Buffer write(ByteString byteString) { if (byteString == null) throw new IllegalArgumentException("byteString == null"); byteString.write(this); return this; } @Override public Buffer writeUtf8(String string) { return writeUtf8(string, 0, string.length()); } @Override public Buffer writeUtf8(String string, int beginIndex, int endIndex) { if (string == null) throw new IllegalArgumentException("string == null"); if (beginIndex < 0) throw new IllegalArgumentException("beginIndex < 0: " + beginIndex); if (endIndex < beginIndex) { throw new IllegalArgumentException("endIndex < beginIndex: " + endIndex + " < " + beginIndex); } if (endIndex > string.length()) { throw new IllegalArgumentException( "endIndex > string.length: " + endIndex + " > " + string.length()); } // Transcode a UTF-16 Java String to UTF-8 bytes. for (int i = beginIndex; i < endIndex;) { int c = string.charAt(i); if (c < 0x80) { Segment tail = writableSegment(1); byte[] data = tail.data; int segmentOffset = tail.limit - i; int runLimit = Math.min(endIndex, Segment.SIZE - segmentOffset); // Emit a 7-bit character with 1 byte. data[segmentOffset + i++] = (byte) c; // 0xxxxxxx // Fast-path contiguous runs of ASCII characters. This is ugly, but yields a ~4x performance // improvement over independent calls to writeByte(). while (i < runLimit) { c = string.charAt(i); if (c >= 0x80) break; data[segmentOffset + i++] = (byte) c; // 0xxxxxxx } int runSize = i + segmentOffset - tail.limit; // Equivalent to i - (previous i). tail.limit += runSize; size += runSize; } else if (c < 0x800) { // Emit a 11-bit character with 2 bytes. writeByte(c >> 6 | 0xc0); // 110xxxxx writeByte(c & 0x3f | 0x80); // 10xxxxxx i++; } else if (c < 0xd800 || c > 0xdfff) { // Emit a 16-bit character with 3 bytes. writeByte(c >> 12 | 0xe0); // 1110xxxx writeByte(c >> 6 & 0x3f | 0x80); // 10xxxxxx writeByte(c & 0x3f | 0x80); // 10xxxxxx i++; } else { // c is a surrogate. Make sure it is a high surrogate & that its successor is a low // surrogate. If not, the UTF-16 is invalid, in which case we emit a replacement character. int low = i + 1 < endIndex ? string.charAt(i + 1) : 0; if (c > 0xdbff || low < 0xdc00 || low > 0xdfff) { writeByte('?'); i++; continue; } // UTF-16 high surrogate: 110110xxxxxxxxxx (10 bits) // UTF-16 low surrogate: 110111yyyyyyyyyy (10 bits) // Unicode code point: 00010000000000000000 + xxxxxxxxxxyyyyyyyyyy (21 bits) int codePoint = 0x010000 + ((c & ~0xd800) << 10 | low & ~0xdc00); // Emit a 21-bit character with 4 bytes. writeByte(codePoint >> 18 | 0xf0); // 11110xxx writeByte(codePoint >> 12 & 0x3f | 0x80); // 10xxxxxx writeByte(codePoint >> 6 & 0x3f | 0x80); // 10xxyyyy writeByte(codePoint & 0x3f | 0x80); // 10yyyyyy i += 2; } } return this; } @Override public Buffer writeUtf8CodePoint(int codePoint) { if (codePoint < 0x80) { // Emit a 7-bit code point with 1 byte. writeByte(codePoint); } else if (codePoint < 0x800) { // Emit a 11-bit code point with 2 bytes. writeByte(codePoint >> 6 | 0xc0); // 110xxxxx writeByte(codePoint & 0x3f | 0x80); // 10xxxxxx } else if (codePoint < 0x10000) { if (codePoint >= 0xd800 && codePoint <= 0xdfff) { // Emit a replacement character for a partial surrogate. writeByte('?'); } else { // Emit a 16-bit code point with 3 bytes. writeByte(codePoint >> 12 | 0xe0); // 1110xxxx writeByte(codePoint >> 6 & 0x3f | 0x80); // 10xxxxxx writeByte(codePoint & 0x3f | 0x80); // 10xxxxxx } } else if (codePoint <= 0x10ffff) { // Emit a 21-bit code point with 4 bytes. writeByte(codePoint >> 18 | 0xf0); // 11110xxx writeByte(codePoint >> 12 & 0x3f | 0x80); // 10xxxxxx writeByte(codePoint >> 6 & 0x3f | 0x80); // 10xxxxxx writeByte(codePoint & 0x3f | 0x80); // 10xxxxxx } else { throw new IllegalArgumentException( "Unexpected code point: " + Integer.toHexString(codePoint)); } return this; } @Override public Buffer writeString(String string, Charset charset) { return writeString(string, 0, string.length(), charset); } @Override public Buffer writeString(String string, int beginIndex, int endIndex, Charset charset) { if (string == null) throw new IllegalArgumentException("string == null"); if (beginIndex < 0) throw new IllegalAccessError("beginIndex < 0: " + beginIndex); if (endIndex < beginIndex) { throw new IllegalArgumentException("endIndex < beginIndex: " + endIndex + " < " + beginIndex); } if (endIndex > string.length()) { throw new IllegalArgumentException( "endIndex > string.length: " + endIndex + " > " + string.length()); } if (charset == null) throw new IllegalArgumentException("charset == null"); if (charset.equals(Util.UTF_8)) return writeUtf8(string, beginIndex, endIndex); byte[] data = string.substring(beginIndex, endIndex).getBytes(charset); return write(data, 0, data.length); } @Override public Buffer write(byte[] source) { if (source == null) throw new IllegalArgumentException("source == null"); return write(source, 0, source.length); } @Override public Buffer write(byte[] source, int offset, int byteCount) { if (source == null) throw new IllegalArgumentException("source == null"); checkOffsetAndCount(source.length, offset, byteCount); int limit = offset + byteCount; while (offset < limit) { Segment tail = writableSegment(1); int toCopy = Math.min(limit - offset, Segment.SIZE - tail.limit); System.arraycopy(source, offset, tail.data, tail.limit, toCopy); offset += toCopy; tail.limit += toCopy; } size += byteCount; return this; } @Override public int write(ByteBuffer source) throws IOException { if (source == null) throw new IllegalArgumentException("source == null"); int byteCount = source.remaining(); int remaining = byteCount; while (remaining > 0) { Segment tail = writableSegment(1); int toCopy = Math.min(remaining, Segment.SIZE - tail.limit); source.get(tail.data, tail.limit, toCopy); remaining -= toCopy; tail.limit += toCopy; } size += byteCount; return byteCount; } @Override public long writeAll(Source source) throws IOException { if (source == null) throw new IllegalArgumentException("source == null"); long totalBytesRead = 0; for (long readCount; (readCount = source.read(this, Segment.SIZE)) != -1; ) { totalBytesRead += readCount; } return totalBytesRead; } @Override public BufferedSink write(Source source, long byteCount) throws IOException { while (byteCount > 0) { long read = source.read(this, byteCount); if (read == -1) throw new EOFException(); byteCount -= read; } return this; } @Override public Buffer writeByte(int b) { Segment tail = writableSegment(1); tail.data[tail.limit++] = (byte) b; size += 1; return this; } @Override public Buffer writeShort(int s) { Segment tail = writableSegment(2); byte[] data = tail.data; int limit = tail.limit; data[limit++] = (byte) ((s >>> 8) & 0xff); data[limit++] = (byte) (s & 0xff); tail.limit = limit; size += 2; return this; } @Override public Buffer writeShortLe(int s) { return writeShort(Util.reverseBytesShort((short) s)); } @Override public Buffer writeInt(int i) { Segment tail = writableSegment(4); byte[] data = tail.data; int limit = tail.limit; data[limit++] = (byte) ((i >>> 24) & 0xff); data[limit++] = (byte) ((i >>> 16) & 0xff); data[limit++] = (byte) ((i >>> 8) & 0xff); data[limit++] = (byte) (i & 0xff); tail.limit = limit; size += 4; return this; } @Override public Buffer writeIntLe(int i) { return writeInt(Util.reverseBytesInt(i)); } @Override public Buffer writeLong(long v) { Segment tail = writableSegment(8); byte[] data = tail.data; int limit = tail.limit; data[limit++] = (byte) ((v >>> 56L) & 0xff); data[limit++] = (byte) ((v >>> 48L) & 0xff); data[limit++] = (byte) ((v >>> 40L) & 0xff); data[limit++] = (byte) ((v >>> 32L) & 0xff); data[limit++] = (byte) ((v >>> 24L) & 0xff); data[limit++] = (byte) ((v >>> 16L) & 0xff); data[limit++] = (byte) ((v >>> 8L) & 0xff); data[limit++] = (byte) (v & 0xff); tail.limit = limit; size += 8; return this; } @Override public Buffer writeLongLe(long v) { return writeLong(reverseBytesLong(v)); } @Override public Buffer writeDecimalLong(long v) { if (v == 0) { // Both a shortcut and required since the following code can't handle zero. return writeByte('0'); } boolean negative = false; if (v < 0) { v = -v; if (v < 0) { // Only true for Long.MIN_VALUE. return writeUtf8("-9223372036854775808"); } negative = true; } // Binary search for character width which favors matching lower numbers. int width = // v < 100000000L ? v < 10000L ? v < 100L ? v < 10L ? 1 : 2 : v < 1000L ? 3 : 4 : v < 1000000L ? v < 100000L ? 5 : 6 : v < 10000000L ? 7 : 8 : v < 1000000000000L ? v < 10000000000L ? v < 1000000000L ? 9 : 10 : v < 100000000000L ? 11 : 12 : v < 1000000000000000L ? v < 10000000000000L ? 13 : v < 100000000000000L ? 14 : 15 : v < 100000000000000000L ? v < 10000000000000000L ? 16 : 17 : v < 1000000000000000000L ? 18 : 19; if (negative) { ++width; } Segment tail = writableSegment(width); byte[] data = tail.data; int pos = tail.limit + width; // We write backwards from right to left. while (v != 0) { int digit = (int) (v % 10); data[--pos] = DIGITS[digit]; v /= 10; } if (negative) { data[--pos] = '-'; } tail.limit += width; this.size += width; return this; } @Override public Buffer writeHexadecimalUnsignedLong(long v) { if (v == 0) { // Both a shortcut and required since the following code can't handle zero. return writeByte('0'); } int width = Long.numberOfTrailingZeros(Long.highestOneBit(v)) / 4 + 1; Segment tail = writableSegment(width); byte[] data = tail.data; for (int pos = tail.limit + width - 1, start = tail.limit; pos >= start; pos--) { data[pos] = DIGITS[(int) (v & 0xF)]; v >>>= 4; } tail.limit += width; size += width; return this; } /** * Returns a tail segment that we can write at least {@code minimumCapacity} * bytes to, creating it if necessary. */ Segment writableSegment(int minimumCapacity) { if (minimumCapacity < 1 || minimumCapacity > Segment.SIZE) throw new IllegalArgumentException(); if (head == null) { head = SegmentPool.take(); // Acquire a first segment. return head.next = head.prev = head; } Segment tail = head.prev; if (tail.limit + minimumCapacity > Segment.SIZE || !tail.owner) { tail = tail.push(SegmentPool.take()); // Append a new empty segment to fill up. } return tail; } @Override public void write(Buffer source, long byteCount) { // Move bytes from the head of the source buffer to the tail of this buffer // while balancing two conflicting goals: don't waste CPU and don't waste // memory. // // // Don't waste CPU (ie. don't copy data around). // // Copying large amounts of data is expensive. Instead, we prefer to // reassign entire segments from one buffer to the other. // // // Don't waste memory. // // As an invariant, adjacent pairs of segments in a buffer should be at // least 50% full, except for the head segment and the tail segment. // // The head segment cannot maintain the invariant because the application is // consuming bytes from this segment, decreasing its level. // // The tail segment cannot maintain the invariant because the application is // producing bytes, which may require new nearly-empty tail segments to be // appended. // // // Moving segments between buffers // // When writing one buffer to another, we prefer to reassign entire segments // over copying bytes into their most compact form. Suppose we have a buffer // with these segment levels [91%, 61%]. If we append a buffer with a // single [72%] segment, that yields [91%, 61%, 72%]. No bytes are copied. // // Or suppose we have a buffer with these segment levels: [100%, 2%], and we // want to append it to a buffer with these segment levels [99%, 3%]. This // operation will yield the following segments: [100%, 2%, 99%, 3%]. That // is, we do not spend time copying bytes around to achieve more efficient // memory use like [100%, 100%, 4%]. // // When combining buffers, we will compact adjacent buffers when their // combined level doesn't exceed 100%. For example, when we start with // [100%, 40%] and append [30%, 80%], the result is [100%, 70%, 80%]. // // // Splitting segments // // Occasionally we write only part of a source buffer to a sink buffer. For // example, given a sink [51%, 91%], we may want to write the first 30% of // a source [92%, 82%] to it. To simplify, we first transform the source to // an equivalent buffer [30%, 62%, 82%] and then move the head segment, // yielding sink [51%, 91%, 30%] and source [62%, 82%]. if (source == null) throw new IllegalArgumentException("source == null"); if (source == this) throw new IllegalArgumentException("source == this"); checkOffsetAndCount(source.size, 0, byteCount); while (byteCount > 0) { // Is a prefix of the source's head segment all that we need to move? if (byteCount < (source.head.limit - source.head.pos)) { Segment tail = head != null ? head.prev : null; if (tail != null && tail.owner && (byteCount + tail.limit - (tail.shared ? 0 : tail.pos) <= Segment.SIZE)) { // Our existing segments are sufficient. Move bytes from source's head to our tail. source.head.writeTo(tail, (int) byteCount); source.size -= byteCount; size += byteCount; return; } else { // We're going to need another segment. Split the source's head // segment in two, then move the first of those two to this buffer. source.head = source.head.split((int) byteCount); } } // Remove the source's head segment and append it to our tail. Segment segmentToMove = source.head; long movedByteCount = segmentToMove.limit - segmentToMove.pos; source.head = segmentToMove.pop(); if (head == null) { head = segmentToMove; head.next = head.prev = head; } else { Segment tail = head.prev; tail = tail.push(segmentToMove); tail.compact(); } source.size -= movedByteCount; size += movedByteCount; byteCount -= movedByteCount; } } @Override public long read(Buffer sink, long byteCount) { if (sink == null) throw new IllegalArgumentException("sink == null"); if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); if (size == 0) return -1L; if (byteCount > size) byteCount = size; sink.write(this, byteCount); return byteCount; } @Override public long indexOf(byte b) { return indexOf(b, 0, Long.MAX_VALUE); } /** * Returns the index of {@code b} in this at or beyond {@code fromIndex}, or * -1 if this buffer does not contain {@code b} in that range. */ @Override public long indexOf(byte b, long fromIndex) { return indexOf(b, fromIndex, Long.MAX_VALUE); } @Override public long indexOf(byte b, long fromIndex, long toIndex) { if (fromIndex < 0 || toIndex < fromIndex) { throw new IllegalArgumentException( String.format("size=%s fromIndex=%s toIndex=%s", size, fromIndex, toIndex)); } if (toIndex > size) toIndex = size; if (fromIndex == toIndex) return -1L; Segment s; long offset; // TODO(jwilson): extract this to a shared helper method when can do so without allocating. findSegmentAndOffset: { // Pick the first segment to scan. This is the first segment with offset <= fromIndex. s = head; if (s == null) { // No segments to scan! return -1L; } else if (size - fromIndex < fromIndex) { // We're scanning in the back half of this buffer. Find the segment starting at the back. offset = size; while (offset > fromIndex) { s = s.prev; offset -= (s.limit - s.pos); } } else { // We're scanning in the front half of this buffer. Find the segment starting at the front. offset = 0L; for (long nextOffset; (nextOffset = offset + (s.limit - s.pos)) < fromIndex; ) { s = s.next; offset = nextOffset; } } } // Scan through the segments, searching for b. while (offset < toIndex) { byte[] data = s.data; int limit = (int) Math.min(s.limit, s.pos + toIndex - offset); int pos = (int) (s.pos + fromIndex - offset); for (; pos < limit; pos++) { if (data[pos] == b) { return pos - s.pos + offset; } } // Not in this segment. Try the next one. offset += (s.limit - s.pos); fromIndex = offset; s = s.next; } return -1L; } @Override public long indexOf(ByteString bytes) throws IOException { return indexOf(bytes, 0); } @Override public long indexOf(ByteString bytes, long fromIndex) throws IOException { if (bytes.size() == 0) throw new IllegalArgumentException("bytes is empty"); if (fromIndex < 0) throw new IllegalArgumentException("fromIndex < 0"); Segment s; long offset; // TODO(jwilson): extract this to a shared helper method when can do so without allocating. findSegmentAndOffset: { // Pick the first segment to scan. This is the first segment with offset <= fromIndex. s = head; if (s == null) { // No segments to scan! return -1L; } else if (size - fromIndex < fromIndex) { // We're scanning in the back half of this buffer. Find the segment starting at the back. offset = size; while (offset > fromIndex) { s = s.prev; offset -= (s.limit - s.pos); } } else { // We're scanning in the front half of this buffer. Find the segment starting at the front. offset = 0L; for (long nextOffset; (nextOffset = offset + (s.limit - s.pos)) < fromIndex; ) { s = s.next; offset = nextOffset; } } } // Scan through the segments, searching for the lead byte. Each time that is found, delegate to // rangeEquals() to check for a complete match. byte b0 = bytes.getByte(0); int bytesSize = bytes.size(); long resultLimit = size - bytesSize + 1; while (offset < resultLimit) { // Scan through the current segment. byte[] data = s.data; int segmentLimit = (int) Math.min(s.limit, s.pos + resultLimit - offset); for (int pos = (int) (s.pos + fromIndex - offset); pos < segmentLimit; pos++) { if (data[pos] == b0 && rangeEquals(s, pos + 1, bytes, 1, bytesSize)) { return pos - s.pos + offset; } } // Not in this segment. Try the next one. offset += (s.limit - s.pos); fromIndex = offset; s = s.next; } return -1L; } @Override public long indexOfElement(ByteString targetBytes) { return indexOfElement(targetBytes, 0); } @Override public long indexOfElement(ByteString targetBytes, long fromIndex) { if (fromIndex < 0) throw new IllegalArgumentException("fromIndex < 0"); Segment s; long offset; // TODO(jwilson): extract this to a shared helper method when can do so without allocating. findSegmentAndOffset: { // Pick the first segment to scan. This is the first segment with offset <= fromIndex. s = head; if (s == null) { // No segments to scan! return -1L; } else if (size - fromIndex < fromIndex) { // We're scanning in the back half of this buffer. Find the segment starting at the back. offset = size; while (offset > fromIndex) { s = s.prev; offset -= (s.limit - s.pos); } } else { // We're scanning in the front half of this buffer. Find the segment starting at the front. offset = 0L; for (long nextOffset; (nextOffset = offset + (s.limit - s.pos)) < fromIndex; ) { s = s.next; offset = nextOffset; } } } // Special case searching for one of two bytes. This is a common case for tools like Moshi, // which search for pairs of chars like `\r` and `\n` or {@code `"` and `\`. The impact of this // optimization is a ~5x speedup for this case without a substantial cost to other cases. if (targetBytes.size() == 2) { // Scan through the segments, searching for either of the two bytes. byte b0 = targetBytes.getByte(0); byte b1 = targetBytes.getByte(1); while (offset < size) { byte[] data = s.data; for (int pos = (int) (s.pos + fromIndex - offset), limit = s.limit; pos < limit; pos++) { int b = data[pos]; if (b == b0 || b == b1) { return pos - s.pos + offset; } } // Not in this segment. Try the next one. offset += (s.limit - s.pos); fromIndex = offset; s = s.next; } } else { // Scan through the segments, searching for a byte that's also in the array. byte[] targetByteArray = targetBytes.internalArray(); while (offset < size) { byte[] data = s.data; for (int pos = (int) (s.pos + fromIndex - offset), limit = s.limit; pos < limit; pos++) { int b = data[pos]; for (byte t : targetByteArray) { if (b == t) return pos - s.pos + offset; } } // Not in this segment. Try the next one. offset += (s.limit - s.pos); fromIndex = offset; s = s.next; } } return -1L; } @Override public boolean rangeEquals(long offset, ByteString bytes) { return rangeEquals(offset, bytes, 0, bytes.size()); } @Override public boolean rangeEquals( long offset, ByteString bytes, int bytesOffset, int byteCount) { if (offset < 0 || bytesOffset < 0 || byteCount < 0 || size - offset < byteCount || bytes.size() - bytesOffset < byteCount) { return false; } for (int i = 0; i < byteCount; i++) { if (getByte(offset + i) != bytes.getByte(bytesOffset + i)) { return false; } } return true; } /** * Returns true if the range within this buffer starting at {@code segmentPos} in {@code segment} * is equal to {@code bytes[bytesOffset..bytesLimit)}. */ private boolean rangeEquals( Segment segment, int segmentPos, ByteString bytes, int bytesOffset, int bytesLimit) { int segmentLimit = segment.limit; byte[] data = segment.data; for (int i = bytesOffset; i < bytesLimit; ) { if (segmentPos == segmentLimit) { segment = segment.next; data = segment.data; segmentPos = segment.pos; segmentLimit = segment.limit; } if (data[segmentPos] != bytes.getByte(i)) { return false; } segmentPos++; i++; } return true; } @Override public void flush() { } @Override public boolean isOpen() { return true; } @Override public void close() { } @Override public Timeout timeout() { return Timeout.NONE; } /** For testing. This returns the sizes of the segments in this buffer. */ List segmentSizes() { if (head == null) return Collections.emptyList(); List result = new ArrayList<>(); result.add(head.limit - head.pos); for (Segment s = head.next; s != head; s = s.next) { result.add(s.limit - s.pos); } return result; } /** Returns the 128-bit MD5 hash of this buffer. */ public final ByteString md5() { return digest("MD5"); } /** Returns the 160-bit SHA-1 hash of this buffer. */ public final ByteString sha1() { return digest("SHA-1"); } /** Returns the 256-bit SHA-256 hash of this buffer. */ public final ByteString sha256() { return digest("SHA-256"); } /** Returns the 512-bit SHA-512 hash of this buffer. */ public final ByteString sha512() { return digest("SHA-512"); } private ByteString digest(String algorithm) { try { MessageDigest messageDigest = MessageDigest.getInstance(algorithm); if (head != null) { messageDigest.update(head.data, head.pos, head.limit - head.pos); for (Segment s = head.next; s != head; s = s.next) { messageDigest.update(s.data, s.pos, s.limit - s.pos); } } return ByteString.of(messageDigest.digest()); } catch (NoSuchAlgorithmException e) { throw new AssertionError(); } } /** Returns the 160-bit SHA-1 HMAC of this buffer. */ public final ByteString hmacSha1(ByteString key) { return hmac("HmacSHA1", key); } /** Returns the 256-bit SHA-256 HMAC of this buffer. */ public final ByteString hmacSha256(ByteString key) { return hmac("HmacSHA256", key); } /** Returns the 512-bit SHA-512 HMAC of this buffer. */ public final ByteString hmacSha512(ByteString key) { return hmac("HmacSHA512", key); } private ByteString hmac(String algorithm, ByteString key) { try { Mac mac = Mac.getInstance(algorithm); mac.init(new SecretKeySpec(key.toByteArray(), algorithm)); if (head != null) { mac.update(head.data, head.pos, head.limit - head.pos); for (Segment s = head.next; s != head; s = s.next) { mac.update(s.data, s.pos, s.limit - s.pos); } } return ByteString.of(mac.doFinal()); } catch (NoSuchAlgorithmException e) { throw new AssertionError(); } catch (InvalidKeyException e) { throw new IllegalArgumentException(e); } } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Buffer)) return false; Buffer that = (Buffer) o; if (size != that.size) return false; if (size == 0) return true; // Both buffers are empty. Segment sa = this.head; Segment sb = that.head; int posA = sa.pos; int posB = sb.pos; for (long pos = 0, count; pos < size; pos += count) { count = Math.min(sa.limit - posA, sb.limit - posB); for (int i = 0; i < count; i++) { if (sa.data[posA++] != sb.data[posB++]) return false; } if (posA == sa.limit) { sa = sa.next; posA = sa.pos; } if (posB == sb.limit) { sb = sb.next; posB = sb.pos; } } return true; } @Override public int hashCode() { Segment s = head; if (s == null) return 0; int result = 1; do { for (int pos = s.pos, limit = s.limit; pos < limit; pos++) { result = 31 * result + s.data[pos]; } s = s.next; } while (s != head); return result; } /** * Returns a human-readable string that describes the contents of this buffer. Typically this * is a string like {@code [text=Hello]} or {@code [hex=0000ffff]}. */ @Override public String toString() { return snapshot().toString(); } /** Returns a deep copy of this buffer. */ @Override public Buffer clone() { Buffer result = new Buffer(); if (size == 0) return result; result.head = head.sharedCopy(); result.head.next = result.head.prev = result.head; for (Segment s = head.next; s != head; s = s.next) { result.head.prev.push(s.sharedCopy()); } result.size = size; return result; } /** Returns an immutable copy of this buffer as a byte string. */ public final ByteString snapshot() { if (size > Integer.MAX_VALUE) { throw new IllegalArgumentException("size > Integer.MAX_VALUE: " + size); } return snapshot((int) size); } /** * Returns an immutable copy of the first {@code byteCount} bytes of this buffer as a byte string. */ public final ByteString snapshot(int byteCount) { if (byteCount == 0) return ByteString.EMPTY; return new SegmentedByteString(this, byteCount); } public final UnsafeCursor readUnsafe() { return readUnsafe(new UnsafeCursor()); } public final UnsafeCursor readUnsafe(UnsafeCursor unsafeCursor) { if (unsafeCursor.buffer != null) { throw new IllegalStateException("already attached to a buffer"); } unsafeCursor.buffer = this; unsafeCursor.readWrite = false; return unsafeCursor; } public final UnsafeCursor readAndWriteUnsafe() { return readAndWriteUnsafe(new UnsafeCursor()); } public final UnsafeCursor readAndWriteUnsafe(UnsafeCursor unsafeCursor) { if (unsafeCursor.buffer != null) { throw new IllegalStateException("already attached to a buffer"); } unsafeCursor.buffer = this; unsafeCursor.readWrite = true; return unsafeCursor; } /** * A handle to the underlying data in a buffer. This handle is unsafe because it does not enforce * its own invariants. Instead, it assumes a careful user who has studied Okio's implementation * details and their consequences. * *

Buffer Internals

* *

Most code should use {@code Buffer} as a black box: a class that holds 0 or more bytes of * data with efficient APIs to append data to the end and to consume data from the front. Usually * this is also the most efficient way to use buffers because it allows Okio to employ several * optimizations, including: * *

    *
  • Fast Allocation: Buffers use a shared pool of memory that is not * zero-filled before use. *
  • Fast Resize: A buffer's capacity can change without copying its * contents. *
  • Fast Move: Memory ownership can be reassigned from one buffer to * another. *
  • Fast Copy: Multiple buffers can share the same underlying memory. *
  • Fast Encoding and Decoding: Common operations like UTF-8 encoding and * decimal decoding do not require intermediate objects to be allocated. *
* *

These optimizations all leverage the way Okio stores data internally. Okio Buffers are * implemented using a doubly-linked list of segments. Each segment is a contiguous range within a * 8 KiB {@code byte[]}. Each segment has two indexes, {@code start}, the offset of the first * byte of the array containing application data, and {@code end}, the offset of the first byte * beyond {@code start} whose data is undefined. * *

New buffers are empty and have no segments: * *

   {@code
   *
   *   Buffer buffer = new Buffer();
   * }
* * We append 7 bytes of data to the end of our empty buffer. Internally, the buffer allocates a * segment and writes its new data there. The lone segment has an 8 KiB byte array but only 7 * bytes of data: * *
   {@code
   *
   *   buffer.writeUtf8("sealion");
   *
   *   // [ 's', 'e', 'a', 'l', 'i', 'o', 'n', '?', '?', '?', ...]
   *   //    ^                                  ^
   *   // start = 0                          end = 7
   * }
* * When we read 4 bytes of data from the buffer, it finds its first segment and returns that data * to us. As bytes are read the data is consumed. The segment tracks this by adjusting its * internal indices. * *
   {@code
   *
   *   buffer.readUtf8(4); // "seal"
   *
   *   // [ 's', 'e', 'a', 'l', 'i', 'o', 'n', '?', '?', '?', ...]
   *   //                        ^              ^
   *   //                     start = 4      end = 7
   * }
* * As we write data into a buffer we fill up its internal segments. When a write doesn't fit into * a buffer's last segment, additional segments are allocated and appended to the linked list of * segments. Each segment has its own start and end indexes tracking where the user's data begins * and ends. * *
   {@code
   *
   *   Buffer xoxo = new Buffer();
   *   xoxo.writeUtf8(Strings.repeat("xo", 5_000));
   *
   *   // [ 'x', 'o', 'x', 'o', 'x', 'o', 'x', 'o', ..., 'x', 'o', 'x', 'o']
   *   //    ^                                                               ^
   *   // start = 0                                                      end = 8192
   *   //
   *   // [ 'x', 'o', 'x', 'o', ..., 'x', 'o', 'x', 'o', '?', '?', '?', ...]
   *   //    ^                                            ^
   *   // start = 0                                   end = 1808
   * }
* * The start index is always inclusive and the end index is always * exclusive. The data preceding the start index is undefined, and the data * at and following the end index is undefined. * *

After the last byte of a segment has been read, that segment may be returned to an internal * segment pool. In addition to reducing the need to do garbage collection, segment pooling also * saves the JVM from needing to zero-fill byte arrays. Okio doesn't need to zero-fill its arrays * because it always writes memory before it reads it. But if you look at a segment in a debugger * you may see its effects. In this example, one of the "xoxo" segments above is reused in an * unrelated buffer: * *

   {@code
   *
   *   Buffer abc = new Buffer();
   *   abc.writeUtf8("abc");
   *
   *   // [ 'a', 'b', 'c', 'o', 'x', 'o', 'x', 'o', ...]
   *   //    ^              ^
   *   // start = 0     end = 3
   * }
* * There is an optimization in {@code Buffer.clone()} and other methods that allows two segments * to share the same underlying byte array. Clones can't write to the shared byte array; instead * they allocate a new (private) segment early. * *
   {@code
   *
   *   Buffer nana = new Buffer();
   *   nana.writeUtf8(Strings.repeat("na", 2_500));
   *   nana.readUtf8(2); // "na"
   *
   *   // [ 'n', 'a', 'n', 'a', ..., 'n', 'a', 'n', 'a', '?', '?', '?', ...]
   *   //              ^                                  ^
   *   //           start = 0                         end = 5000
   *
   *   nana2 = nana.clone();
   *   nana2.writeUtf8("batman");
   *
   *   // [ 'n', 'a', 'n', 'a', ..., 'n', 'a', 'n', 'a', '?', '?', '?', ...]
   *   //              ^                                  ^
   *   //           start = 0                         end = 5000
   *   //
   *   // [ 'b', 'a', 't', 'm', 'a', 'n', '?', '?', '?', ...]
   *   //    ^                             ^
   *   //  start = 0                    end = 7
   * }
* * Segments are not shared when the shared region is small (ie. less than 1 KiB). This is intended * to prevent fragmentation in sharing-heavy use cases. * *

Unsafe Cursor API

* *

This class exposes privileged access to the internal byte arrays of a buffer. A cursor * either references the data of a single segment, it is before the first segment ({@code * offset == -1}), or it is after the last segment ({@code offset == buffer.size}). * *

Call {@link #seek} to move the cursor to the segment that contains a specified offset. After * seeking, {@link #data} references the segment's internal byte array, {@link #start} is the * segment's start and {@link #end} is its end. * *

Call {@link #next} to advance the cursor to the next segment. This returns -1 if there are * no further segments in the buffer. * *

Use {@link Buffer#readUnsafe} to create a cursor to read buffer data and {@link * Buffer#readAndWriteUnsafe} to create a cursor to read and write buffer data. In either case, * always call {@link #close} when done with a cursor. This is convenient with Java 7's * try-with-resources syntax. In this example we read all of the bytes in a buffer into a byte * array: * *

   {@code
   *
   *   byte[] bufferBytes = new byte[(int) buffer.size()];
   *
   *   try (UnsafeCursor cursor = buffer.readUnsafe()) {
   *     while (cursor.next() != -1) {
   *       System.arraycopy(cursor.data, cursor.start,
   *           bufferBytes, (int) cursor.offset, cursor.end - cursor.start);
   *     }
   *   }
   * }
* *

Change the capacity of a buffer with {@link #resizeBuffer}. This is only permitted for * read+write cursors. The buffer's size always changes from the end: shrinking it removes bytes * from the end; growing it adds capacity to the end. * *

Warnings

* *

Most application developers should avoid this API. Those that must use this API should * respect these warnings. * *

Don't mutate a cursor. This class has public, non-final fields because that * is convenient for low-level I/O frameworks. Never assign values to these fields; instead use * the cursor API to adjust these. * *

Never mutate {@code data} unless you have read+write access. You are on the * honor system to never write the buffer in read-only mode. Read-only mode may be more efficient * than read+write mode because it does not need to make private copies of shared segments. * *

Only access data in {@code [start..end)}. Other data in the byte array * is undefined! It may contain private or sensitive data from other parts of your process. * *

Always fill the new capacity when you grow a buffer. New capacity is not * zero-filled and may contain data from other parts of your process. Avoid leaking this * information by always writing something to the newly-allocated capacity. Do not assume that * new capacity will be filled with {@code 0}; it will not be. * *

Do not access a buffer while is being accessed by a cursor. Even simple * read-only operations like {@link Buffer#clone} are unsafe because they mark segments as shared. * *

Do not hard-code the segment size in your application. It is possible that * segment sizes will change with advances in hardware. Future versions of Okio may even have * heterogeneous segment sizes. * *

These warnings are intended to help you to use this API safely. It's here for developers * that need absolutely the most throughput. Since that's you, here's one final performance tip. * You can reuse instances of this class if you like. Use the overloads of {@link #readUnsafe} and * {@link #readAndWriteUnsafe} that take a cursor and close it after use. */ public static final class UnsafeCursor implements Closeable { public Buffer buffer; public boolean readWrite; private Segment segment; public long offset = -1L; public byte[] data; public int start = -1; public int end = -1; /** * Seeks to the next range of bytes, advancing the offset by {@code end - start}. Returns the * size of the readable range (at least 1), or -1 if we have reached the end of the buffer and * there are no more bytes to read. */ public final int next() { if (offset == buffer.size) throw new IllegalStateException(); if (offset == -1L) return seek(0L); return seek(offset + (end - start)); } /** * Reposition the cursor so that the data at {@code offset} is readable at {@code data[start]}. * Returns the number of bytes readable in {@code data} (at least 1), or -1 if there are no data * to read. */ public final int seek(long offset) { if (offset < -1 || offset > buffer.size) { throw new ArrayIndexOutOfBoundsException( String.format("offset=%s > size=%s", offset, buffer.size)); } if (offset == -1 || offset == buffer.size) { this.segment = null; this.offset = offset; this.data = null; this.start = -1; this.end = -1; return -1; } // Navigate to the segment that contains `offset`. Start from our current segment if possible. long min = 0L; long max = buffer.size; Segment head = buffer.head; Segment tail = buffer.head; if (this.segment != null) { long segmentOffset = this.offset - (this.start - this.segment.pos); if (segmentOffset > offset) { // Set the cursor segment to be the 'end' max = segmentOffset; tail = this.segment; } else { // Set the cursor segment to be the 'beginning' min = segmentOffset; head = this.segment; } } Segment next; long nextOffset; if (max - offset > offset - min) { // Start at the 'beginning' and search forwards next = head; nextOffset = min; while (offset >= nextOffset + (next.limit - next.pos)) { nextOffset += (next.limit - next.pos); next = next.next; } } else { // Start at the 'end' and search backwards next = tail; nextOffset = max; while (nextOffset > offset) { next = next.prev; nextOffset -= (next.limit - next.pos); } } // If we're going to write and our segment is shared, swap it for a read-write one. if (readWrite && next.shared) { Segment unsharedNext = next.unsharedCopy(); if (buffer.head == next) { buffer.head = unsharedNext; } next = next.push(unsharedNext); next.prev.pop(); } // Update this cursor to the requested offset within the found segment. this.segment = next; this.offset = offset; this.data = next.data; this.start = next.pos + (int) (offset - nextOffset); this.end = next.limit; return end - start; } /** * Change the size of the buffer so that it equals {@code newSize} by either adding new * capacity at the end or truncating the buffer at the end. Newly added capacity may span * multiple segments. * *

As a side-effect this cursor will {@link #seek seek}. If the buffer is being enlarged it * will move {@link #offset} to the first byte of newly-added capacity. This is the size of the * buffer prior to the {@code resizeBuffer()} call. If the buffer is being shrunk it will move * {@link #offset} to the end of the buffer. * *

Warning: it is the caller’s responsibility to write new data to every byte of the * newly-allocated capacity. Failure to do so may cause serious security problems as the data * in the returned buffers is not zero filled. Buffers may contain dirty pooled segments that * hold very sensitive data from other parts of the current process. * * @return the previous size of the buffer. */ public final long resizeBuffer(long newSize) { if (buffer == null) { throw new IllegalStateException("not attached to a buffer"); } if (!readWrite) { throw new IllegalStateException("resizeBuffer() only permitted for read/write buffers"); } long oldSize = buffer.size; if (newSize <= oldSize) { if (newSize < 0) { throw new IllegalArgumentException("newSize < 0: " + newSize); } // Shrink the buffer by either shrinking segments or removing them. for (long bytesToSubtract = oldSize - newSize; bytesToSubtract > 0; ) { Segment tail = buffer.head.prev; int tailSize = tail.limit - tail.pos; if (tailSize <= bytesToSubtract) { buffer.head = tail.pop(); SegmentPool.recycle(tail); bytesToSubtract -= tailSize; } else { tail.limit -= bytesToSubtract; break; } } // Seek to the end. this.segment = null; this.offset = newSize; this.data = null; this.start = -1; this.end = -1; } else if (newSize > oldSize) { // Enlarge the buffer by either enlarging segments or adding them. boolean needsToSeek = true; for (long bytesToAdd = newSize - oldSize; bytesToAdd > 0; ) { Segment tail = buffer.writableSegment(1); int segmentBytesToAdd = (int) Math.min(bytesToAdd, Segment.SIZE - tail.limit); tail.limit += segmentBytesToAdd; bytesToAdd -= segmentBytesToAdd; // If this is the first segment we're adding, seek to it. if (needsToSeek) { this.segment = tail; this.offset = oldSize; this.data = tail.data; this.start = tail.limit - segmentBytesToAdd; this.end = tail.limit; needsToSeek = false; } } } buffer.size = newSize; return oldSize; } /** * Grow the buffer by adding a contiguous range of capacity in a single * segment. This adds at least {@code minByteCount} bytes but may add up to a full segment of * additional capacity. * *

As a side-effect this cursor will {@link #seek seek}. It will move {@link #offset} to the * first byte of newly-added capacity. This is the size of the buffer prior to the {@code * expandBuffer()} call. * *

If {@code minByteCount} bytes are available in the buffer's current tail segment that will * be used; otherwise another segment will be allocated and appended. In either case this * returns the number of bytes of capacity added to this buffer. * *

Warning: it is the caller’s responsibility to either write new data to every byte of the * newly-allocated capacity, or to {@link #resizeBuffer shrink} the buffer to the data written. * Failure to do so may cause serious security problems as the data in the returned buffers is * not zero filled. Buffers may contain dirty pooled segments that hold very sensitive data from * other parts of the current process. * * @param minByteCount the size of the contiguous capacity. Must be positive and not greater * than the capacity size of a single segment (8 KiB). * @return the number of bytes expanded by. Not less than {@code minByteCount}. */ public final long expandBuffer(int minByteCount) { if (minByteCount <= 0) { throw new IllegalArgumentException("minByteCount <= 0: " + minByteCount); } if (minByteCount > Segment.SIZE) { throw new IllegalArgumentException("minByteCount > Segment.SIZE: " + minByteCount); } if (buffer == null) { throw new IllegalStateException("not attached to a buffer"); } if (!readWrite) { throw new IllegalStateException("expandBuffer() only permitted for read/write buffers"); } long oldSize = buffer.size; Segment tail = buffer.writableSegment(minByteCount); int result = Segment.SIZE - tail.limit; tail.limit = Segment.SIZE; buffer.size = oldSize + result; // Seek to the old size. this.segment = tail; this.offset = oldSize; this.data = tail.data; this.start = Segment.SIZE - result; this.end = Segment.SIZE; return result; } @Override public void close() { // TODO(jwilson): use edit counts or other information to track unexpected changes? if (buffer == null) { throw new IllegalStateException("not attached to a buffer"); } buffer = null; segment = null; offset = -1L; data = null; start = -1; end = -1; } } } okio-okio-parent-1.16.0/okio/src/main/java/okio/BufferedSink.java000066400000000000000000000301461335667376100245630ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; import java.io.OutputStream; import java.nio.channels.WritableByteChannel; import java.nio.charset.Charset; /** * A sink that keeps a buffer internally so that callers can do small writes * without a performance penalty. */ public interface BufferedSink extends Sink, WritableByteChannel { /** Returns this sink's internal buffer. */ Buffer buffer(); BufferedSink write(ByteString byteString) throws IOException; /** * Like {@link OutputStream#write(byte[])}, this writes a complete byte array to * this sink. */ BufferedSink write(byte[] source) throws IOException; /** * Like {@link OutputStream#write(byte[], int, int)}, this writes {@code byteCount} * bytes of {@code source}, starting at {@code offset}. */ BufferedSink write(byte[] source, int offset, int byteCount) throws IOException; /** * Removes all bytes from {@code source} and appends them to this sink. Returns the * number of bytes read which will be 0 if {@code source} is exhausted. */ long writeAll(Source source) throws IOException; /** Removes {@code byteCount} bytes from {@code source} and appends them to this sink. */ BufferedSink write(Source source, long byteCount) throws IOException; /** * Encodes {@code string} in UTF-8 and writes it to this sink.

{@code
   *
   *   Buffer buffer = new Buffer();
   *   buffer.writeUtf8("Uh uh uh!");
   *   buffer.writeByte(' ');
   *   buffer.writeUtf8("You didn't say the magic word!");
   *
   *   assertEquals("Uh uh uh! You didn't say the magic word!", buffer.readUtf8());
   * }
*/ BufferedSink writeUtf8(String string) throws IOException; /** * Encodes the characters at {@code beginIndex} up to {@code endIndex} from {@code string} in * UTF-8 and writes it to this sink.
{@code
   *
   *   Buffer buffer = new Buffer();
   *   buffer.writeUtf8("I'm a hacker!\n", 6, 12);
   *   buffer.writeByte(' ');
   *   buffer.writeUtf8("That's what I said: you're a nerd.\n", 29, 33);
   *   buffer.writeByte(' ');
   *   buffer.writeUtf8("I prefer to be called a hacker!\n", 24, 31);
   *
   *   assertEquals("hacker nerd hacker!", buffer.readUtf8());
   * }
*/ BufferedSink writeUtf8(String string, int beginIndex, int endIndex) throws IOException; /** Encodes {@code codePoint} in UTF-8 and writes it to this sink. */ BufferedSink writeUtf8CodePoint(int codePoint) throws IOException; /** Encodes {@code string} in {@code charset} and writes it to this sink. */ BufferedSink writeString(String string, Charset charset) throws IOException; /** * Encodes the characters at {@code beginIndex} up to {@code endIndex} from {@code string} in * {@code charset} and writes it to this sink. */ BufferedSink writeString(String string, int beginIndex, int endIndex, Charset charset) throws IOException; /** Writes a byte to this sink. */ BufferedSink writeByte(int b) throws IOException; /** * Writes a big-endian short to this sink using two bytes.
{@code
   *
   *   Buffer buffer = new Buffer();
   *   buffer.writeShort(32767);
   *   buffer.writeShort(15);
   *
   *   assertEquals(4, buffer.size());
   *   assertEquals((byte) 0x7f, buffer.readByte());
   *   assertEquals((byte) 0xff, buffer.readByte());
   *   assertEquals((byte) 0x00, buffer.readByte());
   *   assertEquals((byte) 0x0f, buffer.readByte());
   *   assertEquals(0, buffer.size());
   * }
*/ BufferedSink writeShort(int s) throws IOException; /** * Writes a little-endian short to this sink using two bytes.
{@code
   *
   *   Buffer buffer = new Buffer();
   *   buffer.writeShortLe(32767);
   *   buffer.writeShortLe(15);
   *
   *   assertEquals(4, buffer.size());
   *   assertEquals((byte) 0xff, buffer.readByte());
   *   assertEquals((byte) 0x7f, buffer.readByte());
   *   assertEquals((byte) 0x0f, buffer.readByte());
   *   assertEquals((byte) 0x00, buffer.readByte());
   *   assertEquals(0, buffer.size());
   * }
*/ BufferedSink writeShortLe(int s) throws IOException; /** * Writes a big-endian int to this sink using four bytes.
{@code
   *
   *   Buffer buffer = new Buffer();
   *   buffer.writeInt(2147483647);
   *   buffer.writeInt(15);
   *
   *   assertEquals(8, buffer.size());
   *   assertEquals((byte) 0x7f, buffer.readByte());
   *   assertEquals((byte) 0xff, buffer.readByte());
   *   assertEquals((byte) 0xff, buffer.readByte());
   *   assertEquals((byte) 0xff, buffer.readByte());
   *   assertEquals((byte) 0x00, buffer.readByte());
   *   assertEquals((byte) 0x00, buffer.readByte());
   *   assertEquals((byte) 0x00, buffer.readByte());
   *   assertEquals((byte) 0x0f, buffer.readByte());
   *   assertEquals(0, buffer.size());
   * }
*/ BufferedSink writeInt(int i) throws IOException; /** * Writes a little-endian int to this sink using four bytes.
{@code
   *
   *   Buffer buffer = new Buffer();
   *   buffer.writeIntLe(2147483647);
   *   buffer.writeIntLe(15);
   *
   *   assertEquals(8, buffer.size());
   *   assertEquals((byte) 0xff, buffer.readByte());
   *   assertEquals((byte) 0xff, buffer.readByte());
   *   assertEquals((byte) 0xff, buffer.readByte());
   *   assertEquals((byte) 0x7f, buffer.readByte());
   *   assertEquals((byte) 0x0f, buffer.readByte());
   *   assertEquals((byte) 0x00, buffer.readByte());
   *   assertEquals((byte) 0x00, buffer.readByte());
   *   assertEquals((byte) 0x00, buffer.readByte());
   *   assertEquals(0, buffer.size());
   * }
*/ BufferedSink writeIntLe(int i) throws IOException; /** * Writes a big-endian long to this sink using eight bytes.
{@code
   *
   *   Buffer buffer = new Buffer();
   *   buffer.writeLong(9223372036854775807L);
   *   buffer.writeLong(15);
   *
   *   assertEquals(16, buffer.size());
   *   assertEquals((byte) 0x7f, buffer.readByte());
   *   assertEquals((byte) 0xff, buffer.readByte());
   *   assertEquals((byte) 0xff, buffer.readByte());
   *   assertEquals((byte) 0xff, buffer.readByte());
   *   assertEquals((byte) 0xff, buffer.readByte());
   *   assertEquals((byte) 0xff, buffer.readByte());
   *   assertEquals((byte) 0xff, buffer.readByte());
   *   assertEquals((byte) 0xff, buffer.readByte());
   *   assertEquals((byte) 0x00, buffer.readByte());
   *   assertEquals((byte) 0x00, buffer.readByte());
   *   assertEquals((byte) 0x00, buffer.readByte());
   *   assertEquals((byte) 0x00, buffer.readByte());
   *   assertEquals((byte) 0x00, buffer.readByte());
   *   assertEquals((byte) 0x00, buffer.readByte());
   *   assertEquals((byte) 0x00, buffer.readByte());
   *   assertEquals((byte) 0x0f, buffer.readByte());
   *   assertEquals(0, buffer.size());
   * }
*/ BufferedSink writeLong(long v) throws IOException; /** * Writes a little-endian long to this sink using eight bytes.
{@code
   *
   *   Buffer buffer = new Buffer();
   *   buffer.writeLongLe(9223372036854775807L);
   *   buffer.writeLongLe(15);
   *
   *   assertEquals(16, buffer.size());
   *   assertEquals((byte) 0xff, buffer.readByte());
   *   assertEquals((byte) 0xff, buffer.readByte());
   *   assertEquals((byte) 0xff, buffer.readByte());
   *   assertEquals((byte) 0xff, buffer.readByte());
   *   assertEquals((byte) 0xff, buffer.readByte());
   *   assertEquals((byte) 0xff, buffer.readByte());
   *   assertEquals((byte) 0xff, buffer.readByte());
   *   assertEquals((byte) 0x7f, buffer.readByte());
   *   assertEquals((byte) 0x0f, buffer.readByte());
   *   assertEquals((byte) 0x00, buffer.readByte());
   *   assertEquals((byte) 0x00, buffer.readByte());
   *   assertEquals((byte) 0x00, buffer.readByte());
   *   assertEquals((byte) 0x00, buffer.readByte());
   *   assertEquals((byte) 0x00, buffer.readByte());
   *   assertEquals((byte) 0x00, buffer.readByte());
   *   assertEquals((byte) 0x00, buffer.readByte());
   *   assertEquals(0, buffer.size());
   * }
*/ BufferedSink writeLongLe(long v) throws IOException; /** * Writes a long to this sink in signed decimal form (i.e., as a string in base 10).
{@code
   *
   *   Buffer buffer = new Buffer();
   *   buffer.writeDecimalLong(8675309L);
   *   buffer.writeByte(' ');
   *   buffer.writeDecimalLong(-123L);
   *   buffer.writeByte(' ');
   *   buffer.writeDecimalLong(1L);
   *
   *   assertEquals("8675309 -123 1", buffer.readUtf8());
   * }
*/ BufferedSink writeDecimalLong(long v) throws IOException; /** * Writes a long to this sink in hexadecimal form (i.e., as a string in base 16).
{@code
   *
   *   Buffer buffer = new Buffer();
   *   buffer.writeHexadecimalUnsignedLong(65535L);
   *   buffer.writeByte(' ');
   *   buffer.writeHexadecimalUnsignedLong(0xcafebabeL);
   *   buffer.writeByte(' ');
   *   buffer.writeHexadecimalUnsignedLong(0x10L);
   *
   *   assertEquals("ffff cafebabe 10", buffer.readUtf8());
   * }
*/ BufferedSink writeHexadecimalUnsignedLong(long v) throws IOException; /** * Writes all buffered data to the underlying sink, if one exists. Then that sink is recursively * flushed which pushes data as far as possible towards its ultimate destination. Typically that * destination is a network socket or file.
{@code
   *
   *   BufferedSink b0 = new Buffer();
   *   BufferedSink b1 = Okio.buffer(b0);
   *   BufferedSink b2 = Okio.buffer(b1);
   *
   *   b2.writeUtf8("hello");
   *   assertEquals(5, b2.buffer().size());
   *   assertEquals(0, b1.buffer().size());
   *   assertEquals(0, b0.buffer().size());
   *
   *   b2.flush();
   *   assertEquals(0, b2.buffer().size());
   *   assertEquals(0, b1.buffer().size());
   *   assertEquals(5, b0.buffer().size());
   * }
*/ @Override void flush() throws IOException; /** * Writes all buffered data to the underlying sink, if one exists. Like {@link #flush}, but * weaker. Call this before this buffered sink goes out of scope so that its data can reach its * destination.
{@code
   *
   *   BufferedSink b0 = new Buffer();
   *   BufferedSink b1 = Okio.buffer(b0);
   *   BufferedSink b2 = Okio.buffer(b1);
   *
   *   b2.writeUtf8("hello");
   *   assertEquals(5, b2.buffer().size());
   *   assertEquals(0, b1.buffer().size());
   *   assertEquals(0, b0.buffer().size());
   *
   *   b2.emit();
   *   assertEquals(0, b2.buffer().size());
   *   assertEquals(5, b1.buffer().size());
   *   assertEquals(0, b0.buffer().size());
   *
   *   b1.emit();
   *   assertEquals(0, b2.buffer().size());
   *   assertEquals(0, b1.buffer().size());
   *   assertEquals(5, b0.buffer().size());
   * }
*/ BufferedSink emit() throws IOException; /** * Writes complete segments to the underlying sink, if one exists. Like {@link #flush}, but * weaker. Use this to limit the memory held in the buffer to a single segment. Typically * application code will not need to call this: it is only necessary when application code writes * directly to this {@linkplain #buffer() sink's buffer}.
{@code
   *
   *   BufferedSink b0 = new Buffer();
   *   BufferedSink b1 = Okio.buffer(b0);
   *   BufferedSink b2 = Okio.buffer(b1);
   *
   *   b2.buffer().write(new byte[20_000]);
   *   assertEquals(20_000, b2.buffer().size());
   *   assertEquals(     0, b1.buffer().size());
   *   assertEquals(     0, b0.buffer().size());
   *
   *   b2.emitCompleteSegments();
   *   assertEquals( 3_616, b2.buffer().size());
   *   assertEquals(     0, b1.buffer().size());
   *   assertEquals(16_384, b0.buffer().size()); // This example assumes 8192 byte segments.
   * }
*/ BufferedSink emitCompleteSegments() throws IOException; /** Returns an output stream that writes to this sink. */ OutputStream outputStream(); } okio-okio-parent-1.16.0/okio/src/main/java/okio/BufferedSource.java000066400000000000000000000512501335667376100251160ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; import java.io.InputStream; import java.nio.channels.ReadableByteChannel; import java.nio.charset.Charset; import javax.annotation.Nullable; /** * A source that keeps a buffer internally so that callers can do small reads without a performance * penalty. It also allows clients to read ahead, buffering as much as necessary before consuming * input. */ public interface BufferedSource extends Source, ReadableByteChannel { /** * Returns this source's internal buffer. * * @deprecated use getBuffer() instead. */ @Deprecated Buffer buffer(); /** This source's internal buffer. */ Buffer getBuffer(); /** * Returns true if there are no more bytes in this source. This will block until there are bytes * to read or the source is definitely exhausted. */ boolean exhausted() throws IOException; /** * Returns when the buffer contains at least {@code byteCount} bytes. Throws an * {@link java.io.EOFException} if the source is exhausted before the required bytes can be read. */ void require(long byteCount) throws IOException; /** * Returns true when the buffer contains at least {@code byteCount} bytes, expanding it as * necessary. Returns false if the source is exhausted before the requested bytes can be read. */ boolean request(long byteCount) throws IOException; /** Removes a byte from this source and returns it. */ byte readByte() throws IOException; /** * Removes two bytes from this source and returns a big-endian short.
{@code
   *
   *   Buffer buffer = new Buffer()
   *       .writeByte(0x7f)
   *       .writeByte(0xff)
   *       .writeByte(0x00)
   *       .writeByte(0x0f);
   *   assertEquals(4, buffer.size());
   *
   *   assertEquals(32767, buffer.readShort());
   *   assertEquals(2, buffer.size());
   *
   *   assertEquals(15, buffer.readShort());
   *   assertEquals(0, buffer.size());
   * }
*/ short readShort() throws IOException; /** * Removes two bytes from this source and returns a little-endian short.
{@code
   *
   *   Buffer buffer = new Buffer()
   *       .writeByte(0xff)
   *       .writeByte(0x7f)
   *       .writeByte(0x0f)
   *       .writeByte(0x00);
   *   assertEquals(4, buffer.size());
   *
   *   assertEquals(32767, buffer.readShortLe());
   *   assertEquals(2, buffer.size());
   *
   *   assertEquals(15, buffer.readShortLe());
   *   assertEquals(0, buffer.size());
   * }
*/ short readShortLe() throws IOException; /** * Removes four bytes from this source and returns a big-endian int.
{@code
   *
   *   Buffer buffer = new Buffer()
   *       .writeByte(0x7f)
   *       .writeByte(0xff)
   *       .writeByte(0xff)
   *       .writeByte(0xff)
   *       .writeByte(0x00)
   *       .writeByte(0x00)
   *       .writeByte(0x00)
   *       .writeByte(0x0f);
   *   assertEquals(8, buffer.size());
   *
   *   assertEquals(2147483647, buffer.readInt());
   *   assertEquals(4, buffer.size());
   *
   *   assertEquals(15, buffer.readInt());
   *   assertEquals(0, buffer.size());
   * }
*/ int readInt() throws IOException; /** * Removes four bytes from this source and returns a little-endian int.
{@code
   *
   *   Buffer buffer = new Buffer()
   *       .writeByte(0xff)
   *       .writeByte(0xff)
   *       .writeByte(0xff)
   *       .writeByte(0x7f)
   *       .writeByte(0x0f)
   *       .writeByte(0x00)
   *       .writeByte(0x00)
   *       .writeByte(0x00);
   *   assertEquals(8, buffer.size());
   *
   *   assertEquals(2147483647, buffer.readIntLe());
   *   assertEquals(4, buffer.size());
   *
   *   assertEquals(15, buffer.readIntLe());
   *   assertEquals(0, buffer.size());
   * }
*/ int readIntLe() throws IOException; /** * Removes eight bytes from this source and returns a big-endian long.
{@code
   *
   *   Buffer buffer = new Buffer()
   *       .writeByte(0x7f)
   *       .writeByte(0xff)
   *       .writeByte(0xff)
   *       .writeByte(0xff)
   *       .writeByte(0xff)
   *       .writeByte(0xff)
   *       .writeByte(0xff)
   *       .writeByte(0xff)
   *       .writeByte(0x00)
   *       .writeByte(0x00)
   *       .writeByte(0x00)
   *       .writeByte(0x00)
   *       .writeByte(0x00)
   *       .writeByte(0x00)
   *       .writeByte(0x00)
   *       .writeByte(0x0f);
   *   assertEquals(16, buffer.size());
   *
   *   assertEquals(9223372036854775807L, buffer.readLong());
   *   assertEquals(8, buffer.size());
   *
   *   assertEquals(15, buffer.readLong());
   *   assertEquals(0, buffer.size());
   * }
*/ long readLong() throws IOException; /** * Removes eight bytes from this source and returns a little-endian long.
{@code
   *
   *   Buffer buffer = new Buffer()
   *       .writeByte(0xff)
   *       .writeByte(0xff)
   *       .writeByte(0xff)
   *       .writeByte(0xff)
   *       .writeByte(0xff)
   *       .writeByte(0xff)
   *       .writeByte(0xff)
   *       .writeByte(0x7f)
   *       .writeByte(0x0f)
   *       .writeByte(0x00)
   *       .writeByte(0x00)
   *       .writeByte(0x00)
   *       .writeByte(0x00)
   *       .writeByte(0x00)
   *       .writeByte(0x00)
   *       .writeByte(0x00);
   *   assertEquals(16, buffer.size());
   *
   *   assertEquals(9223372036854775807L, buffer.readLongLe());
   *   assertEquals(8, buffer.size());
   *
   *   assertEquals(15, buffer.readLongLe());
   *   assertEquals(0, buffer.size());
   * }
*/ long readLongLe() throws IOException; /** * Reads a long from this source in signed decimal form (i.e., as a string in base 10 with * optional leading '-'). This will iterate until a non-digit character is found.
{@code
   *
   *   Buffer buffer = new Buffer()
   *       .writeUtf8("8675309 -123 00001");
   *
   *   assertEquals(8675309L, buffer.readDecimalLong());
   *   assertEquals(' ', buffer.readByte());
   *   assertEquals(-123L, buffer.readDecimalLong());
   *   assertEquals(' ', buffer.readByte());
   *   assertEquals(1L, buffer.readDecimalLong());
   * }
* * @throws NumberFormatException if the found digits do not fit into a {@code long} or a decimal * number was not present. */ long readDecimalLong() throws IOException; /** * Reads a long form this source in hexadecimal form (i.e., as a string in base 16). This will * iterate until a non-hexadecimal character is found.
{@code
   *
   *   Buffer buffer = new Buffer()
   *       .writeUtf8("ffff CAFEBABE 10");
   *
   *   assertEquals(65535L, buffer.readHexadecimalUnsignedLong());
   *   assertEquals(' ', buffer.readByte());
   *   assertEquals(0xcafebabeL, buffer.readHexadecimalUnsignedLong());
   *   assertEquals(' ', buffer.readByte());
   *   assertEquals(0x10L, buffer.readHexadecimalUnsignedLong());
   * }
* * @throws NumberFormatException if the found hexadecimal does not fit into a {@code long} or * hexadecimal was not found. */ long readHexadecimalUnsignedLong() throws IOException; /** * Reads and discards {@code byteCount} bytes from this source. Throws an * {@link java.io.EOFException} if the source is exhausted before the * requested bytes can be skipped. */ void skip(long byteCount) throws IOException; /** Removes all bytes bytes from this and returns them as a byte string. */ ByteString readByteString() throws IOException; /** Removes {@code byteCount} bytes from this and returns them as a byte string. */ ByteString readByteString(long byteCount) throws IOException; /** * Finds the first string in {@code options} that is a prefix of this buffer, consumes it from * this buffer, and returns its index. If no byte string in {@code options} is a prefix of this * buffer this returns -1 and no bytes are consumed. * *

This can be used as an alternative to {@link #readByteString} or even {@link #readUtf8} if * the set of expected values is known in advance.

{@code
   *
   *   Options FIELDS = Options.of(
   *       ByteString.encodeUtf8("depth="),
   *       ByteString.encodeUtf8("height="),
   *       ByteString.encodeUtf8("width="));
   *
   *   Buffer buffer = new Buffer()
   *       .writeUtf8("width=640\n")
   *       .writeUtf8("height=480\n");
   *
   *   assertEquals(2, buffer.select(FIELDS));
   *   assertEquals(640, buffer.readDecimalLong());
   *   assertEquals('\n', buffer.readByte());
   *   assertEquals(1, buffer.select(FIELDS));
   *   assertEquals(480, buffer.readDecimalLong());
   *   assertEquals('\n', buffer.readByte());
   * }
*/ int select(Options options) throws IOException; /** Removes all bytes from this and returns them as a byte array. */ byte[] readByteArray() throws IOException; /** Removes {@code byteCount} bytes from this and returns them as a byte array. */ byte[] readByteArray(long byteCount) throws IOException; /** * Removes up to {@code sink.length} bytes from this and copies them into {@code sink}. Returns * the number of bytes read, or -1 if this source is exhausted. */ int read(byte[] sink) throws IOException; /** * Removes exactly {@code sink.length} bytes from this and copies them into {@code sink}. Throws * an {@link java.io.EOFException} if the requested number of bytes cannot be read. */ void readFully(byte[] sink) throws IOException; /** * Removes up to {@code byteCount} bytes from this and copies them into {@code sink} at {@code * offset}. Returns the number of bytes read, or -1 if this source is exhausted. */ int read(byte[] sink, int offset, int byteCount) throws IOException; /** * Removes exactly {@code byteCount} bytes from this and appends them to {@code sink}. Throws an * {@link java.io.EOFException} if the requested number of bytes cannot be read. */ void readFully(Buffer sink, long byteCount) throws IOException; /** * Removes all bytes from this and appends them to {@code sink}. Returns the total number of bytes * written to {@code sink} which will be 0 if this is exhausted. */ long readAll(Sink sink) throws IOException; /** * Removes all bytes from this, decodes them as UTF-8, and returns the string. Returns the empty * string if this source is empty.
{@code
   *
   *   Buffer buffer = new Buffer()
   *       .writeUtf8("Uh uh uh!")
   *       .writeByte(' ')
   *       .writeUtf8("You didn't say the magic word!");
   *
   *   assertEquals("Uh uh uh! You didn't say the magic word!", buffer.readUtf8());
   *   assertEquals(0, buffer.size());
   *
   *   assertEquals("", buffer.readUtf8());
   *   assertEquals(0, buffer.size());
   * }
*/ String readUtf8() throws IOException; /** * Removes {@code byteCount} bytes from this, decodes them as UTF-8, and returns the string. *
{@code
   *
   *   Buffer buffer = new Buffer()
   *       .writeUtf8("Uh uh uh!")
   *       .writeByte(' ')
   *       .writeUtf8("You didn't say the magic word!");
   *   assertEquals(40, buffer.size());
   *
   *   assertEquals("Uh uh uh! You ", buffer.readUtf8(14));
   *   assertEquals(26, buffer.size());
   *
   *   assertEquals("didn't say the", buffer.readUtf8(14));
   *   assertEquals(12, buffer.size());
   *
   *   assertEquals(" magic word!", buffer.readUtf8(12));
   *   assertEquals(0, buffer.size());
   * }
*/ String readUtf8(long byteCount) throws IOException; /** * Removes and returns characters up to but not including the next line break. A line break is * either {@code "\n"} or {@code "\r\n"}; these characters are not included in the result. *
{@code
   *
   *   Buffer buffer = new Buffer()
   *       .writeUtf8("I'm a hacker!\n")
   *       .writeUtf8("That's what I said: you're a nerd.\n")
   *       .writeUtf8("I prefer to be called a hacker!\n");
   *   assertEquals(81, buffer.size());
   *
   *   assertEquals("I'm a hacker!", buffer.readUtf8Line());
   *   assertEquals(67, buffer.size());
   *
   *   assertEquals("That's what I said: you're a nerd.", buffer.readUtf8Line());
   *   assertEquals(32, buffer.size());
   *
   *   assertEquals("I prefer to be called a hacker!", buffer.readUtf8Line());
   *   assertEquals(0, buffer.size());
   *
   *   assertEquals(null, buffer.readUtf8Line());
   *   assertEquals(0, buffer.size());
   * }
* *

On the end of the stream this method returns null, just like {@link * java.io.BufferedReader}. If the source doesn't end with a line break then an implicit line * break is assumed. Null is returned once the source is exhausted. Use this for human-generated * data, where a trailing line break is optional. */ @Nullable String readUtf8Line() throws IOException; /** * Removes and returns characters up to but not including the next line break. A line break is * either {@code "\n"} or {@code "\r\n"}; these characters are not included in the result. * *

On the end of the stream this method throws. Every call must consume either * '\r\n' or '\n'. If these characters are absent in the stream, an {@link java.io.EOFException} * is thrown. Use this for machine-generated data where a missing line break implies truncated * input. */ String readUtf8LineStrict() throws IOException; /** * Like {@link #readUtf8LineStrict()}, except this allows the caller to specify the longest * allowed match. Use this to protect against streams that may not include * {@code "\n"} or {@code "\r\n"}. * *

The returned string will have at most {@code limit} UTF-8 bytes, and the maximum number * of bytes scanned is {@code limit + 2}. If {@code limit == 0} this will always throw * an {@code EOFException} because no bytes will be scanned. * *

This method is safe. No bytes are discarded if the match fails, and the caller is free * to try another match:

{@code
   *
   *   Buffer buffer = new Buffer();
   *   buffer.writeUtf8("12345\r\n");
   *
   *   // This will throw! There must be \r\n or \n at the limit or before it.
   *   buffer.readUtf8LineStrict(4);
   *
   *   // No bytes have been consumed so the caller can retry.
   *   assertEquals("12345", buffer.readUtf8LineStrict(5));
   * }
*/ String readUtf8LineStrict(long limit) throws IOException; /** * Removes and returns a single UTF-8 code point, reading between 1 and 4 bytes as necessary. * *

If this source is exhausted before a complete code point can be read, this throws an {@link * java.io.EOFException} and consumes no input. * *

If this source doesn't start with a properly-encoded UTF-8 code point, this method will * remove 1 or more non-UTF-8 bytes and return the replacement character ({@code U+FFFD}). This * covers encoding problems (the input is not properly-encoded UTF-8), characters out of range * (beyond the 0x10ffff limit of Unicode), code points for UTF-16 surrogates (U+d800..U+dfff) and * overlong encodings (such as {@code 0xc080} for the NUL character in modified UTF-8). */ int readUtf8CodePoint() throws IOException; /** Removes all bytes from this, decodes them as {@code charset}, and returns the string. */ String readString(Charset charset) throws IOException; /** * Removes {@code byteCount} bytes from this, decodes them as {@code charset}, and returns the * string. */ String readString(long byteCount, Charset charset) throws IOException; /** Equivalent to {@link #indexOf(byte, long) indexOf(b, 0)}. */ long indexOf(byte b) throws IOException; /** * Returns the index of the first {@code b} in the buffer at or after {@code fromIndex}. This * expands the buffer as necessary until {@code b} is found. This reads an unbounded number of * bytes into the buffer. Returns -1 if the stream is exhausted before the requested byte is * found.

{@code
   *
   *   Buffer buffer = new Buffer();
   *   buffer.writeUtf8("Don't move! He can't see us if we don't move.");
   *
   *   byte m = 'm';
   *   assertEquals(6,  buffer.indexOf(m));
   *   assertEquals(40, buffer.indexOf(m, 12));
   * }
*/ long indexOf(byte b, long fromIndex) throws IOException; /** * Returns the index of {@code b} if it is found in the range of {@code fromIndex} inclusive * to {@code toIndex} exclusive. If {@code b} isn't found, or if {@code fromIndex == toIndex}, * then -1 is returned. * *

The scan terminates at either {@code toIndex} or the end of the buffer, whichever comes * first. The maximum number of bytes scanned is {@code toIndex-fromIndex}. */ long indexOf(byte b, long fromIndex, long toIndex) throws IOException; /** Equivalent to {@link #indexOf(ByteString, long) indexOf(bytes, 0)}. */ long indexOf(ByteString bytes) throws IOException; /** * Returns the index of the first match for {@code bytes} in the buffer at or after {@code * fromIndex}. This expands the buffer as necessary until {@code bytes} is found. This reads an * unbounded number of bytes into the buffer. Returns -1 if the stream is exhausted before the * requested bytes are found.

{@code
   *
   *   ByteString MOVE = ByteString.encodeUtf8("move");
   *
   *   Buffer buffer = new Buffer();
   *   buffer.writeUtf8("Don't move! He can't see us if we don't move.");
   *
   *   assertEquals(6,  buffer.indexOf(MOVE));
   *   assertEquals(40, buffer.indexOf(MOVE, 12));
   * }
*/ long indexOf(ByteString bytes, long fromIndex) throws IOException; /** Equivalent to {@link #indexOfElement(ByteString, long) indexOfElement(targetBytes, 0)}. */ long indexOfElement(ByteString targetBytes) throws IOException; /** * Returns the first index in this buffer that is at or after {@code fromIndex} and that contains * any of the bytes in {@code targetBytes}. This expands the buffer as necessary until a target * byte is found. This reads an unbounded number of bytes into the buffer. Returns -1 if the * stream is exhausted before the requested byte is found.
{@code
   *
   *   ByteString ANY_VOWEL = ByteString.encodeUtf8("AEOIUaeoiu");
   *
   *   Buffer buffer = new Buffer();
   *   buffer.writeUtf8("Dr. Alan Grant");
   *
   *   assertEquals(4,  buffer.indexOfElement(ANY_VOWEL));    // 'A' in 'Alan'.
   *   assertEquals(11, buffer.indexOfElement(ANY_VOWEL, 9)); // 'a' in 'Grant'.
   * }
*/ long indexOfElement(ByteString targetBytes, long fromIndex) throws IOException; /** * Returns true if the bytes at {@code offset} in this source equal {@code bytes}. This expands * the buffer as necessary until a byte does not match, all bytes are matched, or if the stream * is exhausted before enough bytes could determine a match.
{@code
   *
   *   ByteString simonSays = ByteString.encodeUtf8("Simon says:");
   *
   *   Buffer standOnOneLeg = new Buffer().writeUtf8("Simon says: Stand on one leg.");
   *   assertTrue(standOnOneLeg.rangeEquals(0, simonSays));
   *
   *   Buffer payMeMoney = new Buffer().writeUtf8("Pay me $1,000,000.");
   *   assertFalse(payMeMoney.rangeEquals(0, simonSays));
   * }
*/ boolean rangeEquals(long offset, ByteString bytes) throws IOException; /** * Returns true if {@code byteCount} bytes at {@code offset} in this source equal {@code bytes} * at {@code bytesOffset}. This expands the buffer as necessary until a byte does not match, all * bytes are matched, or if the stream is exhausted before enough bytes could determine a match. */ boolean rangeEquals(long offset, ByteString bytes, int bytesOffset, int byteCount) throws IOException; /** * Returns a new {@code BufferedSource} that can read data from this {@code BufferedSource} * without consuming it. The returned source becomes invalid once this source is next read or * closed. * * For example, we can use {@code peek()} to lookahead and read the same data multiple times. * *
 {@code
   *
   *   Buffer buffer = new Buffer();
   *   buffer.writeUtf8("abcdefghi");
   *
   *   buffer.readUtf8(3) // returns "abc", buffer contains "defghi"
   *
   *   BufferedSource peek = buffer.peek();
   *   peek.readUtf8(3); // returns "def", buffer contains "defghi"
   *   peek.readUtf8(3); // returns "ghi", buffer contains "defghi"
   *
   *   buffer.readUtf8(3); // returns "def", buffer contains "ghi"
   * }
*/ BufferedSource peek(); /** Returns an input stream that reads from this source. */ InputStream inputStream(); } okio-okio-parent-1.16.0/okio/src/main/java/okio/ByteString.java000066400000000000000000000437241335667376100243140ustar00rootroot00000000000000/* * Copyright 2014 Square Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.Serializable; import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import javax.annotation.Nullable; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import static okio.Util.arrayRangeEquals; import static okio.Util.checkOffsetAndCount; /** * An immutable sequence of bytes. * *

Byte strings compare lexicographically as a sequence of unsigned bytes. That * is, the byte string {@code ff} sorts after {@code 00}. This is counter to the sort order of the * corresponding bytes, where {@code -1} sorts before {@code 0}. * *

Full disclosure: this class provides untrusted input and output streams with * raw access to the underlying byte array. A hostile stream implementation could keep a reference * to the mutable byte string, violating the immutable guarantee of this class. For this reason a * byte string's immutability guarantee cannot be relied upon for security in applets and other * environments that run both trusted and untrusted code in the same process. */ public class ByteString implements Serializable, Comparable { static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; private static final long serialVersionUID = 1L; /** A singleton empty {@code ByteString}. */ public static final ByteString EMPTY = ByteString.of(); final byte[] data; transient int hashCode; // Lazily computed; 0 if unknown. transient String utf8; // Lazily computed. ByteString(byte[] data) { this.data = data; // Trusted internal constructor doesn't clone data. } /** * Returns a new byte string containing a clone of the bytes of {@code data}. */ public static ByteString of(byte... data) { if (data == null) throw new IllegalArgumentException("data == null"); return new ByteString(data.clone()); } /** * Returns a new byte string containing a copy of {@code byteCount} bytes of {@code data} starting * at {@code offset}. */ public static ByteString of(byte[] data, int offset, int byteCount) { if (data == null) throw new IllegalArgumentException("data == null"); checkOffsetAndCount(data.length, offset, byteCount); byte[] copy = new byte[byteCount]; System.arraycopy(data, offset, copy, 0, byteCount); return new ByteString(copy); } public static ByteString of(ByteBuffer data) { if (data == null) throw new IllegalArgumentException("data == null"); byte[] copy = new byte[data.remaining()]; data.get(copy); return new ByteString(copy); } /** Returns a new byte string containing the {@code UTF-8} bytes of {@code s}. */ public static ByteString encodeUtf8(String s) { if (s == null) throw new IllegalArgumentException("s == null"); ByteString byteString = new ByteString(s.getBytes(Util.UTF_8)); byteString.utf8 = s; return byteString; } /** Returns a new byte string containing the {@code charset}-encoded bytes of {@code s}. */ public static ByteString encodeString(String s, Charset charset) { if (s == null) throw new IllegalArgumentException("s == null"); if (charset == null) throw new IllegalArgumentException("charset == null"); return new ByteString(s.getBytes(charset)); } /** Constructs a new {@code String} by decoding the bytes as {@code UTF-8}. */ public String utf8() { String result = utf8; // We don't care if we double-allocate in racy code. return result != null ? result : (utf8 = new String(data, Util.UTF_8)); } /** Constructs a new {@code String} by decoding the bytes using {@code charset}. */ public String string(Charset charset) { if (charset == null) throw new IllegalArgumentException("charset == null"); return new String(data, charset); } /** * Returns this byte string encoded as Base64. In violation of the * RFC, the returned string does not wrap lines at 76 columns. */ public String base64() { return Base64.encode(data); } /** Returns the 128-bit MD5 hash of this byte string. */ public ByteString md5() { return digest("MD5"); } /** Returns the 160-bit SHA-1 hash of this byte string. */ public ByteString sha1() { return digest("SHA-1"); } /** Returns the 256-bit SHA-256 hash of this byte string. */ public ByteString sha256() { return digest("SHA-256"); } /** Returns the 512-bit SHA-512 hash of this byte string. */ public ByteString sha512() { return digest("SHA-512"); } private ByteString digest(String algorithm) { try { return ByteString.of(MessageDigest.getInstance(algorithm).digest(data)); } catch (NoSuchAlgorithmException e) { throw new AssertionError(e); } } /** Returns the 160-bit SHA-1 HMAC of this byte string. */ public ByteString hmacSha1(ByteString key) { return hmac("HmacSHA1", key); } /** Returns the 256-bit SHA-256 HMAC of this byte string. */ public ByteString hmacSha256(ByteString key) { return hmac("HmacSHA256", key); } /** Returns the 512-bit SHA-512 HMAC of this byte string. */ public ByteString hmacSha512(ByteString key) { return hmac("HmacSHA512", key); } private ByteString hmac(String algorithm, ByteString key) { try { Mac mac = Mac.getInstance(algorithm); mac.init(new SecretKeySpec(key.toByteArray(), algorithm)); return ByteString.of(mac.doFinal(data)); } catch (NoSuchAlgorithmException e) { throw new AssertionError(e); } catch (InvalidKeyException e) { throw new IllegalArgumentException(e); } } /** * Returns this byte string encoded as URL-safe * Base64. */ public String base64Url() { return Base64.encodeUrl(data); } /** * Decodes the Base64-encoded bytes and returns their value as a byte string. * Returns null if {@code base64} is not a Base64-encoded sequence of bytes. */ public static @Nullable ByteString decodeBase64(String base64) { if (base64 == null) throw new IllegalArgumentException("base64 == null"); byte[] decoded = Base64.decode(base64); return decoded != null ? new ByteString(decoded) : null; } /** Returns this byte string encoded in hexadecimal. */ public String hex() { char[] result = new char[data.length * 2]; int c = 0; for (byte b : data) { result[c++] = HEX_DIGITS[(b >> 4) & 0xf]; result[c++] = HEX_DIGITS[b & 0xf]; } return new String(result); } /** Decodes the hex-encoded bytes and returns their value a byte string. */ public static ByteString decodeHex(String hex) { if (hex == null) throw new IllegalArgumentException("hex == null"); if (hex.length() % 2 != 0) throw new IllegalArgumentException("Unexpected hex string: " + hex); byte[] result = new byte[hex.length() / 2]; for (int i = 0; i < result.length; i++) { int d1 = decodeHexDigit(hex.charAt(i * 2)) << 4; int d2 = decodeHexDigit(hex.charAt(i * 2 + 1)); result[i] = (byte) (d1 + d2); } return of(result); } private static int decodeHexDigit(char c) { if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'A' && c <= 'F') return c - 'A' + 10; throw new IllegalArgumentException("Unexpected hex digit: " + c); } /** * Reads {@code count} bytes from {@code in} and returns the result. * * @throws java.io.EOFException if {@code in} has fewer than {@code count} * bytes to read. */ public static ByteString read(InputStream in, int byteCount) throws IOException { if (in == null) throw new IllegalArgumentException("in == null"); if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); byte[] result = new byte[byteCount]; for (int offset = 0, read; offset < byteCount; offset += read) { read = in.read(result, offset, byteCount - offset); if (read == -1) throw new EOFException(); } return new ByteString(result); } /** * Returns a byte string equal to this byte string, but with the bytes 'A' * through 'Z' replaced with the corresponding byte in 'a' through 'z'. * Returns this byte string if it contains no bytes in 'A' through 'Z'. */ public ByteString toAsciiLowercase() { // Search for an uppercase character. If we don't find one, return this. for (int i = 0; i < data.length; i++) { byte c = data[i]; if (c < 'A' || c > 'Z') continue; // If we reach this point, this string is not not lowercase. Create and // return a new byte string. byte[] lowercase = data.clone(); lowercase[i++] = (byte) (c - ('A' - 'a')); for (; i < lowercase.length; i++) { c = lowercase[i]; if (c < 'A' || c > 'Z') continue; lowercase[i] = (byte) (c - ('A' - 'a')); } return new ByteString(lowercase); } return this; } /** * Returns a byte string equal to this byte string, but with the bytes 'a' * through 'z' replaced with the corresponding byte in 'A' through 'Z'. * Returns this byte string if it contains no bytes in 'a' through 'z'. */ public ByteString toAsciiUppercase() { // Search for an lowercase character. If we don't find one, return this. for (int i = 0; i < data.length; i++) { byte c = data[i]; if (c < 'a' || c > 'z') continue; // If we reach this point, this string is not not uppercase. Create and // return a new byte string. byte[] lowercase = data.clone(); lowercase[i++] = (byte) (c - ('a' - 'A')); for (; i < lowercase.length; i++) { c = lowercase[i]; if (c < 'a' || c > 'z') continue; lowercase[i] = (byte) (c - ('a' - 'A')); } return new ByteString(lowercase); } return this; } /** * Returns a byte string that is a substring of this byte string, beginning at the specified * index until the end of this string. Returns this byte string if {@code beginIndex} is 0. */ public ByteString substring(int beginIndex) { return substring(beginIndex, data.length); } /** * Returns a byte string that is a substring of this byte string, beginning at the specified * {@code beginIndex} and ends at the specified {@code endIndex}. Returns this byte string if * {@code beginIndex} is 0 and {@code endIndex} is the length of this byte string. */ public ByteString substring(int beginIndex, int endIndex) { if (beginIndex < 0) throw new IllegalArgumentException("beginIndex < 0"); if (endIndex > data.length) { throw new IllegalArgumentException("endIndex > length(" + data.length + ")"); } int subLen = endIndex - beginIndex; if (subLen < 0) throw new IllegalArgumentException("endIndex < beginIndex"); if ((beginIndex == 0) && (endIndex == data.length)) { return this; } byte[] copy = new byte[subLen]; System.arraycopy(data, beginIndex, copy, 0, subLen); return new ByteString(copy); } /** Returns the byte at {@code pos}. */ public byte getByte(int pos) { return data[pos]; } /** * Returns the number of bytes in this ByteString. */ public int size() { return data.length; } /** * Returns a byte array containing a copy of the bytes in this {@code ByteString}. */ public byte[] toByteArray() { return data.clone(); } /** Returns the bytes of this string without a defensive copy. Do not mutate! */ byte[] internalArray() { return data; } /** * Returns a {@code ByteBuffer} view of the bytes in this {@code ByteString}. */ public ByteBuffer asByteBuffer() { return ByteBuffer.wrap(data).asReadOnlyBuffer(); } /** Writes the contents of this byte string to {@code out}. */ public void write(OutputStream out) throws IOException { if (out == null) throw new IllegalArgumentException("out == null"); out.write(data); } /** Writes the contents of this byte string to {@code buffer}. */ void write(Buffer buffer) { buffer.write(data, 0, data.length); } /** * Returns true if the bytes of this in {@code [offset..offset+byteCount)} equal the bytes of * {@code other} in {@code [otherOffset..otherOffset+byteCount)}. Returns false if either range is * out of bounds. */ public boolean rangeEquals(int offset, ByteString other, int otherOffset, int byteCount) { return other.rangeEquals(otherOffset, this.data, offset, byteCount); } /** * Returns true if the bytes of this in {@code [offset..offset+byteCount)} equal the bytes of * {@code other} in {@code [otherOffset..otherOffset+byteCount)}. Returns false if either range is * out of bounds. */ public boolean rangeEquals(int offset, byte[] other, int otherOffset, int byteCount) { return offset >= 0 && offset <= data.length - byteCount && otherOffset >= 0 && otherOffset <= other.length - byteCount && arrayRangeEquals(data, offset, other, otherOffset, byteCount); } public final boolean startsWith(ByteString prefix) { return rangeEquals(0, prefix, 0, prefix.size()); } public final boolean startsWith(byte[] prefix) { return rangeEquals(0, prefix, 0, prefix.length); } public final boolean endsWith(ByteString suffix) { return rangeEquals(size() - suffix.size(), suffix, 0, suffix.size()); } public final boolean endsWith(byte[] suffix) { return rangeEquals(size() - suffix.length, suffix, 0, suffix.length); } public final int indexOf(ByteString other) { return indexOf(other.internalArray(), 0); } public final int indexOf(ByteString other, int fromIndex) { return indexOf(other.internalArray(), fromIndex); } public final int indexOf(byte[] other) { return indexOf(other, 0); } public int indexOf(byte[] other, int fromIndex) { fromIndex = Math.max(fromIndex, 0); for (int i = fromIndex, limit = data.length - other.length; i <= limit; i++) { if (arrayRangeEquals(data, i, other, 0, other.length)) { return i; } } return -1; } public final int lastIndexOf(ByteString other) { return lastIndexOf(other.internalArray(), size()); } public final int lastIndexOf(ByteString other, int fromIndex) { return lastIndexOf(other.internalArray(), fromIndex); } public final int lastIndexOf(byte[] other) { return lastIndexOf(other, size()); } public int lastIndexOf(byte[] other, int fromIndex) { fromIndex = Math.min(fromIndex, data.length - other.length); for (int i = fromIndex; i >= 0; i--) { if (arrayRangeEquals(data, i, other, 0, other.length)) { return i; } } return -1; } @Override public boolean equals(Object o) { if (o == this) return true; return o instanceof ByteString && ((ByteString) o).size() == data.length && ((ByteString) o).rangeEquals(0, data, 0, data.length); } @Override public int hashCode() { int result = hashCode; return result != 0 ? result : (hashCode = Arrays.hashCode(data)); } @Override public int compareTo(ByteString byteString) { int sizeA = size(); int sizeB = byteString.size(); for (int i = 0, size = Math.min(sizeA, sizeB); i < size; i++) { int byteA = getByte(i) & 0xff; int byteB = byteString.getByte(i) & 0xff; if (byteA == byteB) continue; return byteA < byteB ? -1 : 1; } if (sizeA == sizeB) return 0; return sizeA < sizeB ? -1 : 1; } /** * Returns a human-readable string that describes the contents of this byte string. Typically this * is a string like {@code [text=Hello]} or {@code [hex=0000ffff]}. */ @Override public String toString() { if (data.length == 0) { return "[size=0]"; } String text = utf8(); int i = codePointIndexToCharIndex(text, 64); if (i == -1) { return data.length <= 64 ? "[hex=" + hex() + "]" : "[size=" + data.length + " hex=" + substring(0, 64).hex() + "…]"; } String safeText = text.substring(0, i) .replace("\\", "\\\\") .replace("\n", "\\n") .replace("\r", "\\r"); return i < text.length() ? "[size=" + data.length + " text=" + safeText + "…]" : "[text=" + safeText + "]"; } static int codePointIndexToCharIndex(String s, int codePointCount) { for (int i = 0, j = 0, length = s.length(), c; i < length; i += Character.charCount(c)) { if (j == codePointCount) { return i; } c = s.codePointAt(i); if ((Character.isISOControl(c) && c != '\n' && c != '\r') || c == Buffer.REPLACEMENT_CHARACTER) { return -1; } j++; } return s.length(); } private void readObject(ObjectInputStream in) throws IOException { int dataLength = in.readInt(); ByteString byteString = ByteString.read(in, dataLength); try { Field field = ByteString.class.getDeclaredField("data"); field.setAccessible(true); field.set(this, byteString.data); } catch (NoSuchFieldException e) { throw new AssertionError(); } catch (IllegalAccessException e) { throw new AssertionError(); } } private void writeObject(ObjectOutputStream out) throws IOException { out.writeInt(data.length); out.write(data); } } okio-okio-parent-1.16.0/okio/src/main/java/okio/DeflaterSink.java000066400000000000000000000114331335667376100245650ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; import java.util.zip.Deflater; import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement; import static okio.Util.checkOffsetAndCount; /** * A sink that uses DEFLATE to * compress data written to another source. * *

Sync flush

* Aggressive flushing of this stream may result in reduced compression. Each * call to {@link #flush} immediately compresses all currently-buffered data; * this early compression may be less effective than compression performed * without flushing. * *

This is equivalent to using {@link Deflater} with the sync flush option. * This class does not offer any partial flush mechanism. For best performance, * only call {@link #flush} when application behavior requires it. */ public final class DeflaterSink implements Sink { private final BufferedSink sink; private final Deflater deflater; private boolean closed; public DeflaterSink(Sink sink, Deflater deflater) { this(Okio.buffer(sink), deflater); } /** * This package-private constructor shares a buffer with its trusted caller. * In general we can't share a BufferedSource because the deflater holds input * bytes until they are inflated. */ DeflaterSink(BufferedSink sink, Deflater deflater) { if (sink == null) throw new IllegalArgumentException("source == null"); if (deflater == null) throw new IllegalArgumentException("inflater == null"); this.sink = sink; this.deflater = deflater; } @Override public void write(Buffer source, long byteCount) throws IOException { checkOffsetAndCount(source.size, 0, byteCount); while (byteCount > 0) { // Share bytes from the head segment of 'source' with the deflater. Segment head = source.head; int toDeflate = (int) Math.min(byteCount, head.limit - head.pos); deflater.setInput(head.data, head.pos, toDeflate); // Deflate those bytes into sink. deflate(false); // Mark those bytes as read. source.size -= toDeflate; head.pos += toDeflate; if (head.pos == head.limit) { source.head = head.pop(); SegmentPool.recycle(head); } byteCount -= toDeflate; } } @IgnoreJRERequirement private void deflate(boolean syncFlush) throws IOException { Buffer buffer = sink.buffer(); while (true) { Segment s = buffer.writableSegment(1); // The 4-parameter overload of deflate() doesn't exist in the RI until // Java 1.7, and is public (although with @hide) on Android since 2.3. // The @hide tag means that this code won't compile against the Android // 2.3 SDK, but it will run fine there. int deflated = syncFlush ? deflater.deflate(s.data, s.limit, Segment.SIZE - s.limit, Deflater.SYNC_FLUSH) : deflater.deflate(s.data, s.limit, Segment.SIZE - s.limit); if (deflated > 0) { s.limit += deflated; buffer.size += deflated; sink.emitCompleteSegments(); } else if (deflater.needsInput()) { if (s.pos == s.limit) { // We allocated a tail segment, but didn't end up needing it. Recycle! buffer.head = s.pop(); SegmentPool.recycle(s); } return; } } } @Override public void flush() throws IOException { deflate(true); sink.flush(); } void finishDeflate() throws IOException { deflater.finish(); deflate(false); } @Override public void close() throws IOException { if (closed) return; // Emit deflated data to the underlying sink. If this fails, we still need // to close the deflater and the sink; otherwise we risk leaking resources. Throwable thrown = null; try { finishDeflate(); } catch (Throwable e) { thrown = e; } try { deflater.end(); } catch (Throwable e) { if (thrown == null) thrown = e; } try { sink.close(); } catch (Throwable e) { if (thrown == null) thrown = e; } closed = true; if (thrown != null) Util.sneakyRethrow(thrown); } @Override public Timeout timeout() { return sink.timeout(); } @Override public String toString() { return "DeflaterSink(" + sink + ")"; } } okio-okio-parent-1.16.0/okio/src/main/java/okio/ForwardingSink.java000066400000000000000000000030371335667376100251420ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; /** A {@link Sink} which forwards calls to another. Useful for subclassing. */ public abstract class ForwardingSink implements Sink { private final Sink delegate; public ForwardingSink(Sink delegate) { if (delegate == null) throw new IllegalArgumentException("delegate == null"); this.delegate = delegate; } /** {@link Sink} to which this instance is delegating. */ public final Sink delegate() { return delegate; } @Override public void write(Buffer source, long byteCount) throws IOException { delegate.write(source, byteCount); } @Override public void flush() throws IOException { delegate.flush(); } @Override public Timeout timeout() { return delegate.timeout(); } @Override public void close() throws IOException { delegate.close(); } @Override public String toString() { return getClass().getSimpleName() + "(" + delegate.toString() + ")"; } } okio-okio-parent-1.16.0/okio/src/main/java/okio/ForwardingSource.java000066400000000000000000000027401335667376100254760ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; /** A {@link Source} which forwards calls to another. Useful for subclassing. */ public abstract class ForwardingSource implements Source { private final Source delegate; public ForwardingSource(Source delegate) { if (delegate == null) throw new IllegalArgumentException("delegate == null"); this.delegate = delegate; } /** {@link Source} to which this instance is delegating. */ public final Source delegate() { return delegate; } @Override public long read(Buffer sink, long byteCount) throws IOException { return delegate.read(sink, byteCount); } @Override public Timeout timeout() { return delegate.timeout(); } @Override public void close() throws IOException { delegate.close(); } @Override public String toString() { return getClass().getSimpleName() + "(" + delegate.toString() + ")"; } } okio-okio-parent-1.16.0/okio/src/main/java/okio/ForwardingTimeout.java000066400000000000000000000041111335667376100256560ustar00rootroot00000000000000/* * Copyright (C) 2015 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; import java.util.concurrent.TimeUnit; /** A {@link Timeout} which forwards calls to another. Useful for subclassing. */ public class ForwardingTimeout extends Timeout { private Timeout delegate; public ForwardingTimeout(Timeout delegate) { if (delegate == null) throw new IllegalArgumentException("delegate == null"); this.delegate = delegate; } /** {@link Timeout} instance to which this instance is currently delegating. */ public final Timeout delegate() { return delegate; } public final ForwardingTimeout setDelegate(Timeout delegate) { if (delegate == null) throw new IllegalArgumentException("delegate == null"); this.delegate = delegate; return this; } @Override public Timeout timeout(long timeout, TimeUnit unit) { return delegate.timeout(timeout, unit); } @Override public long timeoutNanos() { return delegate.timeoutNanos(); } @Override public boolean hasDeadline() { return delegate.hasDeadline(); } @Override public long deadlineNanoTime() { return delegate.deadlineNanoTime(); } @Override public Timeout deadlineNanoTime(long deadlineNanoTime) { return delegate.deadlineNanoTime(deadlineNanoTime); } @Override public Timeout clearTimeout() { return delegate.clearTimeout(); } @Override public Timeout clearDeadline() { return delegate.clearDeadline(); } @Override public void throwIfReached() throws IOException { delegate.throwIfReached(); } } okio-okio-parent-1.16.0/okio/src/main/java/okio/GzipSink.java000066400000000000000000000110651335667376100237510ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; import java.util.zip.CRC32; import java.util.zip.Deflater; import static java.util.zip.Deflater.DEFAULT_COMPRESSION; /** * A sink that uses GZIP to * compress written data to another sink. * *

Sync flush

* Aggressive flushing of this stream may result in reduced compression. Each * call to {@link #flush} immediately compresses all currently-buffered data; * this early compression may be less effective than compression performed * without flushing. * *

This is equivalent to using {@link Deflater} with the sync flush option. * This class does not offer any partial flush mechanism. For best performance, * only call {@link #flush} when application behavior requires it. */ public final class GzipSink implements Sink { /** Sink into which the GZIP format is written. */ private final BufferedSink sink; /** The deflater used to compress the body. */ private final Deflater deflater; /** * The deflater sink takes care of moving data between decompressed source and * compressed sink buffers. */ private final DeflaterSink deflaterSink; private boolean closed; /** Checksum calculated for the compressed body. */ private final CRC32 crc = new CRC32(); public GzipSink(Sink sink) { if (sink == null) throw new IllegalArgumentException("sink == null"); this.deflater = new Deflater(DEFAULT_COMPRESSION, true /* No wrap */); this.sink = Okio.buffer(sink); this.deflaterSink = new DeflaterSink(this.sink, deflater); writeHeader(); } @Override public void write(Buffer source, long byteCount) throws IOException { if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); if (byteCount == 0) return; updateCrc(source, byteCount); deflaterSink.write(source, byteCount); } @Override public void flush() throws IOException { deflaterSink.flush(); } @Override public Timeout timeout() { return sink.timeout(); } @Override public void close() throws IOException { if (closed) return; // This method delegates to the DeflaterSink for finishing the deflate process // but keeps responsibility for releasing the deflater's resources. This is // necessary because writeFooter needs to query the processed byte count which // only works when the deflater is still open. Throwable thrown = null; try { deflaterSink.finishDeflate(); writeFooter(); } catch (Throwable e) { thrown = e; } try { deflater.end(); } catch (Throwable e) { if (thrown == null) thrown = e; } try { sink.close(); } catch (Throwable e) { if (thrown == null) thrown = e; } closed = true; if (thrown != null) Util.sneakyRethrow(thrown); } /** * Returns the {@link Deflater}. * Use it to access stats, dictionary, compression level, etc. */ public final Deflater deflater() { return deflater; } private void writeHeader() { // Write the Gzip header directly into the buffer for the sink to avoid handling IOException. Buffer buffer = this.sink.buffer(); buffer.writeShort(0x1f8b); // Two-byte Gzip ID. buffer.writeByte(0x08); // 8 == Deflate compression method. buffer.writeByte(0x00); // No flags. buffer.writeInt(0x00); // No modification time. buffer.writeByte(0x00); // No extra flags. buffer.writeByte(0x00); // No OS. } private void writeFooter() throws IOException { sink.writeIntLe((int) crc.getValue()); // CRC of original data. sink.writeIntLe((int) deflater.getBytesRead()); // Length of original data. } /** Updates the CRC with the given bytes. */ private void updateCrc(Buffer buffer, long byteCount) { for (Segment head = buffer.head; byteCount > 0; head = head.next) { int segmentLength = (int) Math.min(byteCount, head.limit - head.pos); crc.update(head.data, head.pos, segmentLength); byteCount -= segmentLength; } } } okio-okio-parent-1.16.0/okio/src/main/java/okio/GzipSource.java000066400000000000000000000163401335667376100243060ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.EOFException; import java.io.IOException; import java.util.zip.CRC32; import java.util.zip.Inflater; /** * A source that uses GZIP to * decompress data read from another source. */ public final class GzipSource implements Source { private static final byte FHCRC = 1; private static final byte FEXTRA = 2; private static final byte FNAME = 3; private static final byte FCOMMENT = 4; private static final byte SECTION_HEADER = 0; private static final byte SECTION_BODY = 1; private static final byte SECTION_TRAILER = 2; private static final byte SECTION_DONE = 3; /** The current section. Always progresses forward. */ private int section = SECTION_HEADER; /** * Our source should yield a GZIP header (which we consume directly), followed * by deflated bytes (which we consume via an InflaterSource), followed by a * GZIP trailer (which we also consume directly). */ private final BufferedSource source; /** The inflater used to decompress the deflated body. */ private final Inflater inflater; /** * The inflater source takes care of moving data between compressed source and * decompressed sink buffers. */ private final InflaterSource inflaterSource; /** Checksum used to check both the GZIP header and decompressed body. */ private final CRC32 crc = new CRC32(); public GzipSource(Source source) { if (source == null) throw new IllegalArgumentException("source == null"); this.inflater = new Inflater(true); this.source = Okio.buffer(source); this.inflaterSource = new InflaterSource(this.source, inflater); } @Override public long read(Buffer sink, long byteCount) throws IOException { if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); if (byteCount == 0) return 0; // If we haven't consumed the header, we must consume it before anything else. if (section == SECTION_HEADER) { consumeHeader(); section = SECTION_BODY; } // Attempt to read at least a byte of the body. If we do, we're done. if (section == SECTION_BODY) { long offset = sink.size; long result = inflaterSource.read(sink, byteCount); if (result != -1) { updateCrc(sink, offset, result); return result; } section = SECTION_TRAILER; } // The body is exhausted; time to read the trailer. We always consume the // trailer before returning a -1 exhausted result; that way if you read to // the end of a GzipSource you guarantee that the CRC has been checked. if (section == SECTION_TRAILER) { consumeTrailer(); section = SECTION_DONE; // Gzip streams self-terminate: they return -1 before their underlying // source returns -1. Here we attempt to force the underlying stream to // return -1 which may trigger it to release its resources. If it doesn't // return -1, then our Gzip data finished prematurely! if (!source.exhausted()) { throw new IOException("gzip finished without exhausting source"); } } return -1; } private void consumeHeader() throws IOException { // Read the 10-byte header. We peek at the flags byte first so we know if we // need to CRC the entire header. Then we read the magic ID1ID2 sequence. // We can skip everything else in the first 10 bytes. // +---+---+---+---+---+---+---+---+---+---+ // |ID1|ID2|CM |FLG| MTIME |XFL|OS | (more-->) // +---+---+---+---+---+---+---+---+---+---+ source.require(10); byte flags = source.buffer().getByte(3); boolean fhcrc = ((flags >> FHCRC) & 1) == 1; if (fhcrc) updateCrc(source.buffer(), 0, 10); short id1id2 = source.readShort(); checkEqual("ID1ID2", (short) 0x1f8b, id1id2); source.skip(8); // Skip optional extra fields. // +---+---+=================================+ // | XLEN |...XLEN bytes of "extra field"...| (more-->) // +---+---+=================================+ if (((flags >> FEXTRA) & 1) == 1) { source.require(2); if (fhcrc) updateCrc(source.buffer(), 0, 2); int xlen = source.buffer().readShortLe(); source.require(xlen); if (fhcrc) updateCrc(source.buffer(), 0, xlen); source.skip(xlen); } // Skip an optional 0-terminated name. // +=========================================+ // |...original file name, zero-terminated...| (more-->) // +=========================================+ if (((flags >> FNAME) & 1) == 1) { long index = source.indexOf((byte) 0); if (index == -1) throw new EOFException(); if (fhcrc) updateCrc(source.buffer(), 0, index + 1); source.skip(index + 1); } // Skip an optional 0-terminated comment. // +===================================+ // |...file comment, zero-terminated...| (more-->) // +===================================+ if (((flags >> FCOMMENT) & 1) == 1) { long index = source.indexOf((byte) 0); if (index == -1) throw new EOFException(); if (fhcrc) updateCrc(source.buffer(), 0, index + 1); source.skip(index + 1); } // Confirm the optional header CRC. // +---+---+ // | CRC16 | // +---+---+ if (fhcrc) { checkEqual("FHCRC", source.readShortLe(), (short) crc.getValue()); crc.reset(); } } private void consumeTrailer() throws IOException { // Read the eight-byte trailer. Confirm the body's CRC and size. // +---+---+---+---+---+---+---+---+ // | CRC32 | ISIZE | // +---+---+---+---+---+---+---+---+ checkEqual("CRC", source.readIntLe(), (int) crc.getValue()); checkEqual("ISIZE", source.readIntLe(), (int) inflater.getBytesWritten()); } @Override public Timeout timeout() { return source.timeout(); } @Override public void close() throws IOException { inflaterSource.close(); } /** Updates the CRC with the given bytes. */ private void updateCrc(Buffer buffer, long offset, long byteCount) { // Skip segments that we aren't checksumming. Segment s = buffer.head; for (; offset >= (s.limit - s.pos); s = s.next) { offset -= (s.limit - s.pos); } // Checksum one segment at a time. for (; byteCount > 0; s = s.next) { int pos = (int) (s.pos + offset); int toUpdate = (int) Math.min(s.limit - pos, byteCount); crc.update(s.data, pos, toUpdate); byteCount -= toUpdate; offset = 0; } } private void checkEqual(String name, int expected, int actual) throws IOException { if (actual != expected) { throw new IOException(String.format( "%s: actual 0x%08x != expected 0x%08x", name, actual, expected)); } } } okio-okio-parent-1.16.0/okio/src/main/java/okio/HashingSink.java000066400000000000000000000113451335667376100244220ustar00rootroot00000000000000/* * Copyright (C) 2016 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import javax.annotation.Nullable; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import static okio.Util.checkOffsetAndCount; /** * A sink that computes a hash of the full stream of bytes it has accepted. To use, create an * instance with your preferred hash algorithm. Write all of the data to the sink and then call * {@link #hash()} to compute the final hash value. * *

In this example we use {@code HashingSink} with a {@link BufferedSink} to make writing to the * sink easier.

   {@code
 *
 *   HashingSink hashingSink = HashingSink.sha256(s);
 *   BufferedSink bufferedSink = Okio.buffer(hashingSink);
 *
 *   ... // Write to bufferedSink and either flush or close it.
 *
 *   ByteString hash = hashingSink.hash();
 * }
*/ public final class HashingSink extends ForwardingSink { private final @Nullable MessageDigest messageDigest; private final @Nullable Mac mac; /** Returns a sink that uses the obsolete MD5 hash algorithm to produce 128-bit hashes. */ public static HashingSink md5(Sink sink) { return new HashingSink(sink, "MD5"); } /** Returns a sink that uses the obsolete SHA-1 hash algorithm to produce 160-bit hashes. */ public static HashingSink sha1(Sink sink) { return new HashingSink(sink, "SHA-1"); } /** Returns a sink that uses the SHA-256 hash algorithm to produce 256-bit hashes. */ public static HashingSink sha256(Sink sink) { return new HashingSink(sink, "SHA-256"); } /** Returns a sink that uses the SHA-512 hash algorithm to produce 512-bit hashes. */ public static HashingSink sha512(Sink sink) { return new HashingSink(sink, "SHA-512"); } /** Returns a sink that uses the obsolete SHA-1 HMAC algorithm to produce 160-bit hashes. */ public static HashingSink hmacSha1(Sink sink, ByteString key) { return new HashingSink(sink, key, "HmacSHA1"); } /** Returns a sink that uses the SHA-256 HMAC algorithm to produce 256-bit hashes. */ public static HashingSink hmacSha256(Sink sink, ByteString key) { return new HashingSink(sink, key, "HmacSHA256"); } /** Returns a sink that uses the SHA-512 HMAC algorithm to produce 512-bit hashes. */ public static HashingSink hmacSha512(Sink sink, ByteString key) { return new HashingSink(sink, key, "HmacSHA512"); } private HashingSink(Sink sink, String algorithm) { super(sink); try { this.messageDigest = MessageDigest.getInstance(algorithm); this.mac = null; } catch (NoSuchAlgorithmException e) { throw new AssertionError(); } } private HashingSink(Sink sink, ByteString key, String algorithm) { super(sink); try { this.mac = Mac.getInstance(algorithm); this.mac.init(new SecretKeySpec(key.toByteArray(), algorithm)); this.messageDigest = null; } catch (NoSuchAlgorithmException e) { throw new AssertionError(); } catch (InvalidKeyException e) { throw new IllegalArgumentException(e); } } @Override public void write(Buffer source, long byteCount) throws IOException { checkOffsetAndCount(source.size, 0, byteCount); // Hash byteCount bytes from the prefix of source. long hashedCount = 0; for (Segment s = source.head; hashedCount < byteCount; s = s.next) { int toHash = (int) Math.min(byteCount - hashedCount, s.limit - s.pos); if (messageDigest != null) { messageDigest.update(s.data, s.pos, toHash); } else { mac.update(s.data, s.pos, toHash); } hashedCount += toHash; } // Write those bytes to the sink. super.write(source, byteCount); } /** * Returns the hash of the bytes accepted thus far and resets the internal state of this sink. * *

Warning: This method is not idempotent. Each time this method is called its * internal state is cleared. This starts a new hash with zero bytes accepted. */ public final ByteString hash() { byte[] result = messageDigest != null ? messageDigest.digest() : mac.doFinal(); return ByteString.of(result); } } okio-okio-parent-1.16.0/okio/src/main/java/okio/HashingSource.java000066400000000000000000000110321335667376100247470ustar00rootroot00000000000000/* * Copyright (C) 2016 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; /** * A source that computes a hash of the full stream of bytes it has supplied. To use, create an * instance with your preferred hash algorithm. Exhaust the source by reading all of its bytes and * then call {@link #hash()} to compute the final hash value. * *

In this example we use {@code HashingSource} with a {@link BufferedSource} to make reading * from the source easier.

   {@code
 *
 *   HashingSource hashingSource = HashingSource.sha256(rawSource);
 *   BufferedSource bufferedSource = Okio.buffer(hashingSource);
 *
 *   ... // Read all of bufferedSource.
 *
 *   ByteString hash = hashingSource.hash();
 * }
*/ public final class HashingSource extends ForwardingSource { private final MessageDigest messageDigest; private final Mac mac; /** Returns a sink that uses the obsolete MD5 hash algorithm to produce 128-bit hashes. */ public static HashingSource md5(Source source) { return new HashingSource(source, "MD5"); } /** Returns a sink that uses the obsolete SHA-1 hash algorithm to produce 160-bit hashes. */ public static HashingSource sha1(Source source) { return new HashingSource(source, "SHA-1"); } /** Returns a sink that uses the SHA-256 hash algorithm to produce 256-bit hashes. */ public static HashingSource sha256(Source source) { return new HashingSource(source, "SHA-256"); } /** Returns a sink that uses the obsolete SHA-1 HMAC algorithm to produce 160-bit hashes. */ public static HashingSource hmacSha1(Source source, ByteString key) { return new HashingSource(source, key, "HmacSHA1"); } /** Returns a sink that uses the SHA-256 HMAC algorithm to produce 256-bit hashes. */ public static HashingSource hmacSha256(Source source, ByteString key) { return new HashingSource(source, key, "HmacSHA256"); } private HashingSource(Source source, String algorithm) { super(source); try { this.messageDigest = MessageDigest.getInstance(algorithm); this.mac = null; } catch (NoSuchAlgorithmException e) { throw new AssertionError(); } } private HashingSource(Source source, ByteString key, String algorithm) { super(source); try { this.mac = Mac.getInstance(algorithm); this.mac.init(new SecretKeySpec(key.toByteArray(), algorithm)); this.messageDigest = null; } catch (NoSuchAlgorithmException e) { throw new AssertionError(); } catch (InvalidKeyException e) { throw new IllegalArgumentException(e); } } @Override public long read(Buffer sink, long byteCount) throws IOException { long result = super.read(sink, byteCount); if (result != -1L) { long start = sink.size - result; // Find the first segment that has new bytes. long offset = sink.size; Segment s = sink.head; while (offset > start) { s = s.prev; offset -= (s.limit - s.pos); } // Hash that segment and all the rest until the end. while (offset < sink.size) { int pos = (int) (s.pos + start - offset); if (messageDigest != null) { messageDigest.update(s.data, pos, s.limit - pos); } else { mac.update(s.data, pos, s.limit - pos); } offset += (s.limit - s.pos); start = offset; s = s.next; } } return result; } /** * Returns the hash of the bytes supplied thus far and resets the internal state of this source. * *

Warning: This method is not idempotent. Each time this method is called its * internal state is cleared. This starts a new hash with zero bytes supplied. */ public final ByteString hash() { byte[] result = messageDigest != null ? messageDigest.digest() : mac.doFinal(); return ByteString.of(result); } } okio-okio-parent-1.16.0/okio/src/main/java/okio/InflaterSource.java000066400000000000000000000105741335667376100251440ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.EOFException; import java.io.IOException; import java.util.zip.DataFormatException; import java.util.zip.Inflater; /** * A source that uses DEFLATE * to decompress data read from another source. */ public final class InflaterSource implements Source { private final BufferedSource source; private final Inflater inflater; /** * When we call Inflater.setInput(), the inflater keeps our byte array until * it needs input again. This tracks how many bytes the inflater is currently * holding on to. */ private int bufferBytesHeldByInflater; private boolean closed; public InflaterSource(Source source, Inflater inflater) { this(Okio.buffer(source), inflater); } /** * This package-private constructor shares a buffer with its trusted caller. * In general we can't share a BufferedSource because the inflater holds input * bytes until they are inflated. */ InflaterSource(BufferedSource source, Inflater inflater) { if (source == null) throw new IllegalArgumentException("source == null"); if (inflater == null) throw new IllegalArgumentException("inflater == null"); this.source = source; this.inflater = inflater; } @Override public long read( Buffer sink, long byteCount) throws IOException { if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); if (closed) throw new IllegalStateException("closed"); if (byteCount == 0) return 0; while (true) { boolean sourceExhausted = refill(); // Decompress the inflater's compressed data into the sink. try { Segment tail = sink.writableSegment(1); int toRead = (int) Math.min(byteCount, Segment.SIZE - tail.limit); int bytesInflated = inflater.inflate(tail.data, tail.limit, toRead); if (bytesInflated > 0) { tail.limit += bytesInflated; sink.size += bytesInflated; return bytesInflated; } if (inflater.finished() || inflater.needsDictionary()) { releaseInflatedBytes(); if (tail.pos == tail.limit) { // We allocated a tail segment, but didn't end up needing it. Recycle! sink.head = tail.pop(); SegmentPool.recycle(tail); } return -1; } if (sourceExhausted) throw new EOFException("source exhausted prematurely"); } catch (DataFormatException e) { throw new IOException(e); } } } /** * Refills the inflater with compressed data if it needs input. (And only if * it needs input). Returns true if the inflater required input but the source * was exhausted. */ public final boolean refill() throws IOException { if (!inflater.needsInput()) return false; releaseInflatedBytes(); if (inflater.getRemaining() != 0) throw new IllegalStateException("?"); // TODO: possible? // If there are compressed bytes in the source, assign them to the inflater. if (source.exhausted()) return true; // Assign buffer bytes to the inflater. Segment head = source.buffer().head; bufferBytesHeldByInflater = head.limit - head.pos; inflater.setInput(head.data, head.pos, bufferBytesHeldByInflater); return false; } /** When the inflater has processed compressed data, remove it from the buffer. */ private void releaseInflatedBytes() throws IOException { if (bufferBytesHeldByInflater == 0) return; int toRelease = bufferBytesHeldByInflater - inflater.getRemaining(); bufferBytesHeldByInflater -= toRelease; source.skip(toRelease); } @Override public Timeout timeout() { return source.timeout(); } @Override public void close() throws IOException { if (closed) return; inflater.end(); closed = true; source.close(); } } okio-okio-parent-1.16.0/okio/src/main/java/okio/Okio.java000066400000000000000000000220661335667376100231170ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.OutputStream; import java.net.Socket; import java.net.SocketTimeoutException; import java.nio.file.Files; import java.nio.file.OpenOption; import java.nio.file.Path; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.Nullable; import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement; import static okio.Util.checkOffsetAndCount; /** Essential APIs for working with Okio. */ public final class Okio { static final Logger logger = Logger.getLogger(Okio.class.getName()); private Okio() { } /** * Returns a new source that buffers reads from {@code source}. The returned * source will perform bulk reads into its in-memory buffer. Use this wherever * you read a source to get an ergonomic and efficient access to data. */ public static BufferedSource buffer(Source source) { return new RealBufferedSource(source); } /** * Returns a new sink that buffers writes to {@code sink}. The returned sink * will batch writes to {@code sink}. Use this wherever you write to a sink to * get an ergonomic and efficient access to data. */ public static BufferedSink buffer(Sink sink) { return new RealBufferedSink(sink); } /** Returns a sink that writes to {@code out}. */ public static Sink sink(OutputStream out) { return sink(out, new Timeout()); } private static Sink sink(final OutputStream out, final Timeout timeout) { if (out == null) throw new IllegalArgumentException("out == null"); if (timeout == null) throw new IllegalArgumentException("timeout == null"); return new Sink() { @Override public void write(Buffer source, long byteCount) throws IOException { checkOffsetAndCount(source.size, 0, byteCount); while (byteCount > 0) { timeout.throwIfReached(); Segment head = source.head; int toCopy = (int) Math.min(byteCount, head.limit - head.pos); out.write(head.data, head.pos, toCopy); head.pos += toCopy; byteCount -= toCopy; source.size -= toCopy; if (head.pos == head.limit) { source.head = head.pop(); SegmentPool.recycle(head); } } } @Override public void flush() throws IOException { out.flush(); } @Override public void close() throws IOException { out.close(); } @Override public Timeout timeout() { return timeout; } @Override public String toString() { return "sink(" + out + ")"; } }; } /** * Returns a sink that writes to {@code socket}. Prefer this over {@link * #sink(OutputStream)} because this method honors timeouts. When the socket * write times out, the socket is asynchronously closed by a watchdog thread. */ public static Sink sink(Socket socket) throws IOException { if (socket == null) throw new IllegalArgumentException("socket == null"); if (socket.getOutputStream() == null) throw new IOException("socket's output stream == null"); AsyncTimeout timeout = timeout(socket); Sink sink = sink(socket.getOutputStream(), timeout); return timeout.sink(sink); } /** Returns a source that reads from {@code in}. */ public static Source source(InputStream in) { return source(in, new Timeout()); } private static Source source(final InputStream in, final Timeout timeout) { if (in == null) throw new IllegalArgumentException("in == null"); if (timeout == null) throw new IllegalArgumentException("timeout == null"); return new Source() { @Override public long read(Buffer sink, long byteCount) throws IOException { if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); if (byteCount == 0) return 0; try { timeout.throwIfReached(); Segment tail = sink.writableSegment(1); int maxToCopy = (int) Math.min(byteCount, Segment.SIZE - tail.limit); int bytesRead = in.read(tail.data, tail.limit, maxToCopy); if (bytesRead == -1) return -1; tail.limit += bytesRead; sink.size += bytesRead; return bytesRead; } catch (AssertionError e) { if (isAndroidGetsocknameError(e)) throw new IOException(e); throw e; } } @Override public void close() throws IOException { in.close(); } @Override public Timeout timeout() { return timeout; } @Override public String toString() { return "source(" + in + ")"; } }; } /** Returns a source that reads from {@code file}. */ public static Source source(File file) throws FileNotFoundException { if (file == null) throw new IllegalArgumentException("file == null"); return source(new FileInputStream(file)); } /** Returns a source that reads from {@code path}. */ @IgnoreJRERequirement // Should only be invoked on Java 7+. public static Source source(Path path, OpenOption... options) throws IOException { if (path == null) throw new IllegalArgumentException("path == null"); return source(Files.newInputStream(path, options)); } /** Returns a sink that writes to {@code file}. */ public static Sink sink(File file) throws FileNotFoundException { if (file == null) throw new IllegalArgumentException("file == null"); return sink(new FileOutputStream(file)); } /** Returns a sink that appends to {@code file}. */ public static Sink appendingSink(File file) throws FileNotFoundException { if (file == null) throw new IllegalArgumentException("file == null"); return sink(new FileOutputStream(file, true)); } /** Returns a sink that writes to {@code path}. */ @IgnoreJRERequirement // Should only be invoked on Java 7+. public static Sink sink(Path path, OpenOption... options) throws IOException { if (path == null) throw new IllegalArgumentException("path == null"); return sink(Files.newOutputStream(path, options)); } /** Returns a sink that writes nowhere. */ public static Sink blackhole() { return new Sink() { @Override public void write(Buffer source, long byteCount) throws IOException { source.skip(byteCount); } @Override public void flush() throws IOException { } @Override public Timeout timeout() { return Timeout.NONE; } @Override public void close() throws IOException { } }; } /** * Returns a source that reads from {@code socket}. Prefer this over {@link * #source(InputStream)} because this method honors timeouts. When the socket * read times out, the socket is asynchronously closed by a watchdog thread. */ public static Source source(Socket socket) throws IOException { if (socket == null) throw new IllegalArgumentException("socket == null"); if (socket.getInputStream() == null) throw new IOException("socket's input stream == null"); AsyncTimeout timeout = timeout(socket); Source source = source(socket.getInputStream(), timeout); return timeout.source(source); } private static AsyncTimeout timeout(final Socket socket) { return new AsyncTimeout() { @Override protected IOException newTimeoutException(@Nullable IOException cause) { InterruptedIOException ioe = new SocketTimeoutException("timeout"); if (cause != null) { ioe.initCause(cause); } return ioe; } @Override protected void timedOut() { try { socket.close(); } catch (Exception e) { logger.log(Level.WARNING, "Failed to close timed out socket " + socket, e); } catch (AssertionError e) { if (isAndroidGetsocknameError(e)) { // Catch this exception due to a Firmware issue up to android 4.2.2 // https://code.google.com/p/android/issues/detail?id=54072 logger.log(Level.WARNING, "Failed to close timed out socket " + socket, e); } else { throw e; } } } }; } /** * Returns true if {@code e} is due to a firmware bug fixed after Android 4.2.2. * https://code.google.com/p/android/issues/detail?id=54072 */ static boolean isAndroidGetsocknameError(AssertionError e) { return e.getCause() != null && e.getMessage() != null && e.getMessage().contains("getsockname failed"); } } okio-okio-parent-1.16.0/okio/src/main/java/okio/Options.java000066400000000000000000000214551335667376100236520ustar00rootroot00000000000000/* * Copyright (C) 2016 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.util.AbstractList; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.RandomAccess; /** An indexed set of values that may be read with {@link BufferedSource#select}. */ public final class Options extends AbstractList implements RandomAccess { final ByteString[] byteStrings; final int[] trie; private Options(ByteString[] byteStrings, int[] trie) { this.byteStrings = byteStrings; this.trie = trie; } public static Options of(ByteString... byteStrings) { if (byteStrings.length == 0) { // With no choices we must always return -1. Create a trie that selects from an empty set. return new Options(new ByteString[0], new int[] { 0, -1 }); } // Sort the byte strings which is required when recursively building the trie. Map the sorted // indexes to the caller's indexes. List list = new ArrayList<>(Arrays.asList(byteStrings)); Collections.sort(list); List indexes = new ArrayList<>(); for (int i = 0; i < list.size(); i++) { indexes.add(-1); } for (int i = 0; i < list.size(); i++) { int sortedIndex = Collections.binarySearch(list, byteStrings[i]); indexes.set(sortedIndex, i); } if (list.get(0).size() == 0) { throw new IllegalArgumentException("the empty byte string is not a supported option"); } // Strip elements that will never be returned because they follow their own prefixes. For // example, if the caller provides ["abc", "abcde"] we will never return "abcde" because we // return as soon as we encounter "abc". for (int a = 0; a < list.size(); a++) { ByteString prefix = list.get(a); for (int b = a + 1; b < list.size(); ) { ByteString byteString = list.get(b); if (!byteString.startsWith(prefix)) break; if (byteString.size() == prefix.size()) { throw new IllegalArgumentException("duplicate option: " + byteString); } if (indexes.get(b) > indexes.get(a)) { list.remove(b); indexes.remove(b); } else { b++; } } } Buffer trieBytes = new Buffer(); buildTrieRecursive(0L, trieBytes, 0, list, 0, list.size(), indexes); int[] trie = new int[intCount(trieBytes)]; for (int i = 0; i < trie.length; i++) { trie[i] = trieBytes.readInt(); } if (!trieBytes.exhausted()) { throw new AssertionError(); } return new Options(byteStrings.clone() /* Defensive copy. */, trie); } /** * Builds a trie encoded as an int array. Nodes in the trie are of two types: SELECT and SCAN. * * SELECT nodes are encoded as: * - selectChoiceCount: the number of bytes to choose between (a positive int) * - prefixIndex: the result index at the current position or -1 if the current position is not * a result on its own * - a sorted list of selectChoiceCount bytes to match against the input string * - a heterogeneous list of selectChoiceCount result indexes (>= 0) or offsets (< 0) of the * next node to follow. Elements in this list correspond to elements in the preceding list. * Offsets are negative and must be multiplied by -1 before being used. * * SCAN nodes are encoded as: * - scanByteCount: the number of bytes to match in sequence. This count is negative and must * be multiplied by -1 before being used. * - prefixIndex: the result index at the current position or -1 if the current position is not * a result on its own * - a list of scanByteCount bytes to match * - nextStep: the result index (>= 0) or offset (< 0) of the next node to follow. Offsets are * negative and must be multiplied by -1 before being used. * * This structure is used to improve locality and performance when selecting from a list of * options. */ private static void buildTrieRecursive( long nodeOffset, Buffer node, int byteStringOffset, List byteStrings, int fromIndex, int toIndex, List indexes) { if (fromIndex >= toIndex) throw new AssertionError(); for (int i = fromIndex; i < toIndex; i++) { if (byteStrings.get(i).size() < byteStringOffset) throw new AssertionError(); } ByteString from = byteStrings.get(fromIndex); ByteString to = byteStrings.get(toIndex - 1); int prefixIndex = -1; // If the first element is already matched, that's our prefix. if (byteStringOffset == from.size()) { prefixIndex = indexes.get(fromIndex); fromIndex++; from = byteStrings.get(fromIndex); } if (from.getByte(byteStringOffset) != to.getByte(byteStringOffset)) { // If we have multiple bytes to choose from, encode a SELECT node. int selectChoiceCount = 1; for (int i = fromIndex + 1; i < toIndex; i++) { if (byteStrings.get(i - 1).getByte(byteStringOffset) != byteStrings.get(i).getByte(byteStringOffset)) { selectChoiceCount++; } } // Compute the offset that childNodes will get when we append it to node. long childNodesOffset = nodeOffset + intCount(node) + 2 + (selectChoiceCount * 2); node.writeInt(selectChoiceCount); node.writeInt(prefixIndex); for (int i = fromIndex; i < toIndex; i++) { byte rangeByte = byteStrings.get(i).getByte(byteStringOffset); if (i == fromIndex || rangeByte != byteStrings.get(i - 1).getByte(byteStringOffset)) { node.writeInt(rangeByte & 0xff); } } Buffer childNodes = new Buffer(); int rangeStart = fromIndex; while (rangeStart < toIndex) { byte rangeByte = byteStrings.get(rangeStart).getByte(byteStringOffset); int rangeEnd = toIndex; for (int i = rangeStart + 1; i < toIndex; i++) { if (rangeByte != byteStrings.get(i).getByte(byteStringOffset)) { rangeEnd = i; break; } } if (rangeStart + 1 == rangeEnd && byteStringOffset + 1 == byteStrings.get(rangeStart).size()) { // The result is a single index. node.writeInt(indexes.get(rangeStart)); } else { // The result is another node. node.writeInt((int) (-1 * (childNodesOffset + intCount(childNodes)))); buildTrieRecursive( childNodesOffset, childNodes, byteStringOffset + 1, byteStrings, rangeStart, rangeEnd, indexes); } rangeStart = rangeEnd; } node.write(childNodes, childNodes.size()); } else { // If all of the bytes are the same, encode a SCAN node. int scanByteCount = 0; for (int i = byteStringOffset, max = Math.min(from.size(), to.size()); i < max; i++) { if (from.getByte(i) == to.getByte(i)) { scanByteCount++; } else { break; } } // Compute the offset that childNodes will get when we append it to node. long childNodesOffset = nodeOffset + intCount(node) + 2 + scanByteCount + 1; node.writeInt(-scanByteCount); node.writeInt(prefixIndex); for (int i = byteStringOffset; i < byteStringOffset + scanByteCount; i++) { node.writeInt(from.getByte(i) & 0xff); } if (fromIndex + 1 == toIndex) { // The result is a single index. if (byteStringOffset + scanByteCount != byteStrings.get(fromIndex).size()) { throw new AssertionError(); } node.writeInt(indexes.get(fromIndex)); } else { // The result is another node. Buffer childNodes = new Buffer(); node.writeInt((int) (-1 * (childNodesOffset + intCount(childNodes)))); buildTrieRecursive( childNodesOffset, childNodes, byteStringOffset + scanByteCount, byteStrings, fromIndex, toIndex, indexes); node.write(childNodes, childNodes.size()); } } } @Override public ByteString get(int i) { return byteStrings[i]; } @Override public final int size() { return byteStrings.length; } private static int intCount(Buffer trieBytes) { return (int) (trieBytes.size() / 4); } } okio-okio-parent-1.16.0/okio/src/main/java/okio/PeekSource.java000066400000000000000000000056341335667376100242650ustar00rootroot00000000000000/* * Copyright (C) 2018 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; /** * A {@link Source} which peeks into an upstream {@link BufferedSource} and allows reading and * expanding of the buffered data without consuming it. Does this by requesting additional data from * the upstream source if needed and copying out of the internal buffer of the upstream source if * possible. * *

This source also maintains a snapshot of the starting location of the upstream buffer which it * validates against on every read. If the upstream buffer is read from, this source will become * invalid and throw {@link IllegalStateException} on any future reads. */ final class PeekSource implements Source { private final BufferedSource upstream; private final Buffer buffer; private Segment expectedSegment; private int expectedPos; private boolean closed; private long pos; PeekSource(BufferedSource upstream) { this.upstream = upstream; this.buffer = upstream.buffer(); this.expectedSegment = buffer.head; this.expectedPos = expectedSegment != null ? expectedSegment.pos : -1; } @Override public long read(Buffer sink, long byteCount) throws IOException { if (closed) throw new IllegalStateException("closed"); // Source becomes invalid if there is an expected Segment and it and the expected position // do not match the current head and head position of the upstream buffer if (expectedSegment != null && (expectedSegment != buffer.head || expectedPos != buffer.head.pos)) { throw new IllegalStateException("Peek source is invalid because upstream source was used"); } upstream.request(pos + byteCount); if (expectedSegment == null && buffer.head != null) { // Only once the buffer actually holds data should an expected Segment and position be // recorded. This allows reads from the peek source to repeatedly return -1 and for data to be // added later. Unit tests depend on this behavior. expectedSegment = buffer.head; expectedPos = buffer.head.pos; } long toCopy = Math.min(byteCount, buffer.size - pos); if (toCopy <= 0L) return -1L; buffer.copyTo(sink, pos, toCopy); pos += toCopy; return toCopy; } @Override public Timeout timeout() { return upstream.timeout(); } @Override public void close() throws IOException { closed = true; } } okio-okio-parent-1.16.0/okio/src/main/java/okio/Pipe.java000066400000000000000000000110441335667376100231050ustar00rootroot00000000000000/* * Copyright (C) 2016 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; /** * A source and a sink that are attached. The sink's output is the source's input. Typically each * is accessed by its own thread: a producer thread writes data to the sink and a consumer thread * reads data from the source. * *

This class uses a buffer to decouple source and sink. This buffer has a user-specified maximum * size. When a producer thread outruns its consumer the buffer fills up and eventually writes to * the sink will block until the consumer has caught up. Symmetrically, if a consumer outruns its * producer reads block until there is data to be read. Limits on the amount of time spent waiting * for the other party can be configured with {@linkplain Timeout timeouts} on the source and the * sink. * *

When the sink is closed, source reads will continue to complete normally until the buffer has * been exhausted. At that point reads will return -1, indicating the end of the stream. But if the * source is closed first, writes to the sink will immediately fail with an {@link IOException}. */ public final class Pipe { final long maxBufferSize; final Buffer buffer = new Buffer(); boolean sinkClosed; boolean sourceClosed; private final Sink sink = new PipeSink(); private final Source source = new PipeSource(); public Pipe(long maxBufferSize) { if (maxBufferSize < 1L) { throw new IllegalArgumentException("maxBufferSize < 1: " + maxBufferSize); } this.maxBufferSize = maxBufferSize; } public final Source source() { return source; } public final Sink sink() { return sink; } final class PipeSink implements Sink { final Timeout timeout = new Timeout(); @Override public void write(Buffer source, long byteCount) throws IOException { synchronized (buffer) { if (sinkClosed) throw new IllegalStateException("closed"); while (byteCount > 0) { if (sourceClosed) throw new IOException("source is closed"); long bufferSpaceAvailable = maxBufferSize - buffer.size(); if (bufferSpaceAvailable == 0) { timeout.waitUntilNotified(buffer); // Wait until the source drains the buffer. continue; } long bytesToWrite = Math.min(bufferSpaceAvailable, byteCount); buffer.write(source, bytesToWrite); byteCount -= bytesToWrite; buffer.notifyAll(); // Notify the source that it can resume reading. } } } @Override public void flush() throws IOException { synchronized (buffer) { if (sinkClosed) throw new IllegalStateException("closed"); if (sourceClosed && buffer.size() > 0) throw new IOException("source is closed"); } } @Override public void close() throws IOException { synchronized (buffer) { if (sinkClosed) return; if (sourceClosed && buffer.size() > 0) throw new IOException("source is closed"); sinkClosed = true; buffer.notifyAll(); // Notify the source that no more bytes are coming. } } @Override public Timeout timeout() { return timeout; } } final class PipeSource implements Source { final Timeout timeout = new Timeout(); @Override public long read(Buffer sink, long byteCount) throws IOException { synchronized (buffer) { if (sourceClosed) throw new IllegalStateException("closed"); while (buffer.size() == 0) { if (sinkClosed) return -1L; timeout.waitUntilNotified(buffer); // Wait until the sink fills the buffer. } long result = buffer.read(sink, byteCount); buffer.notifyAll(); // Notify the sink that it can resume writing. return result; } } @Override public void close() throws IOException { synchronized (buffer) { sourceClosed = true; buffer.notifyAll(); // Notify the sink that no more bytes are desired. } } @Override public Timeout timeout() { return timeout; } } } okio-okio-parent-1.16.0/okio/src/main/java/okio/RealBufferedSink.java000066400000000000000000000202261335667376100253650ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.EOFException; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.charset.Charset; final class RealBufferedSink implements BufferedSink { public final Buffer buffer = new Buffer(); public final Sink sink; boolean closed; RealBufferedSink(Sink sink) { if (sink == null) throw new NullPointerException("sink == null"); this.sink = sink; } @Override public Buffer buffer() { return buffer; } @Override public void write(Buffer source, long byteCount) throws IOException { if (closed) throw new IllegalStateException("closed"); buffer.write(source, byteCount); emitCompleteSegments(); } @Override public BufferedSink write(ByteString byteString) throws IOException { if (closed) throw new IllegalStateException("closed"); buffer.write(byteString); return emitCompleteSegments(); } @Override public BufferedSink writeUtf8(String string) throws IOException { if (closed) throw new IllegalStateException("closed"); buffer.writeUtf8(string); return emitCompleteSegments(); } @Override public BufferedSink writeUtf8(String string, int beginIndex, int endIndex) throws IOException { if (closed) throw new IllegalStateException("closed"); buffer.writeUtf8(string, beginIndex, endIndex); return emitCompleteSegments(); } @Override public BufferedSink writeUtf8CodePoint(int codePoint) throws IOException { if (closed) throw new IllegalStateException("closed"); buffer.writeUtf8CodePoint(codePoint); return emitCompleteSegments(); } @Override public BufferedSink writeString(String string, Charset charset) throws IOException { if (closed) throw new IllegalStateException("closed"); buffer.writeString(string, charset); return emitCompleteSegments(); } @Override public BufferedSink writeString(String string, int beginIndex, int endIndex, Charset charset) throws IOException { if (closed) throw new IllegalStateException("closed"); buffer.writeString(string, beginIndex, endIndex, charset); return emitCompleteSegments(); } @Override public BufferedSink write(byte[] source) throws IOException { if (closed) throw new IllegalStateException("closed"); buffer.write(source); return emitCompleteSegments(); } @Override public BufferedSink write(byte[] source, int offset, int byteCount) throws IOException { if (closed) throw new IllegalStateException("closed"); buffer.write(source, offset, byteCount); return emitCompleteSegments(); } @Override public int write(ByteBuffer source) throws IOException { if (closed) throw new IllegalStateException("closed"); int result = buffer.write(source); emitCompleteSegments(); return result; } @Override public long writeAll(Source source) throws IOException { if (source == null) throw new IllegalArgumentException("source == null"); long totalBytesRead = 0; for (long readCount; (readCount = source.read(buffer, Segment.SIZE)) != -1; ) { totalBytesRead += readCount; emitCompleteSegments(); } return totalBytesRead; } @Override public BufferedSink write(Source source, long byteCount) throws IOException { while (byteCount > 0) { long read = source.read(buffer, byteCount); if (read == -1) throw new EOFException(); byteCount -= read; emitCompleteSegments(); } return this; } @Override public BufferedSink writeByte(int b) throws IOException { if (closed) throw new IllegalStateException("closed"); buffer.writeByte(b); return emitCompleteSegments(); } @Override public BufferedSink writeShort(int s) throws IOException { if (closed) throw new IllegalStateException("closed"); buffer.writeShort(s); return emitCompleteSegments(); } @Override public BufferedSink writeShortLe(int s) throws IOException { if (closed) throw new IllegalStateException("closed"); buffer.writeShortLe(s); return emitCompleteSegments(); } @Override public BufferedSink writeInt(int i) throws IOException { if (closed) throw new IllegalStateException("closed"); buffer.writeInt(i); return emitCompleteSegments(); } @Override public BufferedSink writeIntLe(int i) throws IOException { if (closed) throw new IllegalStateException("closed"); buffer.writeIntLe(i); return emitCompleteSegments(); } @Override public BufferedSink writeLong(long v) throws IOException { if (closed) throw new IllegalStateException("closed"); buffer.writeLong(v); return emitCompleteSegments(); } @Override public BufferedSink writeLongLe(long v) throws IOException { if (closed) throw new IllegalStateException("closed"); buffer.writeLongLe(v); return emitCompleteSegments(); } @Override public BufferedSink writeDecimalLong(long v) throws IOException { if (closed) throw new IllegalStateException("closed"); buffer.writeDecimalLong(v); return emitCompleteSegments(); } @Override public BufferedSink writeHexadecimalUnsignedLong(long v) throws IOException { if (closed) throw new IllegalStateException("closed"); buffer.writeHexadecimalUnsignedLong(v); return emitCompleteSegments(); } @Override public BufferedSink emitCompleteSegments() throws IOException { if (closed) throw new IllegalStateException("closed"); long byteCount = buffer.completeSegmentByteCount(); if (byteCount > 0) sink.write(buffer, byteCount); return this; } @Override public BufferedSink emit() throws IOException { if (closed) throw new IllegalStateException("closed"); long byteCount = buffer.size(); if (byteCount > 0) sink.write(buffer, byteCount); return this; } @Override public OutputStream outputStream() { return new OutputStream() { @Override public void write(int b) throws IOException { if (closed) throw new IOException("closed"); buffer.writeByte((byte) b); emitCompleteSegments(); } @Override public void write(byte[] data, int offset, int byteCount) throws IOException { if (closed) throw new IOException("closed"); buffer.write(data, offset, byteCount); emitCompleteSegments(); } @Override public void flush() throws IOException { // For backwards compatibility, a flush() on a closed stream is a no-op. if (!closed) { RealBufferedSink.this.flush(); } } @Override public void close() throws IOException { RealBufferedSink.this.close(); } @Override public String toString() { return RealBufferedSink.this + ".outputStream()"; } }; } @Override public void flush() throws IOException { if (closed) throw new IllegalStateException("closed"); if (buffer.size > 0) { sink.write(buffer, buffer.size); } sink.flush(); } @Override public boolean isOpen() { return !closed; } @Override public void close() throws IOException { if (closed) return; // Emit buffered data to the underlying sink. If this fails, we still need // to close the sink; otherwise we risk leaking resources. Throwable thrown = null; try { if (buffer.size > 0) { sink.write(buffer, buffer.size); } } catch (Throwable e) { thrown = e; } try { sink.close(); } catch (Throwable e) { if (thrown == null) thrown = e; } closed = true; if (thrown != null) Util.sneakyRethrow(thrown); } @Override public Timeout timeout() { return sink.timeout(); } @Override public String toString() { return "buffer(" + sink + ")"; } } okio-okio-parent-1.16.0/okio/src/main/java/okio/RealBufferedSource.java000066400000000000000000000353041335667376100257240ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.charset.Charset; import javax.annotation.Nullable; import static okio.Util.checkOffsetAndCount; final class RealBufferedSource implements BufferedSource { public final Buffer buffer = new Buffer(); public final Source source; boolean closed; RealBufferedSource(Source source) { if (source == null) throw new NullPointerException("source == null"); this.source = source; } @Override public Buffer buffer() { return buffer; } @Override public Buffer getBuffer() { return buffer; } @Override public long read(Buffer sink, long byteCount) throws IOException { if (sink == null) throw new IllegalArgumentException("sink == null"); if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); if (closed) throw new IllegalStateException("closed"); if (buffer.size == 0) { long read = source.read(buffer, Segment.SIZE); if (read == -1) return -1; } long toRead = Math.min(byteCount, buffer.size); return buffer.read(sink, toRead); } @Override public boolean exhausted() throws IOException { if (closed) throw new IllegalStateException("closed"); return buffer.exhausted() && source.read(buffer, Segment.SIZE) == -1; } @Override public void require(long byteCount) throws IOException { if (!request(byteCount)) throw new EOFException(); } @Override public boolean request(long byteCount) throws IOException { if (byteCount < 0) throw new IllegalArgumentException("byteCount < 0: " + byteCount); if (closed) throw new IllegalStateException("closed"); while (buffer.size < byteCount) { if (source.read(buffer, Segment.SIZE) == -1) return false; } return true; } @Override public byte readByte() throws IOException { require(1); return buffer.readByte(); } @Override public ByteString readByteString() throws IOException { buffer.writeAll(source); return buffer.readByteString(); } @Override public ByteString readByteString(long byteCount) throws IOException { require(byteCount); return buffer.readByteString(byteCount); } @Override public int select(Options options) throws IOException { if (closed) throw new IllegalStateException("closed"); while (true) { int index = buffer.selectPrefix(options, true); if (index == -1) return -1; if (index == -2) { // We need to grow the buffer. Do that, then try it all again. if (source.read(buffer, Segment.SIZE) == -1L) return -1; } else { // We matched a full byte string: consume it and return it. int selectedSize = options.byteStrings[index].size(); buffer.skip(selectedSize); return index; } } } @Override public byte[] readByteArray() throws IOException { buffer.writeAll(source); return buffer.readByteArray(); } @Override public byte[] readByteArray(long byteCount) throws IOException { require(byteCount); return buffer.readByteArray(byteCount); } @Override public int read(byte[] sink) throws IOException { return read(sink, 0, sink.length); } @Override public void readFully(byte[] sink) throws IOException { try { require(sink.length); } catch (EOFException e) { // The underlying source is exhausted. Copy the bytes we got before rethrowing. int offset = 0; while (buffer.size > 0) { int read = buffer.read(sink, offset, (int) buffer.size); if (read == -1) throw new AssertionError(); offset += read; } throw e; } buffer.readFully(sink); } @Override public int read(byte[] sink, int offset, int byteCount) throws IOException { checkOffsetAndCount(sink.length, offset, byteCount); if (buffer.size == 0) { long read = source.read(buffer, Segment.SIZE); if (read == -1) return -1; } int toRead = (int) Math.min(byteCount, buffer.size); return buffer.read(sink, offset, toRead); } @Override public int read(ByteBuffer sink) throws IOException { if (buffer.size == 0) { long read = source.read(buffer, Segment.SIZE); if (read == -1) return -1; } return buffer.read(sink); } @Override public void readFully(Buffer sink, long byteCount) throws IOException { try { require(byteCount); } catch (EOFException e) { // The underlying source is exhausted. Copy the bytes we got before rethrowing. sink.writeAll(buffer); throw e; } buffer.readFully(sink, byteCount); } @Override public long readAll(Sink sink) throws IOException { if (sink == null) throw new IllegalArgumentException("sink == null"); long totalBytesWritten = 0; while (source.read(buffer, Segment.SIZE) != -1) { long emitByteCount = buffer.completeSegmentByteCount(); if (emitByteCount > 0) { totalBytesWritten += emitByteCount; sink.write(buffer, emitByteCount); } } if (buffer.size() > 0) { totalBytesWritten += buffer.size(); sink.write(buffer, buffer.size()); } return totalBytesWritten; } @Override public String readUtf8() throws IOException { buffer.writeAll(source); return buffer.readUtf8(); } @Override public String readUtf8(long byteCount) throws IOException { require(byteCount); return buffer.readUtf8(byteCount); } @Override public String readString(Charset charset) throws IOException { if (charset == null) throw new IllegalArgumentException("charset == null"); buffer.writeAll(source); return buffer.readString(charset); } @Override public String readString(long byteCount, Charset charset) throws IOException { require(byteCount); if (charset == null) throw new IllegalArgumentException("charset == null"); return buffer.readString(byteCount, charset); } @Override public @Nullable String readUtf8Line() throws IOException { long newline = indexOf((byte) '\n'); if (newline == -1) { return buffer.size != 0 ? readUtf8(buffer.size) : null; } return buffer.readUtf8Line(newline); } @Override public String readUtf8LineStrict() throws IOException { return readUtf8LineStrict(Long.MAX_VALUE); } @Override public String readUtf8LineStrict(long limit) throws IOException { if (limit < 0) throw new IllegalArgumentException("limit < 0: " + limit); long scanLength = limit == Long.MAX_VALUE ? Long.MAX_VALUE : limit + 1; long newline = indexOf((byte) '\n', 0, scanLength); if (newline != -1) return buffer.readUtf8Line(newline); if (scanLength < Long.MAX_VALUE && request(scanLength) && buffer.getByte(scanLength - 1) == '\r' && request(scanLength + 1) && buffer.getByte(scanLength) == '\n') { return buffer.readUtf8Line(scanLength); // The line was 'limit' UTF-8 bytes followed by \r\n. } Buffer data = new Buffer(); buffer.copyTo(data, 0, Math.min(32, buffer.size())); throw new EOFException("\\n not found: limit=" + Math.min(buffer.size(), limit) + " content=" + data.readByteString().hex() + '…'); } @Override public int readUtf8CodePoint() throws IOException { require(1); byte b0 = buffer.getByte(0); if ((b0 & 0xe0) == 0xc0) { require(2); } else if ((b0 & 0xf0) == 0xe0) { require(3); } else if ((b0 & 0xf8) == 0xf0) { require(4); } return buffer.readUtf8CodePoint(); } @Override public short readShort() throws IOException { require(2); return buffer.readShort(); } @Override public short readShortLe() throws IOException { require(2); return buffer.readShortLe(); } @Override public int readInt() throws IOException { require(4); return buffer.readInt(); } @Override public int readIntLe() throws IOException { require(4); return buffer.readIntLe(); } @Override public long readLong() throws IOException { require(8); return buffer.readLong(); } @Override public long readLongLe() throws IOException { require(8); return buffer.readLongLe(); } @Override public long readDecimalLong() throws IOException { require(1); for (int pos = 0; request(pos + 1); pos++) { byte b = buffer.getByte(pos); if ((b < '0' || b > '9') && (pos != 0 || b != '-')) { // Non-digit, or non-leading negative sign. if (pos == 0) { throw new NumberFormatException(String.format( "Expected leading [0-9] or '-' character but was %#x", b)); } break; } } return buffer.readDecimalLong(); } @Override public long readHexadecimalUnsignedLong() throws IOException { require(1); for (int pos = 0; request(pos + 1); pos++) { byte b = buffer.getByte(pos); if ((b < '0' || b > '9') && (b < 'a' || b > 'f') && (b < 'A' || b > 'F')) { // Non-digit, or non-leading negative sign. if (pos == 0) { throw new NumberFormatException(String.format( "Expected leading [0-9a-fA-F] character but was %#x", b)); } break; } } return buffer.readHexadecimalUnsignedLong(); } @Override public void skip(long byteCount) throws IOException { if (closed) throw new IllegalStateException("closed"); while (byteCount > 0) { if (buffer.size == 0 && source.read(buffer, Segment.SIZE) == -1) { throw new EOFException(); } long toSkip = Math.min(byteCount, buffer.size()); buffer.skip(toSkip); byteCount -= toSkip; } } @Override public long indexOf(byte b) throws IOException { return indexOf(b, 0, Long.MAX_VALUE); } @Override public long indexOf(byte b, long fromIndex) throws IOException { return indexOf(b, fromIndex, Long.MAX_VALUE); } @Override public long indexOf(byte b, long fromIndex, long toIndex) throws IOException { if (closed) throw new IllegalStateException("closed"); if (fromIndex < 0 || toIndex < fromIndex) { throw new IllegalArgumentException( String.format("fromIndex=%s toIndex=%s", fromIndex, toIndex)); } while (fromIndex < toIndex) { long result = buffer.indexOf(b, fromIndex, toIndex); if (result != -1L) return result; // The byte wasn't in the buffer. Give up if we've already reached our target size or if the // underlying stream is exhausted. long lastBufferSize = buffer.size; if (lastBufferSize >= toIndex || source.read(buffer, Segment.SIZE) == -1) return -1L; // Continue the search from where we left off. fromIndex = Math.max(fromIndex, lastBufferSize); } return -1L; } @Override public long indexOf(ByteString bytes) throws IOException { return indexOf(bytes, 0); } @Override public long indexOf(ByteString bytes, long fromIndex) throws IOException { if (closed) throw new IllegalStateException("closed"); while (true) { long result = buffer.indexOf(bytes, fromIndex); if (result != -1) return result; long lastBufferSize = buffer.size; if (source.read(buffer, Segment.SIZE) == -1) return -1L; // Keep searching, picking up from where we left off. fromIndex = Math.max(fromIndex, lastBufferSize - bytes.size() + 1); } } @Override public long indexOfElement(ByteString targetBytes) throws IOException { return indexOfElement(targetBytes, 0); } @Override public long indexOfElement(ByteString targetBytes, long fromIndex) throws IOException { if (closed) throw new IllegalStateException("closed"); while (true) { long result = buffer.indexOfElement(targetBytes, fromIndex); if (result != -1) return result; long lastBufferSize = buffer.size; if (source.read(buffer, Segment.SIZE) == -1) return -1L; // Keep searching, picking up from where we left off. fromIndex = Math.max(fromIndex, lastBufferSize); } } @Override public boolean rangeEquals(long offset, ByteString bytes) throws IOException { return rangeEquals(offset, bytes, 0, bytes.size()); } @Override public boolean rangeEquals(long offset, ByteString bytes, int bytesOffset, int byteCount) throws IOException { if (closed) throw new IllegalStateException("closed"); if (offset < 0 || bytesOffset < 0 || byteCount < 0 || bytes.size() - bytesOffset < byteCount) { return false; } for (int i = 0; i < byteCount; i++) { long bufferOffset = offset + i; if (!request(bufferOffset + 1)) return false; if (buffer.getByte(bufferOffset) != bytes.getByte(bytesOffset + i)) return false; } return true; } @Override public BufferedSource peek() { return Okio.buffer(new PeekSource(this)); } @Override public InputStream inputStream() { return new InputStream() { @Override public int read() throws IOException { if (closed) throw new IOException("closed"); if (buffer.size == 0) { long count = source.read(buffer, Segment.SIZE); if (count == -1) return -1; } return buffer.readByte() & 0xff; } @Override public int read(byte[] data, int offset, int byteCount) throws IOException { if (closed) throw new IOException("closed"); checkOffsetAndCount(data.length, offset, byteCount); if (buffer.size == 0) { long count = source.read(buffer, Segment.SIZE); if (count == -1) return -1; } return buffer.read(data, offset, byteCount); } @Override public int available() throws IOException { if (closed) throw new IOException("closed"); return (int) Math.min(buffer.size, Integer.MAX_VALUE); } @Override public void close() throws IOException { RealBufferedSource.this.close(); } @Override public String toString() { return RealBufferedSource.this + ".inputStream()"; } }; } @Override public boolean isOpen() { return !closed; } @Override public void close() throws IOException { if (closed) return; closed = true; source.close(); buffer.clear(); } @Override public Timeout timeout() { return source.timeout(); } @Override public String toString() { return "buffer(" + source + ")"; } } okio-okio-parent-1.16.0/okio/src/main/java/okio/Segment.java000066400000000000000000000137601335667376100236210ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import javax.annotation.Nullable; /** * A segment of a buffer. * *

Each segment in a buffer is a circularly-linked list node referencing the following and * preceding segments in the buffer. * *

Each segment in the pool is a singly-linked list node referencing the rest of segments in the * pool. * *

The underlying byte arrays of segments may be shared between buffers and byte strings. When a * segment's byte array is shared the segment may not be recycled, nor may its byte data be changed. * The lone exception is that the owner segment is allowed to append to the segment, writing data at * {@code limit} and beyond. There is a single owning segment for each byte array. Positions, * limits, prev, and next references are not shared. */ final class Segment { /** The size of all segments in bytes. */ static final int SIZE = 8192; /** Segments will be shared when doing so avoids {@code arraycopy()} of this many bytes. */ static final int SHARE_MINIMUM = 1024; final byte[] data; /** The next byte of application data byte to read in this segment. */ int pos; /** The first byte of available data ready to be written to. */ int limit; /** True if other segments or byte strings use the same byte array. */ boolean shared; /** True if this segment owns the byte array and can append to it, extending {@code limit}. */ boolean owner; /** Next segment in a linked or circularly-linked list. */ Segment next; /** Previous segment in a circularly-linked list. */ Segment prev; Segment() { this.data = new byte[SIZE]; this.owner = true; this.shared = false; } Segment(byte[] data, int pos, int limit, boolean shared, boolean owner) { this.data = data; this.pos = pos; this.limit = limit; this.shared = shared; this.owner = owner; } /** * Returns a new segment that shares the underlying byte array with this. Adjusting pos and limit * are safe but writes are forbidden. This also marks the current segment as shared, which * prevents it from being pooled. */ final Segment sharedCopy() { shared = true; return new Segment(data, pos, limit, true, false); } /** Returns a new segment that its own private copy of the underlying byte array. */ final Segment unsharedCopy() { return new Segment(data.clone(), pos, limit, false, true); } /** * Removes this segment of a circularly-linked list and returns its successor. * Returns null if the list is now empty. */ public final @Nullable Segment pop() { Segment result = next != this ? next : null; prev.next = next; next.prev = prev; next = null; prev = null; return result; } /** * Appends {@code segment} after this segment in the circularly-linked list. * Returns the pushed segment. */ public final Segment push(Segment segment) { segment.prev = this; segment.next = next; next.prev = segment; next = segment; return segment; } /** * Splits this head of a circularly-linked list into two segments. The first * segment contains the data in {@code [pos..pos+byteCount)}. The second * segment contains the data in {@code [pos+byteCount..limit)}. This can be * useful when moving partial segments from one buffer to another. * *

Returns the new head of the circularly-linked list. */ public final Segment split(int byteCount) { if (byteCount <= 0 || byteCount > limit - pos) throw new IllegalArgumentException(); Segment prefix; // We have two competing performance goals: // - Avoid copying data. We accomplish this by sharing segments. // - Avoid short shared segments. These are bad for performance because they are readonly and // may lead to long chains of short segments. // To balance these goals we only share segments when the copy will be large. if (byteCount >= SHARE_MINIMUM) { prefix = sharedCopy(); } else { prefix = SegmentPool.take(); System.arraycopy(data, pos, prefix.data, 0, byteCount); } prefix.limit = prefix.pos + byteCount; pos += byteCount; prev.push(prefix); return prefix; } /** * Call this when the tail and its predecessor may both be less than half * full. This will copy data so that segments can be recycled. */ public final void compact() { if (prev == this) throw new IllegalStateException(); if (!prev.owner) return; // Cannot compact: prev isn't writable. int byteCount = limit - pos; int availableByteCount = SIZE - prev.limit + (prev.shared ? 0 : prev.pos); if (byteCount > availableByteCount) return; // Cannot compact: not enough writable space. writeTo(prev, byteCount); pop(); SegmentPool.recycle(this); } /** Moves {@code byteCount} bytes from this segment to {@code sink}. */ public final void writeTo(Segment sink, int byteCount) { if (!sink.owner) throw new IllegalArgumentException(); if (sink.limit + byteCount > SIZE) { // We can't fit byteCount bytes at the sink's current position. Shift sink first. if (sink.shared) throw new IllegalArgumentException(); if (sink.limit + byteCount - sink.pos > SIZE) throw new IllegalArgumentException(); System.arraycopy(sink.data, sink.pos, sink.data, 0, sink.limit - sink.pos); sink.limit -= sink.pos; sink.pos = 0; } System.arraycopy(data, pos, sink.data, sink.limit, byteCount); sink.limit += byteCount; pos += byteCount; } } okio-okio-parent-1.16.0/okio/src/main/java/okio/SegmentPool.java000066400000000000000000000036301335667376100244460ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import javax.annotation.Nullable; /** * A collection of unused segments, necessary to avoid GC churn and zero-fill. * This pool is a thread-safe static singleton. */ final class SegmentPool { /** The maximum number of bytes to pool. */ // TODO: Is 64 KiB a good maximum size? Do we ever have that many idle segments? static final long MAX_SIZE = 64 * 1024; // 64 KiB. /** Singly-linked list of segments. */ static @Nullable Segment next; /** Total bytes in this pool. */ static long byteCount; private SegmentPool() { } static Segment take() { synchronized (SegmentPool.class) { if (next != null) { Segment result = next; next = result.next; result.next = null; byteCount -= Segment.SIZE; return result; } } return new Segment(); // Pool is empty. Don't zero-fill while holding a lock. } static void recycle(Segment segment) { if (segment.next != null || segment.prev != null) throw new IllegalArgumentException(); if (segment.shared) return; // This segment cannot be recycled. synchronized (SegmentPool.class) { if (byteCount + Segment.SIZE > MAX_SIZE) return; // Pool is full. byteCount += Segment.SIZE; segment.next = next; segment.pos = segment.limit = 0; next = segment; } } } okio-okio-parent-1.16.0/okio/src/main/java/okio/SegmentedByteString.java000066400000000000000000000253201335667376100261400ustar00rootroot00000000000000/* * Copyright (C) 2015 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.Arrays; import static okio.Util.arrayRangeEquals; import static okio.Util.checkOffsetAndCount; /** * An immutable byte string composed of segments of byte arrays. This class exists to implement * efficient snapshots of buffers. It is implemented as an array of segments, plus a directory in * two halves that describes how the segments compose this byte string. * *

The first half of the directory is the cumulative byte count covered by each segment. The * element at {@code directory[0]} contains the number of bytes held in {@code segments[0]}; the * element at {@code directory[1]} contains the number of bytes held in {@code segments[0] + * segments[1]}, and so on. The element at {@code directory[segments.length - 1]} contains the total * size of this byte string. The first half of the directory is always monotonically increasing. * *

The second half of the directory is the offset in {@code segments} of the first content byte. * Bytes preceding this offset are unused, as are bytes beyond the segment's effective size. * *

Suppose we have a byte string, {@code [A, B, C, D, E, F, G, H, I, J, K, L, M]} that is stored * across three byte arrays: {@code [x, x, x, x, A, B, C, D, E, x, x, x]}, {@code [x, F, G]}, and * {@code [H, I, J, K, L, M, x, x, x, x, x, x]}. The three byte arrays would be stored in {@code * segments} in order. Since the arrays contribute 5, 2, and 6 elements respectively, the directory * starts with {@code [5, 7, 13} to hold the cumulative total at each position. Since the offsets * into the arrays are 4, 1, and 0 respectively, the directory ends with {@code 4, 1, 0]}. * Concatenating these two halves, the complete directory is {@code [5, 7, 13, 4, 1, 0]}. * *

This structure is chosen so that the segment holding a particular offset can be found by * binary search. We use one array rather than two for the directory as a micro-optimization. */ final class SegmentedByteString extends ByteString { final transient byte[][] segments; final transient int[] directory; SegmentedByteString(Buffer buffer, int byteCount) { super(null); checkOffsetAndCount(buffer.size, 0, byteCount); // Walk through the buffer to count how many segments we'll need. int offset = 0; int segmentCount = 0; for (Segment s = buffer.head; offset < byteCount; s = s.next) { if (s.limit == s.pos) { throw new AssertionError("s.limit == s.pos"); // Empty segment. This should not happen! } offset += s.limit - s.pos; segmentCount++; } // Walk through the buffer again to assign segments and build the directory. this.segments = new byte[segmentCount][]; this.directory = new int[segmentCount * 2]; offset = 0; segmentCount = 0; for (Segment s = buffer.head; offset < byteCount; s = s.next) { segments[segmentCount] = s.data; offset += s.limit - s.pos; if (offset > byteCount) { offset = byteCount; // Despite sharing more bytes, only report having up to byteCount. } directory[segmentCount] = offset; directory[segmentCount + segments.length] = s.pos; s.shared = true; segmentCount++; } } @Override public String utf8() { return toByteString().utf8(); } @Override public String string(Charset charset) { return toByteString().string(charset); } @Override public String base64() { return toByteString().base64(); } @Override public String hex() { return toByteString().hex(); } @Override public ByteString toAsciiLowercase() { return toByteString().toAsciiLowercase(); } @Override public ByteString toAsciiUppercase() { return toByteString().toAsciiUppercase(); } @Override public ByteString md5() { return toByteString().md5(); } @Override public ByteString sha1() { return toByteString().sha1(); } @Override public ByteString sha256() { return toByteString().sha256(); } @Override public ByteString hmacSha1(ByteString key) { return toByteString().hmacSha1(key); } @Override public ByteString hmacSha256(ByteString key) { return toByteString().hmacSha256(key); } @Override public String base64Url() { return toByteString().base64Url(); } @Override public ByteString substring(int beginIndex) { return toByteString().substring(beginIndex); } @Override public ByteString substring(int beginIndex, int endIndex) { return toByteString().substring(beginIndex, endIndex); } @Override public byte getByte(int pos) { checkOffsetAndCount(directory[segments.length - 1], pos, 1); int segment = segment(pos); int segmentOffset = segment == 0 ? 0 : directory[segment - 1]; int segmentPos = directory[segment + segments.length]; return segments[segment][pos - segmentOffset + segmentPos]; } /** Returns the index of the segment that contains the byte at {@code pos}. */ private int segment(int pos) { // Search for (pos + 1) instead of (pos) because the directory holds sizes, not indexes. int i = Arrays.binarySearch(directory, 0, segments.length, pos + 1); return i >= 0 ? i : ~i; // If i is negative, bitflip to get the insert position. } @Override public int size() { return directory[segments.length - 1]; } @Override public byte[] toByteArray() { byte[] result = new byte[directory[segments.length - 1]]; int segmentOffset = 0; for (int s = 0, segmentCount = segments.length; s < segmentCount; s++) { int segmentPos = directory[segmentCount + s]; int nextSegmentOffset = directory[s]; System.arraycopy(segments[s], segmentPos, result, segmentOffset, nextSegmentOffset - segmentOffset); segmentOffset = nextSegmentOffset; } return result; } @Override public ByteBuffer asByteBuffer() { return ByteBuffer.wrap(toByteArray()).asReadOnlyBuffer(); } @Override public void write(OutputStream out) throws IOException { if (out == null) throw new IllegalArgumentException("out == null"); int segmentOffset = 0; for (int s = 0, segmentCount = segments.length; s < segmentCount; s++) { int segmentPos = directory[segmentCount + s]; int nextSegmentOffset = directory[s]; out.write(segments[s], segmentPos, nextSegmentOffset - segmentOffset); segmentOffset = nextSegmentOffset; } } @Override void write(Buffer buffer) { int segmentOffset = 0; for (int s = 0, segmentCount = segments.length; s < segmentCount; s++) { int segmentPos = directory[segmentCount + s]; int nextSegmentOffset = directory[s]; Segment segment = new Segment(segments[s], segmentPos, segmentPos + nextSegmentOffset - segmentOffset, true, false); if (buffer.head == null) { buffer.head = segment.next = segment.prev = segment; } else { buffer.head.prev.push(segment); } segmentOffset = nextSegmentOffset; } buffer.size += segmentOffset; } @Override public boolean rangeEquals( int offset, ByteString other, int otherOffset, int byteCount) { if (offset < 0 || offset > size() - byteCount) return false; // Go segment-by-segment through this, passing arrays to other's rangeEquals(). for (int s = segment(offset); byteCount > 0; s++) { int segmentOffset = s == 0 ? 0 : directory[s - 1]; int segmentSize = directory[s] - segmentOffset; int stepSize = Math.min(byteCount, segmentOffset + segmentSize - offset); int segmentPos = directory[segments.length + s]; int arrayOffset = offset - segmentOffset + segmentPos; if (!other.rangeEquals(otherOffset, segments[s], arrayOffset, stepSize)) return false; offset += stepSize; otherOffset += stepSize; byteCount -= stepSize; } return true; } @Override public boolean rangeEquals(int offset, byte[] other, int otherOffset, int byteCount) { if (offset < 0 || offset > size() - byteCount || otherOffset < 0 || otherOffset > other.length - byteCount) { return false; } // Go segment-by-segment through this, comparing ranges of arrays. for (int s = segment(offset); byteCount > 0; s++) { int segmentOffset = s == 0 ? 0 : directory[s - 1]; int segmentSize = directory[s] - segmentOffset; int stepSize = Math.min(byteCount, segmentOffset + segmentSize - offset); int segmentPos = directory[segments.length + s]; int arrayOffset = offset - segmentOffset + segmentPos; if (!arrayRangeEquals(segments[s], arrayOffset, other, otherOffset, stepSize)) return false; offset += stepSize; otherOffset += stepSize; byteCount -= stepSize; } return true; } @Override public int indexOf(byte[] other, int fromIndex) { return toByteString().indexOf(other, fromIndex); } @Override public int lastIndexOf(byte[] other, int fromIndex) { return toByteString().lastIndexOf(other, fromIndex); } /** Returns a copy as a non-segmented byte string. */ private ByteString toByteString() { return new ByteString(toByteArray()); } @Override byte[] internalArray() { return toByteArray(); } @Override public boolean equals(Object o) { if (o == this) return true; return o instanceof ByteString && ((ByteString) o).size() == size() && rangeEquals(0, ((ByteString) o), 0, size()); } @Override public int hashCode() { int result = hashCode; if (result != 0) return result; // Equivalent to Arrays.hashCode(toByteArray()). result = 1; int segmentOffset = 0; for (int s = 0, segmentCount = segments.length; s < segmentCount; s++) { byte[] segment = segments[s]; int segmentPos = directory[segmentCount + s]; int nextSegmentOffset = directory[s]; int segmentSize = nextSegmentOffset - segmentOffset; for (int i = segmentPos, limit = segmentPos + segmentSize; i < limit; i++) { result = (31 * result) + segment[i]; } segmentOffset = nextSegmentOffset; } return (hashCode = result); } @Override public String toString() { return toByteString().toString(); } private Object writeReplace() { return toByteString(); } } okio-okio-parent-1.16.0/okio/src/main/java/okio/Sink.java000066400000000000000000000053071335667376100231210ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.Closeable; import java.io.Flushable; import java.io.IOException; /** * Receives a stream of bytes. Use this interface to write data wherever it's * needed: to the network, storage, or a buffer in memory. Sinks may be layered * to transform received data, such as to compress, encrypt, throttle, or add * protocol framing. * *

Most application code shouldn't operate on a sink directly, but rather on a * {@link BufferedSink} which is both more efficient and more convenient. Use * {@link Okio#buffer(Sink)} to wrap any sink with a buffer. * *

Sinks are easy to test: just use a {@link Buffer} in your tests, and * read from it to confirm it received the data that was expected. * *

Comparison with OutputStream

* This interface is functionally equivalent to {@link java.io.OutputStream}. * *

{@code OutputStream} requires multiple layers when emitted data is * heterogeneous: a {@code DataOutputStream} for primitive values, a {@code * BufferedOutputStream} for buffering, and {@code OutputStreamWriter} for * charset encoding. This class uses {@code BufferedSink} for all of the above. * *

Sink is also easier to layer: there is no {@linkplain * java.io.OutputStream#write(int) single-byte write} method that is awkward to * implement efficiently. * *

Interop with OutputStream

* Use {@link Okio#sink} to adapt an {@code OutputStream} to a sink. Use {@link * BufferedSink#outputStream} to adapt a sink to an {@code OutputStream}. */ public interface Sink extends Closeable, Flushable { /** Removes {@code byteCount} bytes from {@code source} and appends them to this. */ void write(Buffer source, long byteCount) throws IOException; /** Pushes all buffered bytes to their final destination. */ @Override void flush() throws IOException; /** Returns the timeout for this sink. */ Timeout timeout(); /** * Pushes all buffered bytes to their final destination and releases the * resources held by this sink. It is an error to write a closed sink. It is * safe to close a sink more than once. */ @Override void close() throws IOException; } okio-okio-parent-1.16.0/okio/src/main/java/okio/Source.java000066400000000000000000000062501335667376100234530ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.Closeable; import java.io.IOException; /** * Supplies a stream of bytes. Use this interface to read data from wherever * it's located: from the network, storage, or a buffer in memory. Sources may * be layered to transform supplied data, such as to decompress, decrypt, or * remove protocol framing. * *

Most applications shouldn't operate on a source directly, but rather on a * {@link BufferedSource} which is both more efficient and more convenient. Use * {@link Okio#buffer(Source)} to wrap any source with a buffer. * *

Sources are easy to test: just use a {@link Buffer} in your tests, and * fill it with the data your application is to read. * *

Comparison with InputStream

* This interface is functionally equivalent to {@link java.io.InputStream}. * *

{@code InputStream} requires multiple layers when consumed data is * heterogeneous: a {@code DataInputStream} for primitive values, a {@code * BufferedInputStream} for buffering, and {@code InputStreamReader} for * strings. This class uses {@code BufferedSource} for all of the above. * *

Source avoids the impossible-to-implement {@linkplain * java.io.InputStream#available available()} method. Instead callers specify * how many bytes they {@link BufferedSource#require require}. * *

Source omits the unsafe-to-compose {@linkplain java.io.InputStream#mark * mark and reset} state that's tracked by {@code InputStream}; instead, callers * just buffer what they need. * *

When implementing a source, you don't need to worry about the {@linkplain * java.io.InputStream#read single-byte read} method that is awkward to implement efficiently * and returns one of 257 possible values. * *

And source has a stronger {@code skip} method: {@link BufferedSource#skip} * won't return prematurely. * *

Interop with InputStream

* Use {@link Okio#source} to adapt an {@code InputStream} to a source. Use * {@link BufferedSource#inputStream} to adapt a source to an {@code * InputStream}. */ public interface Source extends Closeable { /** * Removes at least 1, and up to {@code byteCount} bytes from this and appends * them to {@code sink}. Returns the number of bytes read, or -1 if this * source is exhausted. */ long read(Buffer sink, long byteCount) throws IOException; /** Returns the timeout for this source. */ Timeout timeout(); /** * Closes this source and releases the resources held by this source. It is an * error to read a closed source. It is safe to close a source more than once. */ @Override void close() throws IOException; } okio-okio-parent-1.16.0/okio/src/main/java/okio/Timeout.java000066400000000000000000000177021335667376100236450ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; import java.io.InterruptedIOException; import java.util.concurrent.TimeUnit; /** * A policy on how much time to spend on a task before giving up. When a task * times out, it is left in an unspecified state and should be abandoned. For * example, if reading from a source times out, that source should be closed and * the read should be retried later. If writing to a sink times out, the same * rules apply: close the sink and retry later. * *

Timeouts and Deadlines

* This class offers two complementary controls to define a timeout policy. * *

Timeouts specify the maximum time to wait for a single * operation to complete. Timeouts are typically used to detect problems like * network partitions. For example, if a remote peer doesn't return any * data for ten seconds, we may assume that the peer is unavailable. * *

Deadlines specify the maximum time to spend on a job, * composed of one or more operations. Use deadlines to set an upper bound on * the time invested on a job. For example, a battery-conscious app may limit * how much time it spends pre-loading content. */ public class Timeout { /** * An empty timeout that neither tracks nor detects timeouts. Use this when * timeouts aren't necessary, such as in implementations whose operations * do not block. */ public static final Timeout NONE = new Timeout() { @Override public Timeout timeout(long timeout, TimeUnit unit) { return this; } @Override public Timeout deadlineNanoTime(long deadlineNanoTime) { return this; } @Override public void throwIfReached() throws IOException { } }; /** * True if {@code deadlineNanoTime} is defined. There is no equivalent to null * or 0 for {@link System#nanoTime}. */ private boolean hasDeadline; private long deadlineNanoTime; private long timeoutNanos; public Timeout() { } /** * Wait at most {@code timeout} time before aborting an operation. Using a * per-operation timeout means that as long as forward progress is being made, * no sequence of operations will fail. * *

If {@code timeout == 0}, operations will run indefinitely. (Operating * system timeouts may still apply.) */ public Timeout timeout(long timeout, TimeUnit unit) { if (timeout < 0) throw new IllegalArgumentException("timeout < 0: " + timeout); if (unit == null) throw new IllegalArgumentException("unit == null"); this.timeoutNanos = unit.toNanos(timeout); return this; } /** Returns the timeout in nanoseconds, or {@code 0} for no timeout. */ public long timeoutNanos() { return timeoutNanos; } /** Returns true if a deadline is enabled. */ public boolean hasDeadline() { return hasDeadline; } /** * Returns the {@linkplain System#nanoTime() nano time} when the deadline will * be reached. * * @throws IllegalStateException if no deadline is set. */ public long deadlineNanoTime() { if (!hasDeadline) throw new IllegalStateException("No deadline"); return deadlineNanoTime; } /** * Sets the {@linkplain System#nanoTime() nano time} when the deadline will be * reached. All operations must complete before this time. Use a deadline to * set a maximum bound on the time spent on a sequence of operations. */ public Timeout deadlineNanoTime(long deadlineNanoTime) { this.hasDeadline = true; this.deadlineNanoTime = deadlineNanoTime; return this; } /** Set a deadline of now plus {@code duration} time. */ public final Timeout deadline(long duration, TimeUnit unit) { if (duration <= 0) throw new IllegalArgumentException("duration <= 0: " + duration); if (unit == null) throw new IllegalArgumentException("unit == null"); return deadlineNanoTime(System.nanoTime() + unit.toNanos(duration)); } /** Clears the timeout. Operating system timeouts may still apply. */ public Timeout clearTimeout() { this.timeoutNanos = 0; return this; } /** Clears the deadline. */ public Timeout clearDeadline() { this.hasDeadline = false; return this; } /** * Throws an {@link InterruptedIOException} if the deadline has been reached or if the current * thread has been interrupted. This method doesn't detect timeouts; that should be implemented to * asynchronously abort an in-progress operation. */ public void throwIfReached() throws IOException { if (Thread.interrupted()) { Thread.currentThread().interrupt(); // Retain interrupted status. throw new InterruptedIOException("interrupted"); } if (hasDeadline && deadlineNanoTime - System.nanoTime() <= 0) { throw new InterruptedIOException("deadline reached"); } } /** * Waits on {@code monitor} until it is notified. Throws {@link InterruptedIOException} if either * the thread is interrupted or if this timeout elapses before {@code monitor} is notified. The * caller must be synchronized on {@code monitor}. * *

Here's a sample class that uses {@code waitUntilNotified()} to await a specific state. Note * that the call is made within a loop to avoid unnecessary waiting and to mitigate spurious * notifications.

{@code
   *
   *   class Dice {
   *     Random random = new Random();
   *     int latestTotal;
   *
   *     public synchronized void roll() {
   *       latestTotal = 2 + random.nextInt(6) + random.nextInt(6);
   *       System.out.println("Rolled " + latestTotal);
   *       notifyAll();
   *     }
   *
   *     public void rollAtFixedRate(int period, TimeUnit timeUnit) {
   *       Executors.newScheduledThreadPool(0).scheduleAtFixedRate(new Runnable() {
   *         public void run() {
   *           roll();
   *          }
   *       }, 0, period, timeUnit);
   *     }
   *
   *     public synchronized void awaitTotal(Timeout timeout, int total)
   *         throws InterruptedIOException {
   *       while (latestTotal != total) {
   *         timeout.waitUntilNotified(this);
   *       }
   *     }
   *   }
   * }
*/ public final void waitUntilNotified(Object monitor) throws InterruptedIOException { try { boolean hasDeadline = hasDeadline(); long timeoutNanos = timeoutNanos(); if (!hasDeadline && timeoutNanos == 0L) { monitor.wait(); // There is no timeout: wait forever. return; } // Compute how long we'll wait. long waitNanos; long start = System.nanoTime(); if (hasDeadline && timeoutNanos != 0) { long deadlineNanos = deadlineNanoTime() - start; waitNanos = Math.min(timeoutNanos, deadlineNanos); } else if (hasDeadline) { waitNanos = deadlineNanoTime() - start; } else { waitNanos = timeoutNanos; } // Attempt to wait that long. This will break out early if the monitor is notified. long elapsedNanos = 0L; if (waitNanos > 0L) { long waitMillis = waitNanos / 1000000L; monitor.wait(waitMillis, (int) (waitNanos - waitMillis * 1000000L)); elapsedNanos = System.nanoTime() - start; } // Throw if the timeout elapsed before the monitor was notified. if (elapsedNanos >= waitNanos) { throw new InterruptedIOException("timeout"); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); // Retain interrupted status. throw new InterruptedIOException("interrupted"); } } } okio-okio-parent-1.16.0/okio/src/main/java/okio/Utf8.java000066400000000000000000000075051335667376100230450ustar00rootroot00000000000000/* * Copyright (C) 2017 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; /** * Okio assumes most applications use UTF-8 exclusively, and offers optimized implementations of * common operations on UTF-8 strings. * *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
{@link ByteString}{@link Buffer}, {@link BufferedSink}, {@link BufferedSource}
Encode a string{@link ByteString#encodeUtf8(String)}{@link BufferedSink#writeUtf8(String)}
Encode a code point{@link BufferedSink#writeUtf8CodePoint(int)}
Decode a string{@link ByteString#utf8()}{@link BufferedSource#readUtf8()}, {@link BufferedSource#readUtf8(long)}
Decode a code point{@link BufferedSource#readUtf8CodePoint()}
Decode until the next {@code \r\n} or {@code \n}{@link BufferedSource#readUtf8LineStrict()}, * {@link BufferedSource#readUtf8LineStrict(long)}
Decode until the next {@code \r\n}, {@code \n}, or {@code EOF}{@link BufferedSource#readUtf8Line()}
Measure the bytes in a UTF-8 string{@link Utf8#size}, {@link Utf8#size(String, int, int)}
*/ public final class Utf8 { private Utf8() { } /** * Returns the number of bytes used to encode {@code string} as UTF-8 when using {@link * ByteString#encodeUtf8} or {@link Buffer#writeUtf8(String)}. */ public static long size(String string) { return size(string, 0, string.length()); } /** * Returns the number of bytes used to encode the slice of {@code string} as UTF-8 when using * {@link BufferedSink#writeUtf8(String, int, int)}. */ public static long size(String string, int beginIndex, int endIndex) { if (string == null) throw new IllegalArgumentException("string == null"); if (beginIndex < 0) throw new IllegalArgumentException("beginIndex < 0: " + beginIndex); if (endIndex < beginIndex) { throw new IllegalArgumentException("endIndex < beginIndex: " + endIndex + " < " + beginIndex); } if (endIndex > string.length()) { throw new IllegalArgumentException( "endIndex > string.length: " + endIndex + " > " + string.length()); } long result = 0; for (int i = beginIndex; i < endIndex;) { int c = string.charAt(i); if (c < 0x80) { // A 7-bit character with 1 byte. result++; i++; } else if (c < 0x800) { // An 11-bit character with 2 bytes. result += 2; i++; } else if (c < 0xd800 || c > 0xdfff) { // A 16-bit character with 3 bytes. result += 3; i++; } else { int low = i + 1 < endIndex ? string.charAt(i + 1) : 0; if (c > 0xdbff || low < 0xdc00 || low > 0xdfff) { // A malformed surrogate, which yields '?'. result++; i++; } else { // A 21-bit character with 4 bytes. result += 4; i += 2; } } } return result; } } okio-okio-parent-1.16.0/okio/src/main/java/okio/Util.java000066400000000000000000000050751335667376100231340ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.nio.charset.Charset; final class Util { /** A cheap and type-safe constant for the UTF-8 Charset. */ public static final Charset UTF_8 = Charset.forName("UTF-8"); private Util() { } public static void checkOffsetAndCount(long size, long offset, long byteCount) { if ((offset | byteCount) < 0 || offset > size || size - offset < byteCount) { throw new ArrayIndexOutOfBoundsException( String.format("size=%s offset=%s byteCount=%s", size, offset, byteCount)); } } public static short reverseBytesShort(short s) { int i = s & 0xffff; int reversed = (i & 0xff00) >>> 8 | (i & 0x00ff) << 8; return (short) reversed; } public static int reverseBytesInt(int i) { return (i & 0xff000000) >>> 24 | (i & 0x00ff0000) >>> 8 | (i & 0x0000ff00) << 8 | (i & 0x000000ff) << 24; } public static long reverseBytesLong(long v) { return (v & 0xff00000000000000L) >>> 56 | (v & 0x00ff000000000000L) >>> 40 | (v & 0x0000ff0000000000L) >>> 24 | (v & 0x000000ff00000000L) >>> 8 | (v & 0x00000000ff000000L) << 8 | (v & 0x0000000000ff0000L) << 24 | (v & 0x000000000000ff00L) << 40 | (v & 0x00000000000000ffL) << 56; } /** * Throws {@code t}, even if the declared throws clause doesn't permit it. * This is a terrible – but terribly convenient – hack that makes it easy to * catch and rethrow exceptions after cleanup. See Java Puzzlers #43. */ public static void sneakyRethrow(Throwable t) { Util.sneakyThrow2(t); } @SuppressWarnings("unchecked") private static void sneakyThrow2(Throwable t) throws T { throw (T) t; } public static boolean arrayRangeEquals( byte[] a, int aOffset, byte[] b, int bOffset, int byteCount) { for (int i = 0; i < byteCount; i++) { if (a[i + aOffset] != b[i + bOffset]) return false; } return true; } } okio-okio-parent-1.16.0/okio/src/main/java/okio/package-info.java000066400000000000000000000003021335667376100245270ustar00rootroot00000000000000/** * Okio complements {@link java.io} and {@link java.nio} to make it much easier to access, store, * and process your data. */ @javax.annotation.ParametersAreNonnullByDefault package okio; okio-okio-parent-1.16.0/okio/src/test/000077500000000000000000000000001335667376100175165ustar00rootroot00000000000000okio-okio-parent-1.16.0/okio/src/test/java/000077500000000000000000000000001335667376100204375ustar00rootroot00000000000000okio-okio-parent-1.16.0/okio/src/test/java/okio/000077500000000000000000000000001335667376100214005ustar00rootroot00000000000000okio-okio-parent-1.16.0/okio/src/test/java/okio/AsyncTimeoutTest.java000066400000000000000000000261671335667376100255430ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; import java.io.InterruptedIOException; import java.util.Arrays; import java.util.List; import java.util.Random; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; import org.junit.Before; import org.junit.Test; import static okio.TestUtil.bufferWithRandomSegmentLayout; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * This test uses four timeouts of varying durations: 250ms, 500ms, 750ms and * 1000ms, named 'a', 'b', 'c' and 'd'. */ public final class AsyncTimeoutTest { private final List timedOut = new CopyOnWriteArrayList<>(); private final AsyncTimeout a = new RecordingAsyncTimeout(); private final AsyncTimeout b = new RecordingAsyncTimeout(); private final AsyncTimeout c = new RecordingAsyncTimeout(); private final AsyncTimeout d = new RecordingAsyncTimeout(); @Before public void setUp() throws Exception { a.timeout( 250, TimeUnit.MILLISECONDS); b.timeout( 500, TimeUnit.MILLISECONDS); c.timeout( 750, TimeUnit.MILLISECONDS); d.timeout(1000, TimeUnit.MILLISECONDS); } @Test public void zeroTimeoutIsNoTimeout() throws Exception { AsyncTimeout timeout = new RecordingAsyncTimeout(); timeout.timeout(0, TimeUnit.MILLISECONDS); timeout.enter(); Thread.sleep(250); assertFalse(timeout.exit()); assertTimedOut(); } @Test public void singleInstanceTimedOut() throws Exception { a.enter(); Thread.sleep(500); assertTrue(a.exit()); assertTimedOut(a); } @Test public void singleInstanceNotTimedOut() throws Exception { b.enter(); Thread.sleep(250); b.exit(); assertFalse(b.exit()); assertTimedOut(); } @Test public void instancesAddedAtEnd() throws Exception { a.enter(); b.enter(); c.enter(); d.enter(); Thread.sleep(1250); assertTrue(a.exit()); assertTrue(b.exit()); assertTrue(c.exit()); assertTrue(d.exit()); assertTimedOut(a, b, c, d); } @Test public void instancesAddedAtFront() throws Exception { d.enter(); c.enter(); b.enter(); a.enter(); Thread.sleep(1250); assertTrue(d.exit()); assertTrue(c.exit()); assertTrue(b.exit()); assertTrue(a.exit()); assertTimedOut(a, b, c, d); } @Test public void instancesRemovedAtFront() throws Exception { a.enter(); b.enter(); c.enter(); d.enter(); assertFalse(a.exit()); assertFalse(b.exit()); assertFalse(c.exit()); assertFalse(d.exit()); assertTimedOut(); } @Test public void instancesRemovedAtEnd() throws Exception { a.enter(); b.enter(); c.enter(); d.enter(); assertFalse(d.exit()); assertFalse(c.exit()); assertFalse(b.exit()); assertFalse(a.exit()); assertTimedOut(); } /** Detecting double-enters is not guaranteed. */ @Test public void doubleEnter() throws Exception { a.enter(); try { a.enter(); fail(); } catch (IllegalStateException expected) { } } @Test public void deadlineOnly() throws Exception { RecordingAsyncTimeout timeout = new RecordingAsyncTimeout(); timeout.deadline(250, TimeUnit.MILLISECONDS); timeout.enter(); Thread.sleep(500); assertTrue(timeout.exit()); assertTimedOut(timeout); } @Test public void deadlineBeforeTimeout() throws Exception { RecordingAsyncTimeout timeout = new RecordingAsyncTimeout(); timeout.deadline(250, TimeUnit.MILLISECONDS); timeout.timeout(750, TimeUnit.MILLISECONDS); timeout.enter(); Thread.sleep(500); assertTrue(timeout.exit()); assertTimedOut(timeout); } @Test public void deadlineAfterTimeout() throws Exception { RecordingAsyncTimeout timeout = new RecordingAsyncTimeout(); timeout.timeout(250, TimeUnit.MILLISECONDS); timeout.deadline(750, TimeUnit.MILLISECONDS); timeout.enter(); Thread.sleep(500); assertTrue(timeout.exit()); assertTimedOut(timeout); } @Test public void deadlineStartsBeforeEnter() throws Exception { RecordingAsyncTimeout timeout = new RecordingAsyncTimeout(); timeout.deadline(500, TimeUnit.MILLISECONDS); Thread.sleep(500); timeout.enter(); Thread.sleep(250); assertTrue(timeout.exit()); assertTimedOut(timeout); } @Test public void deadlineInThePast() throws Exception { RecordingAsyncTimeout timeout = new RecordingAsyncTimeout(); timeout.deadlineNanoTime(System.nanoTime() - 1); timeout.enter(); Thread.sleep(250); assertTrue(timeout.exit()); assertTimedOut(timeout); } @Test public void wrappedSinkTimesOut() throws Exception { Sink sink = new ForwardingSink(new Buffer()) { @Override public void write(Buffer source, long byteCount) throws IOException { try { Thread.sleep(500); } catch (InterruptedException e) { throw new AssertionError(); } } }; AsyncTimeout timeout = new AsyncTimeout(); timeout.timeout(250, TimeUnit.MILLISECONDS); Sink timeoutSink = timeout.sink(sink); Buffer data = new Buffer().writeUtf8("a"); try { timeoutSink.write(data, 1); fail(); } catch (InterruptedIOException expected) { } } @Test public void wrappedSinkFlushTimesOut() throws Exception { Sink sink = new ForwardingSink(new Buffer()) { @Override public void flush() throws IOException { try { Thread.sleep(500); } catch (InterruptedException e) { throw new AssertionError(); } } }; AsyncTimeout timeout = new AsyncTimeout(); timeout.timeout(250, TimeUnit.MILLISECONDS); Sink timeoutSink = timeout.sink(sink); try { timeoutSink.flush(); fail(); } catch (InterruptedIOException expected) { } } @Test public void wrappedSinkCloseTimesOut() throws Exception { Sink sink = new ForwardingSink(new Buffer()) { @Override public void close() throws IOException { try { Thread.sleep(500); } catch (InterruptedException e) { throw new AssertionError(); } } }; AsyncTimeout timeout = new AsyncTimeout(); timeout.timeout(250, TimeUnit.MILLISECONDS); Sink timeoutSink = timeout.sink(sink); try { timeoutSink.close(); fail(); } catch (InterruptedIOException expected) { } } @Test public void wrappedSourceTimesOut() throws Exception { Source source = new ForwardingSource(new Buffer()) { @Override public long read(Buffer sink, long byteCount) throws IOException { try { Thread.sleep(500); return -1; } catch (InterruptedException e) { throw new AssertionError(); } } }; AsyncTimeout timeout = new AsyncTimeout(); timeout.timeout(250, TimeUnit.MILLISECONDS); Source timeoutSource = timeout.source(source); try { timeoutSource.read(null, 0); fail(); } catch (InterruptedIOException expected) { } } @Test public void wrappedSourceCloseTimesOut() throws Exception { Source source = new ForwardingSource(new Buffer()) { @Override public void close() throws IOException { try { Thread.sleep(500); } catch (InterruptedException e) { throw new AssertionError(); } } }; AsyncTimeout timeout = new AsyncTimeout(); timeout.timeout(250, TimeUnit.MILLISECONDS); Source timeoutSource = timeout.source(source); try { timeoutSource.close(); fail(); } catch (InterruptedIOException expected) { } } @Test public void wrappedThrowsWithTimeout() throws Exception { Sink sink = new ForwardingSink(new Buffer()) { @Override public void write(Buffer source, long byteCount) throws IOException { try { Thread.sleep(500); throw new IOException("exception and timeout"); } catch (InterruptedException e) { throw new AssertionError(); } } }; AsyncTimeout timeout = new AsyncTimeout(); timeout.timeout(250, TimeUnit.MILLISECONDS); Sink timeoutSink = timeout.sink(sink); Buffer data = new Buffer().writeUtf8("a"); try { timeoutSink.write(data, 1); fail(); } catch (InterruptedIOException expected) { assertEquals("timeout", expected.getMessage()); assertEquals("exception and timeout", expected.getCause().getMessage()); } } @Test public void wrappedThrowsWithoutTimeout() throws Exception { Sink sink = new ForwardingSink(new Buffer()) { @Override public void write(Buffer source, long byteCount) throws IOException { throw new IOException("no timeout occurred"); } }; AsyncTimeout timeout = new AsyncTimeout(); timeout.timeout(250, TimeUnit.MILLISECONDS); Sink timeoutSink = timeout.sink(sink); Buffer data = new Buffer().writeUtf8("a"); try { timeoutSink.write(data, 1); fail(); } catch (IOException expected) { assertEquals("no timeout occurred", expected.getMessage()); } } /** * We had a bug where writing a very large buffer would fail with an * unexpected timeout because although the sink was making steady forward * progress, doing it all as a single write caused a timeout. */ @Test public void sinkSplitsLargeWrites() throws Exception { byte[] data = new byte[512 * 1024]; Random dice = new Random(0); dice.nextBytes(data); final Buffer source = bufferWithRandomSegmentLayout(dice, data); final Buffer target = new Buffer(); Sink sink = new ForwardingSink(new Buffer()) { @Override public void write(Buffer source, long byteCount) throws IOException { try { Thread.sleep(byteCount / 500); // ~500 KiB/s. target.write(source, byteCount); } catch (InterruptedException e) { throw new AssertionError(); } } }; // Timeout after 250 ms of inactivity. AsyncTimeout timeout = new AsyncTimeout(); timeout.timeout(250, TimeUnit.MILLISECONDS); Sink timeoutSink = timeout.sink(sink); // Transmit 500 KiB of data, which should take ~1 second. But expect no timeout! timeoutSink.write(source, source.size()); // The data should all have arrived. assertEquals(ByteString.of(data), target.readByteString()); } /** Asserts which timeouts fired, and in which order. */ private void assertTimedOut(Timeout... expected) { assertEquals(Arrays.asList(expected), timedOut); } class RecordingAsyncTimeout extends AsyncTimeout { @Override protected void timedOut() { timedOut.add(this); } } } okio-okio-parent-1.16.0/okio/src/test/java/okio/BufferCursorTest.java000066400000000000000000000454011335667376100255160ustar00rootroot00000000000000/* * Copyright (C) 2018 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Random; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; import static okio.Buffer.UnsafeCursor; import static okio.TestUtil.bufferWithRandomSegmentLayout; import static okio.TestUtil.bufferWithSegments; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; @RunWith(Parameterized.class) public final class BufferCursorTest { enum BufferFactory { EMPTY { @Override Buffer newBuffer() { return new Buffer(); } }, SMALL_BUFFER { @Override Buffer newBuffer() { return new Buffer().writeUtf8("abcde"); } }, SMALL_SEGMENTED_BUFFER { @Override Buffer newBuffer() throws Exception { return bufferWithSegments("abc", "defg", "hijkl"); } }, LARGE_BUFFER { @Override Buffer newBuffer() throws Exception { Random dice = new Random(0); byte[] largeByteArray = new byte[512 * 1024]; dice.nextBytes(largeByteArray); return new Buffer().write(largeByteArray); } }, LARGE_BUFFER_WITH_RANDOM_LAYOUT { @Override Buffer newBuffer() throws Exception { Random dice = new Random(0); byte[] largeByteArray = new byte[512 * 1024]; dice.nextBytes(largeByteArray); return bufferWithRandomSegmentLayout(dice, largeByteArray); } }; abstract Buffer newBuffer() throws Exception; } @Parameters(name = "{0}") public static List parameters() throws Exception { List result = new ArrayList<>(); for (BufferFactory bufferFactory : BufferFactory.values()) { result.add(new Object[] { bufferFactory }); } return result; } @Parameter public BufferFactory bufferFactory; @Test public void apiExample() throws Exception { Buffer buffer = new Buffer(); try (UnsafeCursor cursor = buffer.readAndWriteUnsafe()) { cursor.resizeBuffer(1000_000); do { Arrays.fill(cursor.data, cursor.start, cursor.end, (byte) 'x'); } while (cursor.next() != -1); cursor.seek(3); cursor.data[cursor.start] = 'o'; cursor.seek(1); cursor.data[cursor.start] = 'o'; cursor.resizeBuffer(4); } assertEquals(new Buffer().writeUtf8("xoxo"), buffer); } @Test public void accessSegmentBySegment() throws Exception { Buffer buffer = bufferFactory.newBuffer(); try (UnsafeCursor cursor = buffer.readUnsafe()) { Buffer actual = new Buffer(); while (cursor.next() != -1L) { actual.write(cursor.data, cursor.start, cursor.end - cursor.start); } assertEquals(buffer, actual); } } @Test public void seekToNegativeOneSeeksBeforeFirstSegment() throws Exception { Buffer buffer = bufferFactory.newBuffer(); try (UnsafeCursor cursor = buffer.readUnsafe()) { cursor.seek(-1L); assertEquals(-1, cursor.offset); assertEquals(null, cursor.data); assertEquals(-1, cursor.start); assertEquals(-1, cursor.end); cursor.next(); assertEquals(0, cursor.offset); } } @Test public void accessByteByByte() throws Exception { Buffer buffer = bufferFactory.newBuffer(); try (UnsafeCursor cursor = buffer.readUnsafe()) { byte[] actual = new byte[(int) buffer.size()]; for (int i = 0; i < buffer.size(); i++) { cursor.seek(i); actual[i] = cursor.data[cursor.start]; } assertEquals(ByteString.of(actual), buffer.snapshot()); } } @Test public void accessByteByByteReverse() throws Exception { Buffer buffer = bufferFactory.newBuffer(); try (UnsafeCursor cursor = buffer.readUnsafe()) { byte[] actual = new byte[(int) buffer.size()]; for (int i = (int) (buffer.size() - 1); i >= 0; i--) { cursor.seek(i); actual[i] = cursor.data[cursor.start]; } assertEquals(ByteString.of(actual), buffer.snapshot()); } } @Test public void accessByteByByteAlwaysResettingToZero() throws Exception { Buffer buffer = bufferFactory.newBuffer(); try (UnsafeCursor cursor = buffer.readUnsafe()) { byte[] actual = new byte[(int) buffer.size()]; for (int i = 0; i < buffer.size(); i++) { cursor.seek(i); actual[i] = cursor.data[cursor.start]; cursor.seek(0L); } assertEquals(ByteString.of(actual), buffer.snapshot()); } } @Test public void segmentBySegmentNavigation() throws Exception { Buffer buffer = bufferFactory.newBuffer(); UnsafeCursor cursor = buffer.readUnsafe(); assertEquals(-1, cursor.offset); try { long lastOffset = cursor.offset; while (cursor.next() != -1L) { assertTrue(cursor.offset > lastOffset); lastOffset = cursor.offset; } assertEquals(buffer.size(), cursor.offset); assertNull(cursor.data); assertEquals(-1, cursor.start); assertEquals(-1, cursor.end); } finally { cursor.close(); } } @Test public void seekWithinSegment() throws Exception { assumeTrue(bufferFactory == BufferFactory.SMALL_SEGMENTED_BUFFER); Buffer buffer = bufferFactory.newBuffer(); assertEquals("abcdefghijkl", buffer.clone().readUtf8()); // Seek to the 'f' in the "defg" segment. try (UnsafeCursor cursor = buffer.readUnsafe()) { assertEquals(2, cursor.seek(5)); // 2 for 2 bytes left in the segment: "fg". assertEquals(5, cursor.offset); assertEquals(2, cursor.end - cursor.start); assertEquals('d', (char) cursor.data[cursor.start - 2]); // Out of bounds! assertEquals('e', (char) cursor.data[cursor.start - 1]); // Out of bounds! assertEquals('f', (char) cursor.data[cursor.start]); assertEquals('g', (char) cursor.data[cursor.start + 1]); } } @Test public void acquireAndRelease() throws Exception { Buffer buffer = bufferFactory.newBuffer(); UnsafeCursor cursor = new UnsafeCursor(); // Nothing initialized before acquire. assertEquals(-1, cursor.offset); assertNull(cursor.data); assertEquals(-1, cursor.start); assertEquals(-1, cursor.end); buffer.readUnsafe(cursor); cursor.close(); // Nothing initialized after close. assertEquals(-1, cursor.offset); assertNull(cursor.data); assertEquals(-1, cursor.start); assertEquals(-1, cursor.end); } @Test public void doubleAcquire() throws Exception { Buffer buffer = bufferFactory.newBuffer(); try (UnsafeCursor cursor = buffer.readUnsafe()) { buffer.readUnsafe(cursor); fail(); } catch (IllegalStateException expected) { } } @Test public void releaseWithoutAcquire() throws Exception { UnsafeCursor cursor = new UnsafeCursor(); try { cursor.close(); fail(); } catch (IllegalStateException expected) { } } @Test public void releaseAfterRelease() throws Exception { Buffer buffer = bufferFactory.newBuffer(); UnsafeCursor cursor = buffer.readUnsafe(); cursor.close(); try { cursor.close(); fail(); } catch (IllegalStateException expected) { } } @Test public void enlarge() throws Exception { Buffer buffer = bufferFactory.newBuffer(); long originalSize = buffer.size(); Buffer expected = deepCopy(buffer); expected.writeUtf8("abc"); try (UnsafeCursor cursor = buffer.readAndWriteUnsafe()) { assertEquals(originalSize, cursor.resizeBuffer(originalSize + 3)); cursor.seek(originalSize); cursor.data[cursor.start] = 'a'; cursor.seek(originalSize + 1); cursor.data[cursor.start] = 'b'; cursor.seek(originalSize + 2); cursor.data[cursor.start] = 'c'; } assertEquals(expected, buffer); } @Test public void enlargeByManySegments() throws Exception { Buffer buffer = bufferFactory.newBuffer(); long originalSize = buffer.size(); Buffer expected = deepCopy(buffer); expected.writeUtf8(TestUtil.repeat('x', 1_000_000)); try (UnsafeCursor cursor = buffer.readAndWriteUnsafe()) { cursor.resizeBuffer(originalSize + 1_000_000); cursor.seek(originalSize); do { Arrays.fill(cursor.data, cursor.start, cursor.end, (byte) 'x'); } while (cursor.next() != -1); } assertEquals(expected, buffer); } @Test public void resizeNotAcquired() throws Exception { UnsafeCursor cursor = new UnsafeCursor(); try { cursor.resizeBuffer(10); fail(); } catch (IllegalStateException expected) { } } @Test public void expandNotAcquired() throws Exception { UnsafeCursor cursor = new UnsafeCursor(); try { cursor.expandBuffer(10); fail(); } catch (IllegalStateException expected) { } } @Test public void resizeAcquiredReadOnly() throws Exception { Buffer buffer = bufferFactory.newBuffer(); try (UnsafeCursor cursor = buffer.readUnsafe()) { cursor.resizeBuffer(10); fail(); } catch (IllegalStateException expected) { } } @Test public void expandAcquiredReadOnly() throws Exception { Buffer buffer = bufferFactory.newBuffer(); try (UnsafeCursor cursor = buffer.readUnsafe()) { cursor.expandBuffer(10); fail(); } catch (IllegalStateException expected) { } } @Test public void shrink() throws Exception { Buffer buffer = bufferFactory.newBuffer(); assumeTrue(buffer.size() > 3); long originalSize = buffer.size(); Buffer expected = new Buffer(); deepCopy(buffer).copyTo(expected, 0, originalSize - 3); try (UnsafeCursor cursor = buffer.readAndWriteUnsafe()) { assertEquals(originalSize, cursor.resizeBuffer(originalSize - 3)); } assertEquals(expected, buffer); } @Test public void shrinkByManySegments() throws Exception { Buffer buffer = bufferFactory.newBuffer(); assumeTrue(buffer.size() <= 1_000_000); long originalSize = buffer.size(); Buffer toShrink = new Buffer(); toShrink.writeUtf8(TestUtil.repeat('x', 1_000_000)); deepCopy(buffer).copyTo(toShrink, 0, originalSize); UnsafeCursor cursor = new UnsafeCursor(); toShrink.readAndWriteUnsafe(cursor); try { cursor.resizeBuffer(originalSize); } finally { cursor.close(); } Buffer expected = new Buffer(); expected.writeUtf8(TestUtil.repeat('x', (int) originalSize)); assertEquals(expected, toShrink); } @Test public void shrinkAdjustOffset() throws Exception { Buffer buffer = bufferFactory.newBuffer(); assumeTrue(buffer.size() > 4); try (UnsafeCursor cursor = buffer.readAndWriteUnsafe()) { cursor.seek(buffer.size() - 1); cursor.resizeBuffer(3); assertEquals(3, cursor.offset); assertEquals(null, cursor.data); assertEquals(-1, cursor.start); assertEquals(-1, cursor.end); } } @Test public void resizeToSameSizeSeeksToEnd() throws Exception { Buffer buffer = bufferFactory.newBuffer(); long originalSize = buffer.size(); try (UnsafeCursor cursor = buffer.readAndWriteUnsafe()) { cursor.seek(buffer.size() / 2); assertEquals(originalSize, buffer.size()); cursor.resizeBuffer(originalSize); assertEquals(originalSize, buffer.size()); assertEquals(originalSize, cursor.offset); assertNull(cursor.data); assertEquals(-1, cursor.start); assertEquals(-1, cursor.end); } } @Test public void resizeEnlargeMovesCursorToOldSize() throws Exception { Buffer buffer = bufferFactory.newBuffer(); long originalSize = buffer.size(); Buffer expected = deepCopy(buffer); expected.writeUtf8("a"); try (UnsafeCursor cursor = buffer.readAndWriteUnsafe()) { cursor.seek(buffer.size() / 2); assertEquals(originalSize, buffer.size()); cursor.resizeBuffer(originalSize + 1); assertEquals(originalSize, cursor.offset); assertNotNull(cursor.data); assertNotEquals(-1, cursor.start); assertEquals(cursor.start + 1, cursor.end); cursor.data[cursor.start] = 'a'; } assertEquals(expected, buffer); } @Test public void resizeShrinkMovesCursorToEnd() throws Exception { Buffer buffer = bufferFactory.newBuffer(); assumeTrue(buffer.size() > 0); long originalSize = buffer.size(); try (UnsafeCursor cursor = buffer.readAndWriteUnsafe()) { cursor.seek(buffer.size() / 2); assertEquals(originalSize, buffer.size()); cursor.resizeBuffer(originalSize - 1); assertEquals(originalSize - 1, cursor.offset); assertNull(cursor.data); assertEquals(-1, cursor.start); assertEquals(-1, cursor.end); } } @Test public void acquireReadOnlyDoesNotCopySharedDataArray() throws Exception { Buffer buffer = deepCopy(bufferFactory.newBuffer()); assumeTrue(buffer.size() > 0); Buffer shared = buffer.clone(); assertTrue(buffer.head.shared); try (UnsafeCursor cursor = buffer.readUnsafe()) { cursor.seek(0); assertSame(cursor.data, shared.head.data); } } @Test public void acquireReadWriteDoesNotCopyUnsharedDataArray() throws Exception { Buffer buffer = deepCopy(bufferFactory.newBuffer()); assumeTrue(buffer.size() > 0); assertFalse(buffer.head.shared); byte[] originalData = buffer.head.data; try (UnsafeCursor cursor = buffer.readAndWriteUnsafe()) { cursor.seek(0); assertSame(cursor.data, originalData); } } @Test public void acquireReadWriteCopiesSharedDataArray() throws Exception { Buffer buffer = deepCopy(bufferFactory.newBuffer()); assumeTrue(buffer.size() > 0); Buffer shared = buffer.clone(); assertTrue(buffer.head.shared); try (UnsafeCursor cursor = buffer.readAndWriteUnsafe()) { cursor.seek(0); assertNotSame(cursor.data, shared.head.data); } } @Test public void writeSharedSegments() throws Exception { Buffer buffer = bufferFactory.newBuffer(); // Make a deep copy. This buffer's segments are not shared. Buffer deepCopy = deepCopy(buffer); assertTrue(deepCopy.head == null || !deepCopy.head.shared); // Make a shallow copy. Both buffers' segments are shared as a side effect. Buffer shallowCopy = buffer.clone(); assertTrue(shallowCopy.head == null || shallowCopy.head.shared); assertTrue(buffer.head == null || buffer.head.shared); Buffer expected = new Buffer(); expected.writeUtf8(TestUtil.repeat('x', (int) buffer.size())); try (UnsafeCursor cursor = buffer.readAndWriteUnsafe()) { while (cursor.next() != -1) { Arrays.fill(cursor.data, cursor.start, cursor.end, (byte) 'x'); } } // The buffer was fully changed. assertEquals(expected, buffer); // The buffer we're shared with is unchanged. assertEquals(deepCopy, shallowCopy); } /** As an optimization it's okay to use the same cursor on multiple buffers. */ @Test public void cursorReuse() throws Exception { UnsafeCursor cursor = new UnsafeCursor(); Buffer buffer1 = bufferFactory.newBuffer(); buffer1.readUnsafe(cursor); assertSame(buffer1, cursor.buffer); assertFalse(cursor.readWrite); cursor.close(); assertSame(null, cursor.buffer); Buffer buffer2 = bufferFactory.newBuffer(); buffer2.readAndWriteUnsafe(cursor); assertSame(buffer2, cursor.buffer); assertTrue(cursor.readWrite); cursor.close(); assertSame(null, cursor.buffer); } @Test public void expand() throws Exception { Buffer buffer = bufferFactory.newBuffer(); long originalSize = buffer.size(); Buffer expected = deepCopy(buffer); expected.writeUtf8("abcde"); try (UnsafeCursor cursor = buffer.readAndWriteUnsafe()) { cursor.expandBuffer(5); for (int i = 0; i < 5; i++) { cursor.data[cursor.start + i] = (byte) ('a' + i); } cursor.resizeBuffer(originalSize + 5); } assertEquals(expected, buffer); } @Test public void expandSameSegment() throws Exception { Buffer buffer = bufferFactory.newBuffer(); long originalSize = buffer.size(); assumeTrue(originalSize > 0); try (UnsafeCursor cursor = buffer.readAndWriteUnsafe()) { cursor.seek(originalSize - 1); int originalEnd = cursor.end; assumeTrue(originalEnd < Segment.SIZE); long addedByteCount = cursor.expandBuffer(1); assertEquals(Segment.SIZE - originalEnd, addedByteCount); assertEquals(originalSize + addedByteCount, buffer.size()); assertEquals(originalSize, cursor.offset); assertEquals(originalEnd, cursor.start); assertEquals(Segment.SIZE, cursor.end); } } @Test public void expandNewSegment() throws Exception { Buffer buffer = bufferFactory.newBuffer(); long originalSize = buffer.size(); try (UnsafeCursor cursor = buffer.readAndWriteUnsafe()) { long addedByteCount = cursor.expandBuffer(Segment.SIZE); assertEquals(Segment.SIZE, addedByteCount); assertEquals(originalSize, cursor.offset); assertEquals(0, cursor.start); assertEquals(Segment.SIZE, cursor.end); } } @Test public void expandMovesOffsetToOldSize() throws Exception { Buffer buffer = bufferFactory.newBuffer(); long originalSize = buffer.size(); try (UnsafeCursor cursor = buffer.readAndWriteUnsafe()) { cursor.seek(buffer.size() / 2); assertEquals(originalSize, buffer.size()); long addedByteCount = cursor.expandBuffer(5); assertEquals(originalSize + addedByteCount, buffer.size()); assertEquals(originalSize, cursor.offset); } } /** Returns a copy of {@code buffer} with no segments with {@code original}. */ private Buffer deepCopy(Buffer original) { Buffer result = new Buffer(); if (original.size() == 0) return result; result.head = original.head.unsharedCopy(); result.head.next = result.head.prev = result.head; for (Segment s = original.head.next; s != original.head; s = s.next) { result.head.prev.push(s.unsharedCopy()); } result.size = original.size; return result; } } okio-okio-parent-1.16.0/okio/src/test/java/okio/BufferTest.java000066400000000000000000000516711335667376100243260ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.List; import java.util.Random; import org.junit.Test; import static java.util.Arrays.asList; import static okio.TestUtil.bufferWithRandomSegmentLayout; import static okio.TestUtil.repeat; import static okio.Util.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * Tests solely for the behavior of Buffer's implementation. For generic BufferedSink or * BufferedSource behavior use BufferedSinkTest or BufferedSourceTest, respectively. */ public final class BufferTest { @Test public void readAndWriteUtf8() throws Exception { Buffer buffer = new Buffer(); buffer.writeUtf8("ab"); assertEquals(2, buffer.size()); buffer.writeUtf8("cdef"); assertEquals(6, buffer.size()); assertEquals("abcd", buffer.readUtf8(4)); assertEquals(2, buffer.size()); assertEquals("ef", buffer.readUtf8(2)); assertEquals(0, buffer.size()); try { buffer.readUtf8(1); fail(); } catch (ArrayIndexOutOfBoundsException expected) { } } @Test public void completeSegmentByteCountOnEmptyBuffer() throws Exception { Buffer buffer = new Buffer(); assertEquals(0, buffer.completeSegmentByteCount()); } @Test public void completeSegmentByteCountOnBufferWithFullSegments() throws Exception { Buffer buffer = new Buffer(); buffer.writeUtf8(repeat('a', Segment.SIZE * 4)); assertEquals(Segment.SIZE * 4, buffer.completeSegmentByteCount()); } @Test public void completeSegmentByteCountOnBufferWithIncompleteTailSegment() throws Exception { Buffer buffer = new Buffer(); buffer.writeUtf8(repeat('a', Segment.SIZE * 4 - 10)); assertEquals(Segment.SIZE * 3, buffer.completeSegmentByteCount()); } /** Buffer's toString is the same as ByteString's. */ @Test public void bufferToString() throws Exception { assertEquals("[size=0]", new Buffer().toString()); assertEquals("[text=a\\r\\nb\\nc\\rd\\\\e]", new Buffer().writeUtf8("a\r\nb\nc\rd\\e").toString()); assertEquals("[text=Tyrannosaur]", new Buffer().writeUtf8("Tyrannosaur").toString()); assertEquals("[text=təˈranəˌsôr]", new Buffer() .write(ByteString.decodeHex("74c999cb8872616ec999cb8c73c3b472")) .toString()); assertEquals("[hex=0000000000000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000]", new Buffer().write(new byte[64]).toString()); } @Test public void multipleSegmentBuffers() throws Exception { Buffer buffer = new Buffer(); buffer.writeUtf8(repeat('a', 1000)); buffer.writeUtf8(repeat('b', 2500)); buffer.writeUtf8(repeat('c', 5000)); buffer.writeUtf8(repeat('d', 10000)); buffer.writeUtf8(repeat('e', 25000)); buffer.writeUtf8(repeat('f', 50000)); assertEquals(repeat('a', 999), buffer.readUtf8(999)); // a...a assertEquals("a" + repeat('b', 2500) + "c", buffer.readUtf8(2502)); // ab...bc assertEquals(repeat('c', 4998), buffer.readUtf8(4998)); // c...c assertEquals("c" + repeat('d', 10000) + "e", buffer.readUtf8(10002)); // cd...de assertEquals(repeat('e', 24998), buffer.readUtf8(24998)); // e...e assertEquals("e" + repeat('f', 50000), buffer.readUtf8(50001)); // ef...f assertEquals(0, buffer.size()); } @Test public void fillAndDrainPool() throws Exception { Buffer buffer = new Buffer(); // Take 2 * MAX_SIZE segments. This will drain the pool, even if other tests filled it. buffer.write(new byte[(int) SegmentPool.MAX_SIZE]); buffer.write(new byte[(int) SegmentPool.MAX_SIZE]); assertEquals(0, SegmentPool.byteCount); // Recycle MAX_SIZE segments. They're all in the pool. buffer.readByteString(SegmentPool.MAX_SIZE); assertEquals(SegmentPool.MAX_SIZE, SegmentPool.byteCount); // Recycle MAX_SIZE more segments. The pool is full so they get garbage collected. buffer.readByteString(SegmentPool.MAX_SIZE); assertEquals(SegmentPool.MAX_SIZE, SegmentPool.byteCount); // Take MAX_SIZE segments to drain the pool. buffer.write(new byte[(int) SegmentPool.MAX_SIZE]); assertEquals(0, SegmentPool.byteCount); // Take MAX_SIZE more segments. The pool is drained so these will need to be allocated. buffer.write(new byte[(int) SegmentPool.MAX_SIZE]); assertEquals(0, SegmentPool.byteCount); } @Test public void moveBytesBetweenBuffersShareSegment() throws Exception { int size = (Segment.SIZE / 2) - 1; List segmentSizes = moveBytesBetweenBuffers(repeat('a', size), repeat('b', size)); assertEquals(asList(size * 2), segmentSizes); } @Test public void moveBytesBetweenBuffersReassignSegment() throws Exception { int size = (Segment.SIZE / 2) + 1; List segmentSizes = moveBytesBetweenBuffers(repeat('a', size), repeat('b', size)); assertEquals(asList(size, size), segmentSizes); } @Test public void moveBytesBetweenBuffersMultipleSegments() throws Exception { int size = 3 * Segment.SIZE + 1; List segmentSizes = moveBytesBetweenBuffers(repeat('a', size), repeat('b', size)); assertEquals(asList(Segment.SIZE, Segment.SIZE, Segment.SIZE, 1, Segment.SIZE, Segment.SIZE, Segment.SIZE, 1), segmentSizes); } private List moveBytesBetweenBuffers(String... contents) throws IOException { StringBuilder expected = new StringBuilder(); Buffer buffer = new Buffer(); for (String s : contents) { Buffer source = new Buffer(); source.writeUtf8(s); buffer.writeAll(source); expected.append(s); } List segmentSizes = buffer.segmentSizes(); assertEquals(expected.toString(), buffer.readUtf8(expected.length())); return segmentSizes; } /** The big part of source's first segment is being moved. */ @Test public void writeSplitSourceBufferLeft() throws Exception { int writeSize = Segment.SIZE / 2 + 1; Buffer sink = new Buffer(); sink.writeUtf8(repeat('b', Segment.SIZE - 10)); Buffer source = new Buffer(); source.writeUtf8(repeat('a', Segment.SIZE * 2)); sink.write(source, writeSize); assertEquals(asList(Segment.SIZE - 10, writeSize), sink.segmentSizes()); assertEquals(asList(Segment.SIZE - writeSize, Segment.SIZE), source.segmentSizes()); } /** The big part of source's first segment is staying put. */ @Test public void writeSplitSourceBufferRight() throws Exception { int writeSize = Segment.SIZE / 2 - 1; Buffer sink = new Buffer(); sink.writeUtf8(repeat('b', Segment.SIZE - 10)); Buffer source = new Buffer(); source.writeUtf8(repeat('a', Segment.SIZE * 2)); sink.write(source, writeSize); assertEquals(asList(Segment.SIZE - 10, writeSize), sink.segmentSizes()); assertEquals(asList(Segment.SIZE - writeSize, Segment.SIZE), source.segmentSizes()); } @Test public void writePrefixDoesntSplit() throws Exception { Buffer sink = new Buffer(); sink.writeUtf8(repeat('b', 10)); Buffer source = new Buffer(); source.writeUtf8(repeat('a', Segment.SIZE * 2)); sink.write(source, 20); assertEquals(asList(30), sink.segmentSizes()); assertEquals(asList(Segment.SIZE - 20, Segment.SIZE), source.segmentSizes()); assertEquals(30, sink.size()); assertEquals(Segment.SIZE * 2 - 20, source.size()); } @Test public void writePrefixDoesntSplitButRequiresCompact() throws Exception { Buffer sink = new Buffer(); sink.writeUtf8(repeat('b', Segment.SIZE - 10)); // limit = size - 10 sink.readUtf8(Segment.SIZE - 20); // pos = size = 20 Buffer source = new Buffer(); source.writeUtf8(repeat('a', Segment.SIZE * 2)); sink.write(source, 20); assertEquals(asList(30), sink.segmentSizes()); assertEquals(asList(Segment.SIZE - 20, Segment.SIZE), source.segmentSizes()); assertEquals(30, sink.size()); assertEquals(Segment.SIZE * 2 - 20, source.size()); } @Test public void copyToSpanningSegments() throws Exception { Buffer source = new Buffer(); source.writeUtf8(repeat('a', Segment.SIZE * 2)); source.writeUtf8(repeat('b', Segment.SIZE * 2)); ByteArrayOutputStream out = new ByteArrayOutputStream(); source.copyTo(out, 10, Segment.SIZE * 3); assertEquals(repeat('a', Segment.SIZE * 2 - 10) + repeat('b', Segment.SIZE + 10), out.toString()); assertEquals(repeat('a', Segment.SIZE * 2) + repeat('b', Segment.SIZE * 2), source.readUtf8(Segment.SIZE * 4)); } @Test public void copyToStream() throws Exception { Buffer buffer = new Buffer().writeUtf8("hello, world!"); ByteArrayOutputStream out = new ByteArrayOutputStream(); buffer.copyTo(out); String outString = new String(out.toByteArray(), UTF_8); assertEquals("hello, world!", outString); assertEquals("hello, world!", buffer.readUtf8()); } @Test public void writeToSpanningSegments() throws Exception { Buffer buffer = new Buffer(); buffer.writeUtf8(repeat('a', Segment.SIZE * 2)); buffer.writeUtf8(repeat('b', Segment.SIZE * 2)); ByteArrayOutputStream out = new ByteArrayOutputStream(); buffer.skip(10); buffer.writeTo(out, Segment.SIZE * 3); assertEquals(repeat('a', Segment.SIZE * 2 - 10) + repeat('b', Segment.SIZE + 10), out.toString()); assertEquals(repeat('b', Segment.SIZE - 10), buffer.readUtf8(buffer.size)); } @Test public void writeToStream() throws Exception { Buffer buffer = new Buffer().writeUtf8("hello, world!"); ByteArrayOutputStream out = new ByteArrayOutputStream(); buffer.writeTo(out); String outString = new String(out.toByteArray(), UTF_8); assertEquals("hello, world!", outString); assertEquals(0, buffer.size()); } @Test public void readFromStream() throws Exception { InputStream in = new ByteArrayInputStream("hello, world!".getBytes(UTF_8)); Buffer buffer = new Buffer(); buffer.readFrom(in); String out = buffer.readUtf8(); assertEquals("hello, world!", out); } @Test public void readFromSpanningSegments() throws Exception { InputStream in = new ByteArrayInputStream("hello, world!".getBytes(UTF_8)); Buffer buffer = new Buffer().writeUtf8(repeat('a', Segment.SIZE - 10)); buffer.readFrom(in); String out = buffer.readUtf8(); assertEquals(repeat('a', Segment.SIZE - 10) + "hello, world!", out); } @Test public void readFromStreamWithCount() throws Exception { InputStream in = new ByteArrayInputStream("hello, world!".getBytes(UTF_8)); Buffer buffer = new Buffer(); buffer.readFrom(in, 10); String out = buffer.readUtf8(); assertEquals("hello, wor", out); } @Test public void moveAllRequestedBytesWithRead() throws Exception { Buffer sink = new Buffer(); sink.writeUtf8(repeat('a', 10)); Buffer source = new Buffer(); source.writeUtf8(repeat('b', 15)); assertEquals(10, source.read(sink, 10)); assertEquals(20, sink.size()); assertEquals(5, source.size()); assertEquals(repeat('a', 10) + repeat('b', 10), sink.readUtf8(20)); } @Test public void moveFewerThanRequestedBytesWithRead() throws Exception { Buffer sink = new Buffer(); sink.writeUtf8(repeat('a', 10)); Buffer source = new Buffer(); source.writeUtf8(repeat('b', 20)); assertEquals(20, source.read(sink, 25)); assertEquals(30, sink.size()); assertEquals(0, source.size()); assertEquals(repeat('a', 10) + repeat('b', 20), sink.readUtf8(30)); } @Test public void indexOfWithOffset() throws Exception { Buffer buffer = new Buffer(); int halfSegment = Segment.SIZE / 2; buffer.writeUtf8(repeat('a', halfSegment)); buffer.writeUtf8(repeat('b', halfSegment)); buffer.writeUtf8(repeat('c', halfSegment)); buffer.writeUtf8(repeat('d', halfSegment)); assertEquals(0, buffer.indexOf((byte) 'a', 0)); assertEquals(halfSegment - 1, buffer.indexOf((byte) 'a', halfSegment - 1)); assertEquals(halfSegment, buffer.indexOf((byte) 'b', halfSegment - 1)); assertEquals(halfSegment * 2, buffer.indexOf((byte) 'c', halfSegment - 1)); assertEquals(halfSegment * 3, buffer.indexOf((byte) 'd', halfSegment - 1)); assertEquals(halfSegment * 3, buffer.indexOf((byte) 'd', halfSegment * 2)); assertEquals(halfSegment * 3, buffer.indexOf((byte) 'd', halfSegment * 3)); assertEquals(halfSegment * 4 - 1, buffer.indexOf((byte) 'd', halfSegment * 4 - 1)); } @Test public void byteAt() throws Exception { Buffer buffer = new Buffer(); buffer.writeUtf8("a"); buffer.writeUtf8(repeat('b', Segment.SIZE)); buffer.writeUtf8("c"); assertEquals('a', buffer.getByte(0)); assertEquals('a', buffer.getByte(0)); // getByte doesn't mutate! assertEquals('c', buffer.getByte(buffer.size - 1)); assertEquals('b', buffer.getByte(buffer.size - 2)); assertEquals('b', buffer.getByte(buffer.size - 3)); } @Test public void getByteOfEmptyBuffer() throws Exception { Buffer buffer = new Buffer(); try { buffer.getByte(0); fail(); } catch (IndexOutOfBoundsException expected) { } } @Test public void writePrefixToEmptyBuffer() throws IOException { Buffer sink = new Buffer(); Buffer source = new Buffer(); source.writeUtf8("abcd"); sink.write(source, 2); assertEquals("ab", sink.readUtf8(2)); } @Test public void cloneDoesNotObserveWritesToOriginal() throws Exception { Buffer original = new Buffer(); Buffer clone = original.clone(); original.writeUtf8("abc"); assertEquals(0, clone.size()); } @Test public void cloneDoesNotObserveReadsFromOriginal() throws Exception { Buffer original = new Buffer(); original.writeUtf8("abc"); Buffer clone = original.clone(); assertEquals("abc", original.readUtf8(3)); assertEquals(3, clone.size()); assertEquals("ab", clone.readUtf8(2)); } @Test public void originalDoesNotObserveWritesToClone() throws Exception { Buffer original = new Buffer(); Buffer clone = original.clone(); clone.writeUtf8("abc"); assertEquals(0, original.size()); } @Test public void originalDoesNotObserveReadsFromClone() throws Exception { Buffer original = new Buffer(); original.writeUtf8("abc"); Buffer clone = original.clone(); assertEquals("abc", clone.readUtf8(3)); assertEquals(3, original.size()); assertEquals("ab", original.readUtf8(2)); } @Test public void cloneMultipleSegments() throws Exception { Buffer original = new Buffer(); original.writeUtf8(repeat('a', Segment.SIZE * 3)); Buffer clone = original.clone(); original.writeUtf8(repeat('b', Segment.SIZE * 3)); clone.writeUtf8(repeat('c', Segment.SIZE * 3)); assertEquals(repeat('a', Segment.SIZE * 3) + repeat('b', Segment.SIZE * 3), original.readUtf8(Segment.SIZE * 6)); assertEquals(repeat('a', Segment.SIZE * 3) + repeat('c', Segment.SIZE * 3), clone.readUtf8(Segment.SIZE * 6)); } @Test public void equalsAndHashCodeEmpty() throws Exception { Buffer a = new Buffer(); Buffer b = new Buffer(); assertTrue(a.equals(b)); assertTrue(a.hashCode() == b.hashCode()); } @Test public void equalsAndHashCode() throws Exception { Buffer a = new Buffer().writeUtf8("dog"); Buffer b = new Buffer().writeUtf8("hotdog"); assertFalse(a.equals(b)); assertFalse(a.hashCode() == b.hashCode()); b.readUtf8(3); // Leaves b containing 'dog'. assertTrue(a.equals(b)); assertTrue(a.hashCode() == b.hashCode()); } @Test public void equalsAndHashCodeSpanningSegments() throws Exception { byte[] data = new byte[1024 * 1024]; Random dice = new Random(0); dice.nextBytes(data); Buffer a = bufferWithRandomSegmentLayout(dice, data); Buffer b = bufferWithRandomSegmentLayout(dice, data); assertTrue(a.equals(b)); assertTrue(a.hashCode() == b.hashCode()); data[data.length / 2]++; // Change a single byte. Buffer c = bufferWithRandomSegmentLayout(dice, data); assertFalse(a.equals(c)); assertFalse(a.hashCode() == c.hashCode()); } @Test public void bufferInputStreamByteByByte() throws Exception { Buffer source = new Buffer(); source.writeUtf8("abc"); InputStream in = source.inputStream(); assertEquals(3, in.available()); assertEquals('a', in.read()); assertEquals('b', in.read()); assertEquals('c', in.read()); assertEquals(-1, in.read()); assertEquals(0, in.available()); } @Test public void bufferInputStreamBulkReads() throws Exception { Buffer source = new Buffer(); source.writeUtf8("abc"); byte[] byteArray = new byte[4]; Arrays.fill(byteArray, (byte) -5); InputStream in = source.inputStream(); assertEquals(3, in.read(byteArray)); assertEquals("[97, 98, 99, -5]", Arrays.toString(byteArray)); Arrays.fill(byteArray, (byte) -7); assertEquals(-1, in.read(byteArray)); assertEquals("[-7, -7, -7, -7]", Arrays.toString(byteArray)); } /** * When writing data that's already buffered, there's no reason to page the * data by segment. */ @Test public void readAllWritesAllSegmentsAtOnce() throws Exception { Buffer write1 = new Buffer().writeUtf8("" + TestUtil.repeat('a', Segment.SIZE) + TestUtil.repeat('b', Segment.SIZE) + TestUtil.repeat('c', Segment.SIZE)); Buffer source = new Buffer().writeUtf8("" + TestUtil.repeat('a', Segment.SIZE) + TestUtil.repeat('b', Segment.SIZE) + TestUtil.repeat('c', Segment.SIZE)); MockSink mockSink = new MockSink(); assertEquals(Segment.SIZE * 3, source.readAll(mockSink)); assertEquals(0, source.size()); mockSink.assertLog("write(" + write1 + ", " + write1.size() + ")"); } @Test public void writeAllMultipleSegments() throws Exception { Buffer source = new Buffer().writeUtf8(TestUtil.repeat('a', Segment.SIZE * 3)); Buffer sink = new Buffer(); assertEquals(Segment.SIZE * 3, sink.writeAll(source)); assertEquals(0, source.size()); assertEquals(TestUtil.repeat('a', Segment.SIZE * 3), sink.readUtf8()); } @Test public void copyTo() throws Exception { Buffer source = new Buffer(); source.writeUtf8("party"); Buffer target = new Buffer(); source.copyTo(target, 1, 3); assertEquals("art", target.readUtf8()); assertEquals("party", source.readUtf8()); } @Test public void copyToOnSegmentBoundary() throws Exception { String as = repeat('a', Segment.SIZE); String bs = repeat('b', Segment.SIZE); String cs = repeat('c', Segment.SIZE); String ds = repeat('d', Segment.SIZE); Buffer source = new Buffer(); source.writeUtf8(as); source.writeUtf8(bs); source.writeUtf8(cs); Buffer target = new Buffer(); target.writeUtf8(ds); source.copyTo(target, as.length(), bs.length() + cs.length()); assertEquals(ds + bs + cs, target.readUtf8()); } @Test public void copyToOffSegmentBoundary() throws Exception { String as = repeat('a', Segment.SIZE - 1); String bs = repeat('b', Segment.SIZE + 2); String cs = repeat('c', Segment.SIZE - 4); String ds = repeat('d', Segment.SIZE + 8); Buffer source = new Buffer(); source.writeUtf8(as); source.writeUtf8(bs); source.writeUtf8(cs); Buffer target = new Buffer(); target.writeUtf8(ds); source.copyTo(target, as.length(), bs.length() + cs.length()); assertEquals(ds + bs + cs, target.readUtf8()); } @Test public void copyToSourceAndTargetCanBeTheSame() throws Exception { String as = repeat('a', Segment.SIZE); String bs = repeat('b', Segment.SIZE); Buffer source = new Buffer(); source.writeUtf8(as); source.writeUtf8(bs); source.copyTo(source, 0, source.size()); assertEquals(as + bs + as + bs, source.readUtf8()); } @Test public void copyToEmptySource() throws Exception { Buffer source = new Buffer(); Buffer target = new Buffer().writeUtf8("aaa"); source.copyTo(target, 0L, 0L); assertEquals("", source.readUtf8()); assertEquals("aaa", target.readUtf8()); } @Test public void copyToEmptyTarget() throws Exception { Buffer source = new Buffer().writeUtf8("aaa"); Buffer target = new Buffer(); source.copyTo(target, 0L, 3L); assertEquals("aaa", source.readUtf8()); assertEquals("aaa", target.readUtf8()); } @Test public void snapshotReportsAccurateSize() throws Exception { Buffer buf = new Buffer().write(new byte[] { 0, 1, 2, 3 }); assertEquals(1, buf.snapshot(1).size()); } } okio-okio-parent-1.16.0/okio/src/test/java/okio/BufferedSinkTest.java000066400000000000000000000262131335667376100254560ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.EOFException; import java.io.IOException; import java.io.OutputStream; import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.Arrays; import java.util.List; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; import static java.util.Arrays.asList; import static okio.TestUtil.repeat; import static okio.Util.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @RunWith(Parameterized.class) public final class BufferedSinkTest { private interface Factory { Factory BUFFER = new Factory() { @Override public BufferedSink create(Buffer data) { return data; } @Override public String toString() { return "Buffer"; } }; Factory REAL_BUFFERED_SINK = new Factory() { @Override public BufferedSink create(Buffer data) { return new RealBufferedSink(data); } @Override public String toString() { return "RealBufferedSink"; } }; BufferedSink create(Buffer data); } @Parameters(name = "{0}") public static List parameters() { return Arrays.asList( new Object[] {Factory.BUFFER}, new Object[] {Factory.REAL_BUFFERED_SINK}); } @Parameter public Factory factory; private Buffer data; private BufferedSink sink; @Before public void setUp() { data = new Buffer(); sink = factory.create(data); } @Test public void writeNothing() throws IOException { sink.writeUtf8(""); sink.flush(); assertEquals(0, data.size()); } @Test public void writeBytes() throws Exception { sink.writeByte(0xab); sink.writeByte(0xcd); sink.flush(); assertEquals("[hex=abcd]", data.toString()); } @Test public void writeLastByteInSegment() throws Exception { sink.writeUtf8(repeat('a', Segment.SIZE - 1)); sink.writeByte(0x20); sink.writeByte(0x21); sink.flush(); assertEquals(asList(Segment.SIZE, 1), data.segmentSizes()); assertEquals(repeat('a', Segment.SIZE - 1), data.readUtf8(Segment.SIZE - 1)); assertEquals("[text= !]", data.toString()); } @Test public void writeShort() throws Exception { sink.writeShort(0xabcd); sink.writeShort(0x4321); sink.flush(); assertEquals("[hex=abcd4321]", data.toString()); } @Test public void writeShortLe() throws Exception { sink.writeShortLe(0xcdab); sink.writeShortLe(0x2143); sink.flush(); assertEquals("[hex=abcd4321]", data.toString()); } @Test public void writeInt() throws Exception { sink.writeInt(0xabcdef01); sink.writeInt(0x87654321); sink.flush(); assertEquals("[hex=abcdef0187654321]", data.toString()); } @Test public void writeLastIntegerInSegment() throws Exception { sink.writeUtf8(repeat('a', Segment.SIZE - 4)); sink.writeInt(0xabcdef01); sink.writeInt(0x87654321); sink.flush(); assertEquals(asList(Segment.SIZE, 4), data.segmentSizes()); assertEquals(repeat('a', Segment.SIZE - 4), data.readUtf8(Segment.SIZE - 4)); assertEquals("[hex=abcdef0187654321]", data.toString()); } @Test public void writeIntegerDoesNotQuiteFitInSegment() throws Exception { sink.writeUtf8(repeat('a', Segment.SIZE - 3)); sink.writeInt(0xabcdef01); sink.writeInt(0x87654321); sink.flush(); assertEquals(asList(Segment.SIZE - 3, 8), data.segmentSizes()); assertEquals(repeat('a', Segment.SIZE - 3), data.readUtf8(Segment.SIZE - 3)); assertEquals("[hex=abcdef0187654321]", data.toString()); } @Test public void writeIntLe() throws Exception { sink.writeIntLe(0xabcdef01); sink.writeIntLe(0x87654321); sink.flush(); assertEquals("[hex=01efcdab21436587]", data.toString()); } @Test public void writeLong() throws Exception { sink.writeLong(0xabcdef0187654321L); sink.writeLong(0xcafebabeb0b15c00L); sink.flush(); assertEquals("[hex=abcdef0187654321cafebabeb0b15c00]", data.toString()); } @Test public void writeLongLe() throws Exception { sink.writeLongLe(0xabcdef0187654321L); sink.writeLongLe(0xcafebabeb0b15c00L); sink.flush(); assertEquals("[hex=2143658701efcdab005cb1b0bebafeca]", data.toString()); } @Test public void writeStringUtf8() throws IOException { sink.writeUtf8("təˈranəˌsôr"); sink.flush(); assertEquals(ByteString.decodeHex("74c999cb8872616ec999cb8c73c3b472"), data.readByteString()); } @Test public void writeSubstringUtf8() throws IOException { sink.writeUtf8("təˈranəˌsôr", 3, 7); sink.flush(); assertEquals(ByteString.decodeHex("72616ec999"), data.readByteString()); } @Test public void writeStringWithCharset() throws IOException { sink.writeString("təˈranəˌsôr", Charset.forName("utf-32be")); sink.flush(); assertEquals(ByteString.decodeHex("0000007400000259000002c800000072000000610000006e00000259" + "000002cc00000073000000f400000072"), data.readByteString()); } @Test public void writeSubstringWithCharset() throws IOException { sink.writeString("təˈranəˌsôr", 3, 7, Charset.forName("utf-32be")); sink.flush(); assertEquals(ByteString.decodeHex("00000072000000610000006e00000259"), data.readByteString()); } @Test public void writeUtf8SubstringWithCharset() throws IOException { sink.writeString("təˈranəˌsôr", 3, 7, Charset.forName("utf-8")); sink.flush(); assertEquals(ByteString.encodeUtf8("ranə"), data.readByteString()); } @Test public void writeAll() throws Exception { Buffer source = new Buffer().writeUtf8("abcdef"); assertEquals(6, sink.writeAll(source)); assertEquals(0, source.size()); sink.flush(); assertEquals("abcdef", data.readUtf8()); } @Test public void writeSource() throws Exception { Buffer source = new Buffer().writeUtf8("abcdef"); // Force resolution of the Source method overload. sink.write((Source) source, 4); sink.flush(); assertEquals("abcd", data.readUtf8()); assertEquals("ef", source.readUtf8()); } @Test public void writeSourceReadsFully() throws Exception { Source source = new ForwardingSource(new Buffer()) { @Override public long read(Buffer sink, long byteCount) throws IOException { sink.writeUtf8("abcd"); return 4; } }; sink.write(source, 8); sink.flush(); assertEquals("abcdabcd", data.readUtf8()); } @Test public void writeSourcePropagatesEof() throws IOException { Source source = new Buffer().writeUtf8("abcd"); try { sink.write(source, 8); fail(); } catch (EOFException expected) { } // Ensure that whatever was available was correctly written. sink.flush(); assertEquals("abcd", data.readUtf8()); } @Test public void writeSourceWithZeroIsNoOp() throws IOException { // This test ensures that a zero byte count never calls through to read the source. It may be // tied to something like a socket which will potentially block trying to read a segment when // ultimately we don't want any data. Source source = new ForwardingSource(new Buffer()) { @Override public long read(Buffer sink, long byteCount) throws IOException { throw new AssertionError(); } }; sink.write(source, 0); assertEquals(0, data.size()); } @Test public void writeAllExhausted() throws Exception { Buffer source = new Buffer(); assertEquals(0, sink.writeAll(source)); assertEquals(0, source.size()); } @Test public void closeEmitsBufferedBytes() throws IOException { sink.writeByte('a'); sink.close(); assertEquals('a', data.readByte()); } @Test public void outputStream() throws Exception { OutputStream out = sink.outputStream(); out.write('a'); out.write(repeat('b', 9998).getBytes(UTF_8)); out.write('c'); out.flush(); assertEquals("a" + repeat('b', 9998) + "c", data.readUtf8()); } @Test public void outputStreamBounds() throws Exception { OutputStream out = sink.outputStream(); try { out.write(new byte[100], 50, 51); fail(); } catch (ArrayIndexOutOfBoundsException expected) { } } @Test public void longDecimalString() throws IOException { assertLongDecimalString(0); assertLongDecimalString(Long.MIN_VALUE); assertLongDecimalString(Long.MAX_VALUE); for (int i = 1; i < 20; i++) { long value = BigInteger.valueOf(10L).pow(i).longValue(); assertLongDecimalString(value - 1); assertLongDecimalString(value); } } private void assertLongDecimalString(long value) throws IOException { sink.writeDecimalLong(value).writeUtf8("zzz").flush(); String expected = Long.toString(value) + "zzz"; String actual = data.readUtf8(); assertEquals(value + " expected " + expected + " but was " + actual, actual, expected); } @Test public void longHexString() throws IOException { assertLongHexString(0); assertLongHexString(Long.MIN_VALUE); assertLongHexString(Long.MAX_VALUE); for (int i = 0; i < 16; i++) { assertLongHexString((1 << i) - 1); assertLongHexString(1 << i); } } @Test public void writeNioBuffer() throws Exception { String expected = "abcdefg"; ByteBuffer nioByteBuffer = ByteBuffer.allocate(1024); nioByteBuffer.put("abcdefg".getBytes(Util.UTF_8)); nioByteBuffer.flip(); int byteCount = sink.write(nioByteBuffer); assertEquals(expected.length(), byteCount); assertEquals(expected.length(), nioByteBuffer.position()); assertEquals(expected.length(), nioByteBuffer.limit()); sink.flush(); assertEquals(expected, data.readUtf8()); } @Test public void writeLargeNioBufferWritesAllData() throws Exception { String expected = TestUtil.repeat('a', Segment.SIZE * 3); ByteBuffer nioByteBuffer = ByteBuffer.allocate(Segment.SIZE * 4); nioByteBuffer.put(TestUtil.repeat('a', Segment.SIZE * 3).getBytes(Util.UTF_8)); nioByteBuffer.flip(); int byteCount = sink.write(nioByteBuffer); assertEquals(expected.length(), byteCount); assertEquals(expected.length(), nioByteBuffer.position()); assertEquals(expected.length(), nioByteBuffer.limit()); sink.flush(); assertEquals(expected, data.readUtf8()); } private void assertLongHexString(long value) throws IOException { sink.writeHexadecimalUnsignedLong(value).writeUtf8("zzz").flush(); String expected = String.format("%x", value) + "zzz"; String actual = data.readUtf8(); assertEquals(value + " expected " + expected + " but was " + actual, actual, expected); } } okio-okio-parent-1.16.0/okio/src/test/java/okio/BufferedSourceTest.java000066400000000000000000001213551335667376100260150ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.Arrays; import java.util.List; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; import static okio.TestUtil.assertByteArrayEquals; import static okio.TestUtil.assertByteArraysEquals; import static okio.TestUtil.repeat; import static okio.Util.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; @RunWith(Parameterized.class) public final class BufferedSourceTest { interface Factory { Factory BUFFER = new Factory() { @Override public Pipe pipe() { Buffer buffer = new Buffer(); Pipe result = new Pipe(); result.sink = buffer; result.source = buffer; return result; } @Override public String toString() { return "Buffer"; } }; Factory REAL_BUFFERED_SOURCE = new Factory() { @Override public Pipe pipe() { Buffer buffer = new Buffer(); Pipe result = new Pipe(); result.sink = buffer; result.source = new RealBufferedSource(buffer); return result; } @Override public String toString() { return "RealBufferedSource"; } }; /** * A factory deliberately written to create buffers whose internal segments are always 1 byte * long. We like testing with these segments because are likely to trigger bugs! */ Factory ONE_BYTE_AT_A_TIME = new Factory() { @Override public Pipe pipe() { Buffer buffer = new Buffer(); Pipe result = new Pipe(); result.sink = buffer; result.source = new RealBufferedSource(new ForwardingSource(buffer) { @Override public long read(Buffer sink, long byteCount) throws IOException { // This reads a byte into a new buffer, then clones it so that the segments are shared. // Shared segments cannot be compacted so we'll get a long chain of short segments. Buffer box = new Buffer(); long result = super.read(box, Math.min(byteCount, 1L)); if (result > 0L) sink.write(box.clone(), result); return result; } }); return result; } @Override public String toString() { return "OneByteAtATime"; } }; Factory PEEK_BUFFER = new Factory() { @Override public Pipe pipe() { Buffer buffer = new Buffer(); Pipe result = new Pipe(); result.sink = buffer; result.source = buffer.peek(); return result; } @Override public String toString() { return "PeekBuffer"; } }; Factory PEEK_BUFFERED_SOURCE = new Factory() { @Override public Pipe pipe() { Buffer buffer = new Buffer(); Pipe result = new Pipe(); result.sink = buffer; result.source = Okio.buffer((Source) buffer).peek(); return result; } @Override public String toString() { return "PeekBufferedSource"; } }; Pipe pipe(); } private static class Pipe { BufferedSink sink; BufferedSource source; } @Parameters(name = "{0}") public static List parameters() { return Arrays.asList( new Object[] { Factory.BUFFER}, new Object[] { Factory.REAL_BUFFERED_SOURCE}, new Object[] { Factory.ONE_BYTE_AT_A_TIME}, new Object[] { Factory.PEEK_BUFFER }, new Object[] { Factory.PEEK_BUFFERED_SOURCE }); } @Parameter public Factory factory; private BufferedSink sink; private BufferedSource source; @Before public void setUp() { Pipe pipe = factory.pipe(); sink = pipe.sink; source = pipe.source; } @Test public void readBytes() throws Exception { sink.write(new byte[] { (byte) 0xab, (byte) 0xcd }); assertEquals(0xab, source.readByte() & 0xff); assertEquals(0xcd, source.readByte() & 0xff); assertTrue(source.exhausted()); } @Test public void readShort() throws Exception { sink.write(new byte[] { (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x01 }); assertEquals((short) 0xabcd, source.readShort()); assertEquals((short) 0xef01, source.readShort()); assertTrue(source.exhausted()); } @Test public void readShortLe() throws Exception { sink.write(new byte[] { (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x10 }); assertEquals((short) 0xcdab, source.readShortLe()); assertEquals((short) 0x10ef, source.readShortLe()); assertTrue(source.exhausted()); } @Test public void readShortSplitAcrossMultipleSegments() throws Exception { sink.writeUtf8(repeat('a', Segment.SIZE - 1)); sink.write(new byte[] { (byte) 0xab, (byte) 0xcd }); source.skip(Segment.SIZE - 1); assertEquals((short) 0xabcd, source.readShort()); assertTrue(source.exhausted()); } @Test public void readInt() throws Exception { sink.write(new byte[] { (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x01, (byte) 0x87, (byte) 0x65, (byte) 0x43, (byte) 0x21 }); assertEquals(0xabcdef01, source.readInt()); assertEquals(0x87654321, source.readInt()); assertTrue(source.exhausted()); } @Test public void readIntLe() throws Exception { sink.write(new byte[] { (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x10, (byte) 0x87, (byte) 0x65, (byte) 0x43, (byte) 0x21 }); assertEquals(0x10efcdab, source.readIntLe()); assertEquals(0x21436587, source.readIntLe()); assertTrue(source.exhausted()); } @Test public void readIntSplitAcrossMultipleSegments() throws Exception { sink.writeUtf8(repeat('a', Segment.SIZE - 3)); sink.write(new byte[] { (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x01 }); source.skip(Segment.SIZE - 3); assertEquals(0xabcdef01, source.readInt()); assertTrue(source.exhausted()); } @Test public void readLong() throws Exception { sink.write(new byte[] { (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x10, (byte) 0x87, (byte) 0x65, (byte) 0x43, (byte) 0x21, (byte) 0x36, (byte) 0x47, (byte) 0x58, (byte) 0x69, (byte) 0x12, (byte) 0x23, (byte) 0x34, (byte) 0x45 }); assertEquals(0xabcdef1087654321L, source.readLong()); assertEquals(0x3647586912233445L, source.readLong()); assertTrue(source.exhausted()); } @Test public void readLongLe() throws Exception { sink.write(new byte[] { (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x10, (byte) 0x87, (byte) 0x65, (byte) 0x43, (byte) 0x21, (byte) 0x36, (byte) 0x47, (byte) 0x58, (byte) 0x69, (byte) 0x12, (byte) 0x23, (byte) 0x34, (byte) 0x45 }); assertEquals(0x2143658710efcdabL, source.readLongLe()); assertEquals(0x4534231269584736L, source.readLongLe()); assertTrue(source.exhausted()); } @Test public void readLongSplitAcrossMultipleSegments() throws Exception { sink.writeUtf8(repeat('a', Segment.SIZE - 7)); sink.write(new byte[] { (byte) 0xab, (byte) 0xcd, (byte) 0xef, (byte) 0x01, (byte) 0x87, (byte) 0x65, (byte) 0x43, (byte) 0x21, }); source.skip(Segment.SIZE - 7); assertEquals(0xabcdef0187654321L, source.readLong()); assertTrue(source.exhausted()); } @Test public void readAll() throws IOException { source.buffer().writeUtf8("abc"); sink.writeUtf8("def"); Buffer sink = new Buffer(); assertEquals(6, source.readAll(sink)); assertEquals("abcdef", sink.readUtf8()); assertTrue(source.exhausted()); } @Test public void readAllExhausted() throws IOException { MockSink mockSink = new MockSink(); assertEquals(0, source.readAll(mockSink)); assertTrue(source.exhausted()); mockSink.assertLog(); } @Test public void readExhaustedSource() throws Exception { Buffer sink = new Buffer(); sink.writeUtf8(repeat('a', 10)); assertEquals(-1, source.read(sink, 10)); assertEquals(10, sink.size()); assertTrue(source.exhausted()); } @Test public void readZeroBytesFromSource() throws Exception { Buffer sink = new Buffer(); sink.writeUtf8(repeat('a', 10)); // Either 0 or -1 is reasonable here. For consistency with Android's // ByteArrayInputStream we return 0. assertEquals(-1, source.read(sink, 0)); assertEquals(10, sink.size()); assertTrue(source.exhausted()); } @Test public void readFully() throws Exception { sink.writeUtf8(repeat('a', 10000)); Buffer sink = new Buffer(); source.readFully(sink, 9999); assertEquals(repeat('a', 9999), sink.readUtf8()); assertEquals("a", source.readUtf8()); } @Test public void readFullyTooShortThrows() throws IOException { sink.writeUtf8("Hi"); Buffer sink = new Buffer(); try { source.readFully(sink, 5); fail(); } catch (EOFException ignored) { } // Verify we read all that we could from the source. assertEquals("Hi", sink.readUtf8()); } @Test public void readFullyByteArray() throws IOException { Buffer data = new Buffer(); data.writeUtf8("Hello").writeUtf8(repeat('e', Segment.SIZE)); byte[] expected = data.clone().readByteArray(); sink.write(data, data.size()); byte[] sink = new byte[Segment.SIZE + 5]; source.readFully(sink); assertByteArraysEquals(expected, sink); } @Test public void readFullyByteArrayTooShortThrows() throws IOException { sink.writeUtf8("Hello"); byte[] sink = new byte[6]; try { source.readFully(sink); fail(); } catch (EOFException ignored) { } // Verify we read all that we could from the source. assertByteArraysEquals(new byte[] { 'H', 'e', 'l', 'l', 'o', 0 }, sink); } @Test public void readIntoByteArray() throws IOException { sink.writeUtf8("abcd"); byte[] sink = new byte[3]; int read = source.read(sink); if (factory == Factory.ONE_BYTE_AT_A_TIME) { assertEquals(1, read); byte[] expected = { 'a', 0, 0 }; assertByteArraysEquals(expected, sink); } else { assertEquals(3, read); byte[] expected = { 'a', 'b', 'c' }; assertByteArraysEquals(expected, sink); } } @Test public void readIntoByteArrayNotEnough() throws IOException { sink.writeUtf8("abcd"); byte[] sink = new byte[5]; int read = source.read(sink); if (factory == Factory.ONE_BYTE_AT_A_TIME) { assertEquals(1, read); byte[] expected = { 'a', 0, 0, 0, 0 }; assertByteArraysEquals(expected, sink); } else { assertEquals(4, read); byte[] expected = { 'a', 'b', 'c', 'd', 0 }; assertByteArraysEquals(expected, sink); } } @Test public void readIntoByteArrayOffsetAndCount() throws IOException { sink.writeUtf8("abcd"); byte[] sink = new byte[7]; int read = source.read(sink, 2, 3); if (factory == Factory.ONE_BYTE_AT_A_TIME) { assertEquals(1, read); byte[] expected = { 0, 0, 'a', 0, 0, 0, 0 }; assertByteArraysEquals(expected, sink); } else { assertEquals(3, read); byte[] expected = { 0, 0, 'a', 'b', 'c', 0, 0 }; assertByteArraysEquals(expected, sink); } } @Test public void readByteArray() throws IOException { String string = "abcd" + repeat('e', Segment.SIZE); sink.writeUtf8(string); assertByteArraysEquals(string.getBytes(UTF_8), source.readByteArray()); } @Test public void readByteArrayPartial() throws IOException { sink.writeUtf8("abcd"); assertEquals("[97, 98, 99]", Arrays.toString(source.readByteArray(3))); assertEquals("d", source.readUtf8(1)); } @Test public void readByteString() throws IOException { sink.writeUtf8("abcd").writeUtf8(repeat('e', Segment.SIZE)); assertEquals("abcd" + repeat('e', Segment.SIZE), source.readByteString().utf8()); } @Test public void readByteStringPartial() throws IOException { sink.writeUtf8("abcd").writeUtf8(repeat('e', Segment.SIZE)); assertEquals("abc", source.readByteString(3).utf8()); assertEquals("d", source.readUtf8(1)); } @Test public void readSpecificCharsetPartial() throws Exception { sink.write( ByteString.decodeHex("0000007600000259000002c80000006c000000e40000007300000259" + "000002cc000000720000006100000070000000740000025900000072")); assertEquals("vəˈläsə", source.readString(7 * 4, Charset.forName("utf-32"))); } @Test public void readSpecificCharset() throws Exception { sink.write( ByteString.decodeHex("0000007600000259000002c80000006c000000e40000007300000259" + "000002cc000000720000006100000070000000740000025900000072")); assertEquals("vəˈläsəˌraptər", source.readString(Charset.forName("utf-32"))); } @Test public void readUtf8SpansSegments() throws Exception { sink.writeUtf8(repeat('a', Segment.SIZE * 2)); source.skip(Segment.SIZE - 1); assertEquals("aa", source.readUtf8(2)); } @Test public void readUtf8Segment() throws Exception { sink.writeUtf8(repeat('a', Segment.SIZE)); assertEquals(repeat('a', Segment.SIZE), source.readUtf8(Segment.SIZE)); } @Test public void readUtf8PartialBuffer() throws Exception { sink.writeUtf8(repeat('a', Segment.SIZE + 20)); assertEquals(repeat('a', Segment.SIZE + 10), source.readUtf8(Segment.SIZE + 10)); } @Test public void readUtf8EntireBuffer() throws Exception { sink.writeUtf8(repeat('a', Segment.SIZE * 2)); assertEquals(repeat('a', Segment.SIZE * 2), source.readUtf8()); } @Test public void skip() throws Exception { sink.writeUtf8("a"); sink.writeUtf8(repeat('b', Segment.SIZE)); sink.writeUtf8("c"); source.skip(1); assertEquals('b', source.readByte() & 0xff); source.skip(Segment.SIZE - 2); assertEquals('b', source.readByte() & 0xff); source.skip(1); assertTrue(source.exhausted()); } @Test public void skipInsufficientData() throws Exception { sink.writeUtf8("a"); try { source.skip(2); fail(); } catch (EOFException ignored) { } } @Test public void indexOf() throws Exception { // The segment is empty. assertEquals(-1, source.indexOf((byte) 'a')); // The segment has one value. sink.writeUtf8("a"); // a assertEquals(0, source.indexOf((byte) 'a')); assertEquals(-1, source.indexOf((byte) 'b')); // The segment has lots of data. sink.writeUtf8(repeat('b', Segment.SIZE - 2)); // ab...b assertEquals(0, source.indexOf((byte) 'a')); assertEquals(1, source.indexOf((byte) 'b')); assertEquals(-1, source.indexOf((byte) 'c')); // The segment doesn't start at 0, it starts at 2. source.skip(2); // b...b assertEquals(-1, source.indexOf((byte) 'a')); assertEquals(0, source.indexOf((byte) 'b')); assertEquals(-1, source.indexOf((byte) 'c')); // The segment is full. sink.writeUtf8("c"); // b...bc assertEquals(-1, source.indexOf((byte) 'a')); assertEquals(0, source.indexOf((byte) 'b')); assertEquals(Segment.SIZE - 3, source.indexOf((byte) 'c')); // The segment doesn't start at 2, it starts at 4. source.skip(2); // b...bc assertEquals(-1, source.indexOf((byte) 'a')); assertEquals(0, source.indexOf((byte) 'b')); assertEquals(Segment.SIZE - 5, source.indexOf((byte) 'c')); // Two segments. sink.writeUtf8("d"); // b...bcd, d is in the 2nd segment. assertEquals(Segment.SIZE - 4, source.indexOf((byte) 'd')); assertEquals(-1, source.indexOf((byte) 'e')); } @Test public void indexOfByteWithStartOffset() throws IOException { sink.writeUtf8("a").writeUtf8(repeat('b', Segment.SIZE)).writeUtf8("c"); assertEquals(-1, source.indexOf((byte) 'a', 1)); assertEquals(15, source.indexOf((byte) 'b', 15)); } @Test public void indexOfByteWithBothOffsets() throws IOException { if (factory == Factory.ONE_BYTE_AT_A_TIME) { // When run on Travis, ONE_BYTE_AT_A_TIME // causes out-of-memory errors. return; } byte a = (byte) 'a'; byte c = (byte) 'c'; int size = Segment.SIZE * 5; byte[] bytes = new byte[size]; Arrays.fill(bytes, a); // These are tricky places where the buffer // starts, ends, or segments come together. int[] points = { 0, 1, 2, Segment.SIZE - 1, Segment.SIZE, Segment.SIZE + 1, size / 2 - 1, size / 2, size / 2 + 1, size - Segment.SIZE - 1, size - Segment.SIZE, size - Segment.SIZE + 1, size - 3, size - 2, size - 1 }; // In each iteration, we write c to the known point and then search for it using different // windows. Some of the windows don't overlap with c's position, and therefore a match shouldn't // be found. for (int p : points) { bytes[p] = c; sink.write(bytes); assertEquals( p, source.indexOf(c, 0, size )); assertEquals( p, source.indexOf(c, 0, p + 1 )); assertEquals( p, source.indexOf(c, p, size )); assertEquals( p, source.indexOf(c, p, p + 1 )); assertEquals( p, source.indexOf(c, p / 2, p * 2 + 1)); assertEquals(-1, source.indexOf(c, 0, p / 2 )); assertEquals(-1, source.indexOf(c, 0, p )); assertEquals(-1, source.indexOf(c, 0, 0 )); assertEquals(-1, source.indexOf(c, p, p )); // Reset. source.readUtf8(); bytes[p] = a; } } @Test public void indexOfByteInvalidBoundsThrows() throws IOException { sink.writeUtf8("abc"); try { source.indexOf((byte) 'a', -1); fail("Expected failure: fromIndex < 0"); } catch (IllegalArgumentException expected) { } try { source.indexOf((byte) 'a', 10, 0); fail("Expected failure: fromIndex > toIndex"); } catch (IllegalArgumentException expected) { } } @Test public void indexOfByteString() throws IOException { assertEquals(-1, source.indexOf(ByteString.encodeUtf8("flop"))); sink.writeUtf8("flip flop"); assertEquals(5, source.indexOf(ByteString.encodeUtf8("flop"))); source.readUtf8(); // Clear stream. // Make sure we backtrack and resume searching after partial match. sink.writeUtf8("hi hi hi hey"); assertEquals(3, source.indexOf(ByteString.encodeUtf8("hi hi hey"))); } @Test public void indexOfByteStringAtSegmentBoundary() throws IOException { sink.writeUtf8(repeat('a', Segment.SIZE - 1)); sink.writeUtf8("bcd"); assertEquals(Segment.SIZE - 3, source.indexOf(ByteString.encodeUtf8("aabc"), Segment.SIZE - 4)); assertEquals(Segment.SIZE - 3, source.indexOf(ByteString.encodeUtf8("aabc"), Segment.SIZE - 3)); assertEquals(Segment.SIZE - 2, source.indexOf(ByteString.encodeUtf8("abcd"), Segment.SIZE - 2)); assertEquals(Segment.SIZE - 2, source.indexOf(ByteString.encodeUtf8("abc"), Segment.SIZE - 2)); assertEquals(Segment.SIZE - 2, source.indexOf(ByteString.encodeUtf8("abc"), Segment.SIZE - 2)); assertEquals(Segment.SIZE - 2, source.indexOf(ByteString.encodeUtf8("ab"), Segment.SIZE - 2)); assertEquals(Segment.SIZE - 2, source.indexOf(ByteString.encodeUtf8("a"), Segment.SIZE - 2)); assertEquals(Segment.SIZE - 1, source.indexOf(ByteString.encodeUtf8("bc"), Segment.SIZE - 2)); assertEquals(Segment.SIZE - 1, source.indexOf(ByteString.encodeUtf8("b"), Segment.SIZE - 2)); assertEquals(Segment.SIZE, source.indexOf(ByteString.encodeUtf8("c"), Segment.SIZE - 2)); assertEquals(Segment.SIZE, source.indexOf(ByteString.encodeUtf8("c"), Segment.SIZE )); assertEquals(Segment.SIZE + 1, source.indexOf(ByteString.encodeUtf8("d"), Segment.SIZE - 2)); assertEquals(Segment.SIZE + 1, source.indexOf(ByteString.encodeUtf8("d"), Segment.SIZE + 1)); } @Test public void indexOfDoesNotWrapAround() throws IOException { sink.writeUtf8(repeat('a', Segment.SIZE - 1)); sink.writeUtf8("bcd"); assertEquals(-1, source.indexOf(ByteString.encodeUtf8("abcda"), Segment.SIZE - 3)); } @Test public void indexOfByteStringWithOffset() throws IOException { assertEquals(-1, source.indexOf(ByteString.encodeUtf8("flop"), 1)); sink.writeUtf8("flop flip flop"); assertEquals(10, source.indexOf(ByteString.encodeUtf8("flop"), 1)); source.readUtf8(); // Clear stream // Make sure we backtrack and resume searching after partial match. sink.writeUtf8("hi hi hi hi hey"); assertEquals(6, source.indexOf(ByteString.encodeUtf8("hi hi hey"), 1)); } @Test public void indexOfByteStringInvalidArgumentsThrows() throws IOException { try { source.indexOf(ByteString.of()); fail(); } catch (IllegalArgumentException e) { assertEquals("bytes is empty", e.getMessage()); } try { source.indexOf(ByteString.encodeUtf8("hi"), -1); fail(); } catch (IllegalArgumentException e) { assertEquals("fromIndex < 0", e.getMessage()); } } /** * With {@link Factory#ONE_BYTE_AT_A_TIME}, this code was extremely slow. * https://github.com/square/okio/issues/171 */ @Test public void indexOfByteStringAcrossSegmentBoundaries() throws IOException { sink.writeUtf8(repeat('a', Segment.SIZE * 2 - 3)); sink.writeUtf8("bcdefg"); assertEquals(Segment.SIZE * 2 - 4, source.indexOf(ByteString.encodeUtf8("ab"))); assertEquals(Segment.SIZE * 2 - 4, source.indexOf(ByteString.encodeUtf8("abc"))); assertEquals(Segment.SIZE * 2 - 4, source.indexOf(ByteString.encodeUtf8("abcd"))); assertEquals(Segment.SIZE * 2 - 4, source.indexOf(ByteString.encodeUtf8("abcde"))); assertEquals(Segment.SIZE * 2 - 4, source.indexOf(ByteString.encodeUtf8("abcdef"))); assertEquals(Segment.SIZE * 2 - 4, source.indexOf(ByteString.encodeUtf8("abcdefg"))); assertEquals(Segment.SIZE * 2 - 3, source.indexOf(ByteString.encodeUtf8("bcdefg"))); assertEquals(Segment.SIZE * 2 - 2, source.indexOf(ByteString.encodeUtf8("cdefg"))); assertEquals(Segment.SIZE * 2 - 1, source.indexOf(ByteString.encodeUtf8("defg"))); assertEquals(Segment.SIZE * 2, source.indexOf(ByteString.encodeUtf8("efg"))); assertEquals(Segment.SIZE * 2 + 1, source.indexOf(ByteString.encodeUtf8("fg"))); assertEquals(Segment.SIZE * 2 + 2, source.indexOf(ByteString.encodeUtf8("g"))); } @Test public void indexOfElement() throws IOException { sink.writeUtf8("a").writeUtf8(repeat('b', Segment.SIZE)).writeUtf8("c"); assertEquals(0, source.indexOfElement(ByteString.encodeUtf8("DEFGaHIJK"))); assertEquals(1, source.indexOfElement(ByteString.encodeUtf8("DEFGHIJKb"))); assertEquals(Segment.SIZE + 1, source.indexOfElement(ByteString.encodeUtf8("cDEFGHIJK"))); assertEquals(1, source.indexOfElement(ByteString.encodeUtf8("DEFbGHIc"))); assertEquals(-1L, source.indexOfElement(ByteString.encodeUtf8("DEFGHIJK"))); assertEquals(-1L, source.indexOfElement(ByteString.encodeUtf8(""))); } @Test public void indexOfElementWithOffset() throws IOException { sink.writeUtf8("a").writeUtf8(repeat('b', Segment.SIZE)).writeUtf8("c"); assertEquals(-1, source.indexOfElement(ByteString.encodeUtf8("DEFGaHIJK"), 1)); assertEquals(15, source.indexOfElement(ByteString.encodeUtf8("DEFGHIJKb"), 15)); } @Test public void request() throws IOException { sink.writeUtf8("a").writeUtf8(repeat('b', Segment.SIZE)).writeUtf8("c"); assertTrue(source.request(Segment.SIZE + 2)); assertFalse(source.request(Segment.SIZE + 3)); } @Test public void require() throws IOException { sink.writeUtf8("a").writeUtf8(repeat('b', Segment.SIZE)).writeUtf8("c"); source.require(Segment.SIZE + 2); try { source.require(Segment.SIZE + 3); fail(); } catch (EOFException expected) { } } @Test public void inputStream() throws Exception { sink.writeUtf8("abc"); InputStream in = source.inputStream(); byte[] bytes = { 'z', 'z', 'z' }; int read = in.read(bytes); if (factory == Factory.ONE_BYTE_AT_A_TIME) { assertEquals(1, read); assertByteArrayEquals("azz", bytes); read = in.read(bytes); assertEquals(1, read); assertByteArrayEquals("bzz", bytes); read = in.read(bytes); assertEquals(1, read); assertByteArrayEquals("czz", bytes); } else { assertEquals(3, read); assertByteArrayEquals("abc", bytes); } assertEquals(-1, in.read()); } @Test public void inputStreamOffsetCount() throws Exception { sink.writeUtf8("abcde"); InputStream in = source.inputStream(); byte[] bytes = { 'z', 'z', 'z', 'z', 'z' }; int read = in.read(bytes, 1, 3); if (factory == Factory.ONE_BYTE_AT_A_TIME) { assertEquals(1, read); assertByteArrayEquals("zazzz", bytes); } else { assertEquals(3, read); assertByteArrayEquals("zabcz", bytes); } } @Test public void inputStreamSkip() throws Exception { sink.writeUtf8("abcde"); InputStream in = source.inputStream(); assertEquals(4, in.skip(4)); assertEquals('e', in.read()); sink.writeUtf8("abcde"); assertEquals(5, in.skip(10)); // Try to skip too much. assertEquals(0, in.skip(1)); // Try to skip when exhausted. } @Test public void inputStreamCharByChar() throws Exception { sink.writeUtf8("abc"); InputStream in = source.inputStream(); assertEquals('a', in.read()); assertEquals('b', in.read()); assertEquals('c', in.read()); assertEquals(-1, in.read()); } @Test public void inputStreamBounds() throws IOException { sink.writeUtf8(repeat('a', 100)); InputStream in = source.inputStream(); try { in.read(new byte[100], 50, 51); fail(); } catch (ArrayIndexOutOfBoundsException expected) { } } @Test public void longHexString() throws IOException { assertLongHexString("8000000000000000", 0x8000000000000000L); assertLongHexString("fffffffffffffffe", 0xFFFFFFFFFFFFFFFEL); assertLongHexString("FFFFFFFFFFFFFFFe", 0xFFFFFFFFFFFFFFFEL); assertLongHexString("ffffffffffffffff", 0xffffffffffffffffL); assertLongHexString("FFFFFFFFFFFFFFFF", 0xFFFFFFFFFFFFFFFFL); assertLongHexString("0000000000000000", 0x0); assertLongHexString("0000000000000001", 0x1); assertLongHexString("7999999999999999", 0x7999999999999999L); assertLongHexString("FF", 0xFF); assertLongHexString("0000000000000001", 0x1); } @Test public void hexStringWithManyLeadingZeros() throws IOException { assertLongHexString("00000000000000001", 0x1); assertLongHexString("0000000000000000ffffffffffffffff", 0xffffffffffffffffL); assertLongHexString("00000000000000007fffffffffffffff", 0x7fffffffffffffffL); assertLongHexString(TestUtil.repeat('0', Segment.SIZE + 1) + "1", 0x1); } private void assertLongHexString(String s, long expected) throws IOException { sink.writeUtf8(s); long actual = source.readHexadecimalUnsignedLong(); assertEquals(s + " --> " + expected, expected, actual); } @Test public void longHexStringAcrossSegment() throws IOException { sink.writeUtf8(repeat('a', Segment.SIZE - 8)).writeUtf8("FFFFFFFFFFFFFFFF"); source.skip(Segment.SIZE - 8); assertEquals(-1, source.readHexadecimalUnsignedLong()); } @Test public void longHexStringTooLongThrows() throws IOException { try { sink.writeUtf8("fffffffffffffffff"); source.readHexadecimalUnsignedLong(); fail(); } catch (NumberFormatException e) { assertEquals("Number too large: fffffffffffffffff", e.getMessage()); } } @Test public void longHexStringTooShortThrows() throws IOException { try { sink.writeUtf8(" "); source.readHexadecimalUnsignedLong(); fail(); } catch (NumberFormatException e) { assertEquals("Expected leading [0-9a-fA-F] character but was 0x20", e.getMessage()); } } @Test public void longHexEmptySourceThrows() throws IOException { try { sink.writeUtf8(""); source.readHexadecimalUnsignedLong(); fail(); } catch (IllegalStateException | EOFException expected) { } } @Test public void longDecimalString() throws IOException { assertLongDecimalString("-9223372036854775808", -9223372036854775808L); assertLongDecimalString("-1", -1L); assertLongDecimalString("0", 0L); assertLongDecimalString("1", 1L); assertLongDecimalString("9223372036854775807", 9223372036854775807L); assertLongDecimalString("00000001", 1L); assertLongDecimalString("-000001", -1L); } private void assertLongDecimalString(String s, long expected) throws IOException { sink.writeUtf8(s); sink.writeUtf8("zzz"); long actual = source.readDecimalLong(); assertEquals(s + " --> " + expected, expected, actual); assertEquals("zzz", source.readUtf8()); } @Test public void longDecimalStringAcrossSegment() throws IOException { sink.writeUtf8(repeat('a', Segment.SIZE - 8)).writeUtf8("1234567890123456"); sink.writeUtf8("zzz"); source.skip(Segment.SIZE - 8); assertEquals(1234567890123456L, source.readDecimalLong()); assertEquals("zzz", source.readUtf8()); } @Test public void longDecimalStringTooLongThrows() throws IOException { try { sink.writeUtf8("12345678901234567890"); // Too many digits. source.readDecimalLong(); fail(); } catch (NumberFormatException e) { assertEquals("Number too large: 12345678901234567890", e.getMessage()); } } @Test public void longDecimalStringTooHighThrows() throws IOException { try { sink.writeUtf8("9223372036854775808"); // Right size but cannot fit. source.readDecimalLong(); fail(); } catch (NumberFormatException e) { assertEquals("Number too large: 9223372036854775808", e.getMessage()); } } @Test public void longDecimalStringTooLowThrows() throws IOException { try { sink.writeUtf8("-9223372036854775809"); // Right size but cannot fit. source.readDecimalLong(); fail(); } catch (NumberFormatException e) { assertEquals("Number too large: -9223372036854775809", e.getMessage()); } } @Test public void longDecimalStringTooShortThrows() throws IOException { try { sink.writeUtf8(" "); source.readDecimalLong(); fail(); } catch (NumberFormatException e) { assertEquals("Expected leading [0-9] or '-' character but was 0x20", e.getMessage()); } } @Test public void longDecimalEmptyThrows() throws IOException { try { sink.writeUtf8(""); source.readDecimalLong(); fail(); } catch (IllegalStateException | EOFException expected) { } } @Test public void codePoints() throws IOException { sink.write(ByteString.decodeHex("7f")); assertEquals(0x7f, source.readUtf8CodePoint()); sink.write(ByteString.decodeHex("dfbf")); assertEquals(0x07ff, source.readUtf8CodePoint()); sink.write(ByteString.decodeHex("efbfbf")); assertEquals(0xffff, source.readUtf8CodePoint()); sink.write(ByteString.decodeHex("f48fbfbf")); assertEquals(0x10ffff, source.readUtf8CodePoint()); } @Test public void decimalStringWithManyLeadingZeros() throws IOException { assertLongDecimalString("00000000000000001", 1); assertLongDecimalString("00000000000000009223372036854775807", 9223372036854775807L); assertLongDecimalString("-00000000000000009223372036854775808", -9223372036854775808L); assertLongDecimalString(TestUtil.repeat('0', Segment.SIZE + 1) + "1", 1); } @Test public void select() throws IOException { Options options = Options.of( ByteString.encodeUtf8("ROCK"), ByteString.encodeUtf8("SCISSORS"), ByteString.encodeUtf8("PAPER")); sink.writeUtf8("PAPER,SCISSORS,ROCK"); assertEquals(2, source.select(options)); assertEquals(',', source.readByte()); assertEquals(1, source.select(options)); assertEquals(',', source.readByte()); assertEquals(0, source.select(options)); assertTrue(source.exhausted()); } @Test public void selectSpanningMultipleSegments() throws IOException { ByteString commonPrefix = TestUtil.randomBytes(Segment.SIZE + 10); ByteString a = new Buffer().write(commonPrefix).writeUtf8("a").readByteString(); ByteString bc = new Buffer().write(commonPrefix).writeUtf8("bc").readByteString(); ByteString bd = new Buffer().write(commonPrefix).writeUtf8("bd").readByteString(); Options options = Options.of(a, bc, bd); sink.write(bd); sink.write(a); sink.write(bc); assertEquals(2, source.select(options)); assertEquals(0, source.select(options)); assertEquals(1, source.select(options)); assertTrue(source.exhausted()); } @Test public void selectNotFound() throws IOException { Options options = Options.of( ByteString.encodeUtf8("ROCK"), ByteString.encodeUtf8("SCISSORS"), ByteString.encodeUtf8("PAPER")); sink.writeUtf8("SPOCK"); assertEquals(-1, source.select(options)); assertEquals("SPOCK", source.readUtf8()); } @Test public void selectValuesHaveCommonPrefix() throws IOException { Options options = Options.of( ByteString.encodeUtf8("abcd"), ByteString.encodeUtf8("abce"), ByteString.encodeUtf8("abcc")); sink.writeUtf8("abcc").writeUtf8("abcd").writeUtf8("abce"); assertEquals(2, source.select(options)); assertEquals(0, source.select(options)); assertEquals(1, source.select(options)); } @Test public void selectLongerThanSource() throws IOException { Options options = Options.of( ByteString.encodeUtf8("abcd"), ByteString.encodeUtf8("abce"), ByteString.encodeUtf8("abcc")); sink.writeUtf8("abc"); assertEquals(-1, source.select(options)); assertEquals("abc", source.readUtf8()); } @Test public void selectReturnsFirstByteStringThatMatches() throws IOException { Options options = Options.of( ByteString.encodeUtf8("abcd"), ByteString.encodeUtf8("abc"), ByteString.encodeUtf8("abcde")); sink.writeUtf8("abcdef"); assertEquals(0, source.select(options)); assertEquals("ef", source.readUtf8()); } @Test public void selectFromEmptySource() throws IOException { Options options = Options.of( ByteString.encodeUtf8("abc"), ByteString.encodeUtf8("def")); assertEquals(-1, source.select(options)); } @Test public void selectNoByteStringsFromEmptySource() throws IOException { Options options = Options.of(); assertEquals(-1, source.select(options)); } @Test public void selectEmptyByteString() throws IOException { try { Options.of(ByteString.of()); fail(); } catch (IllegalArgumentException expected) { } } @Test public void peek() throws IOException { sink.writeUtf8("abcdefghi"); sink.emit(); assertEquals("abc", source.readUtf8(3)); BufferedSource peek = source.peek(); assertEquals("def", peek.readUtf8(3)); assertEquals("ghi", peek.readUtf8(3)); assertFalse(peek.request(1)); assertEquals("def", source.readUtf8(3)); } @Test public void peekMultiple() throws IOException { sink.writeUtf8("abcdefghi"); sink.emit(); assertEquals("abc", source.readUtf8(3)); BufferedSource peek1 = source.peek(); BufferedSource peek2 = source.peek(); assertEquals("def", peek1.readUtf8(3)); assertEquals("def", peek2.readUtf8(3)); assertEquals("ghi", peek2.readUtf8(3)); assertFalse(peek2.request(1)); assertEquals("ghi", peek1.readUtf8(3)); assertFalse(peek1.request(1)); assertEquals("def", source.readUtf8(3)); } @Test public void peekLarge() throws IOException { sink.writeUtf8("abcdef"); sink.writeUtf8(repeat('g', 2 * Segment.SIZE)); sink.writeUtf8("hij"); sink.emit(); assertEquals("abc", source.readUtf8(3)); BufferedSource peek = source.peek(); assertEquals("def", peek.readUtf8(3)); peek.skip(2 * Segment.SIZE); assertEquals("hij", peek.readUtf8(3)); assertFalse(peek.request(1)); assertEquals("def", source.readUtf8(3)); source.skip(2 * Segment.SIZE); assertEquals("hij", source.readUtf8(3)); } @Test public void peekInvalid() throws IOException { sink.writeUtf8("abcdefghi"); sink.emit(); assertEquals("abc", source.readUtf8(3)); BufferedSource peek = source.peek(); assertEquals("def", peek.readUtf8(3)); assertEquals("ghi", peek.readUtf8(3)); assertFalse(peek.request(1)); assertEquals("def", source.readUtf8(3)); try { peek.readUtf8(); fail(); } catch (IllegalStateException e) { assertEquals("Peek source is invalid because upstream source was used", e.getMessage()); } } @Test public void peekSegmentThenInvalid() throws IOException { sink.writeUtf8("abc"); sink.writeUtf8(repeat('d', 2 * Segment.SIZE)); sink.emit(); assertEquals("abc", source.readUtf8(3)); // Peek a little data and skip the rest of the upstream source BufferedSource peek = source.peek(); assertEquals("ddd", peek.readUtf8(3)); source.readAll(Okio.blackhole()); // Skip the rest of the buffered data peek.skip(Segment.SIZE - 3); try { peek.readByte(); fail(); } catch (IllegalStateException e) { assertEquals("Peek source is invalid because upstream source was used", e.getMessage()); } } @Test public void rangeEquals() throws IOException { sink.writeUtf8("A man, a plan, a canal. Panama."); assertTrue(source.rangeEquals(7 , ByteString.encodeUtf8("a plan"))); assertTrue(source.rangeEquals(0 , ByteString.encodeUtf8("A man"))); assertTrue(source.rangeEquals(24, ByteString.encodeUtf8("Panama"))); assertFalse(source.rangeEquals(24, ByteString.encodeUtf8("Panama. Panama. Panama."))); } @Test public void rangeEqualsWithOffsetAndCount() throws IOException { sink.writeUtf8("A man, a plan, a canal. Panama."); assertTrue(source.rangeEquals(7 , ByteString.encodeUtf8("aaa plannn"), 2, 6)); assertTrue(source.rangeEquals(0 , ByteString.encodeUtf8("AAA mannn"), 2, 5)); assertTrue(source.rangeEquals(24, ByteString.encodeUtf8("PPPanamaaa"), 2, 6)); } @Test public void rangeEqualsOnlyReadsUntilMismatch() throws IOException { assumeTrue(factory == Factory.ONE_BYTE_AT_A_TIME); // Other sources read in chunks anyway. sink.writeUtf8("A man, a plan, a canal. Panama."); assertFalse(source.rangeEquals(0, ByteString.encodeUtf8("A man."))); assertEquals("A man,", source.buffer().readUtf8()); } @Test public void rangeEqualsArgumentValidation() throws IOException { // Negative source offset. assertFalse(source.rangeEquals(-1, ByteString.encodeUtf8("A"))); // Negative bytes offset. assertFalse(source.rangeEquals(0, ByteString.encodeUtf8("A"), -1, 1)); // Bytes offset longer than bytes length. assertFalse(source.rangeEquals(0, ByteString.encodeUtf8("A"), 2, 1)); // Negative byte count. assertFalse(source.rangeEquals(0, ByteString.encodeUtf8("A"), 0, -1)); // Byte count longer than bytes length. assertFalse(source.rangeEquals(0, ByteString.encodeUtf8("A"), 0, 2)); // Bytes offset plus byte count longer than bytes length. assertFalse(source.rangeEquals(0, ByteString.encodeUtf8("A"), 1, 1)); } @Test public void readNioBuffer() throws Exception { String expected = factory == Factory.ONE_BYTE_AT_A_TIME ? "a" : "abcdefg"; sink.writeUtf8("abcdefg"); ByteBuffer nioByteBuffer = ByteBuffer.allocate(1024); int byteCount = source.read(nioByteBuffer); assertEquals(expected.length(), byteCount); assertEquals(expected.length(), nioByteBuffer.position()); assertEquals(nioByteBuffer.capacity(), nioByteBuffer.limit()); nioByteBuffer.flip(); byte[] data = new byte[expected.length()]; nioByteBuffer.get(data); assertEquals(expected, new String(data)); } @Test public void readLargeNioBufferOnlyReadsOneSegment() throws Exception { String expected = factory == Factory.ONE_BYTE_AT_A_TIME ? "a" : TestUtil.repeat('a', Segment.SIZE); sink.writeUtf8(TestUtil.repeat('a', Segment.SIZE * 4)); ByteBuffer nioByteBuffer = ByteBuffer.allocate(Segment.SIZE * 3); int byteCount = source.read(nioByteBuffer); assertEquals(expected.length(), byteCount); assertEquals(expected.length(), nioByteBuffer.position()); assertEquals(nioByteBuffer.capacity(), nioByteBuffer.limit()); nioByteBuffer.flip(); byte[] data = new byte[expected.length()]; nioByteBuffer.get(data); assertEquals(expected, new String(data)); } } okio-okio-parent-1.16.0/okio/src/test/java/okio/ByteStringTest.java000066400000000000000000000672031335667376100252050ustar00rootroot00000000000000/* * Copyright 2014 Square Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Random; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; import static okio.TestUtil.assertByteArraysEquals; import static okio.TestUtil.assertEquivalent; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @RunWith(Parameterized.class) public final class ByteStringTest { interface Factory { Factory BYTE_STRING = new Factory() { @Override public ByteString decodeHex(String hex) { return ByteString.decodeHex(hex); } @Override public ByteString encodeUtf8(String s) { return ByteString.encodeUtf8(s); } }; Factory SEGMENTED_BYTE_STRING = new Factory() { @Override public ByteString decodeHex(String hex) { Buffer buffer = new Buffer(); buffer.write(ByteString.decodeHex(hex)); return buffer.snapshot(); } @Override public ByteString encodeUtf8(String s) { Buffer buffer = new Buffer(); buffer.writeUtf8(s); return buffer.snapshot(); } }; Factory ONE_BYTE_PER_SEGMENT = new Factory() { @Override public ByteString decodeHex(String hex) { return makeSegments(ByteString.decodeHex(hex)); } @Override public ByteString encodeUtf8(String s) { return makeSegments(ByteString.encodeUtf8(s)); } private ByteString makeSegments(ByteString source) { Buffer buffer = new Buffer(); for (int i = 0; i < source.size(); i++) { Segment segment = buffer.writableSegment(Segment.SIZE); segment.data[segment.pos] = source.getByte(i); segment.limit++; buffer.size++; } return buffer.snapshot(); } }; ByteString decodeHex(String hex); ByteString encodeUtf8(String s); } @Parameters(name = "{1}") public static List parameters() { return Arrays.asList( new Object[] { Factory.BYTE_STRING, "ByteString" }, new Object[] { Factory.SEGMENTED_BYTE_STRING, "SegmentedByteString" }, new Object[] { Factory.ONE_BYTE_PER_SEGMENT, "SegmentedByteString (one-at-a-time)" }); } @Parameter(0) public Factory factory; @Parameter(1) public String name; @Test public void ofCopyRange() { byte[] bytes = "Hello, World!".getBytes(Util.UTF_8); ByteString byteString = ByteString.of(bytes, 2, 9); // Verify that the bytes were copied out. bytes[4] = (byte) 'a'; assertEquals("llo, Worl", byteString.utf8()); } @Test public void ofByteBuffer() { byte[] bytes = "Hello, World!".getBytes(Util.UTF_8); ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); byteBuffer.position(2).limit(11); ByteString byteString = ByteString.of(byteBuffer); // Verify that the bytes were copied out. byteBuffer.put(4, (byte) 'a'); assertEquals("llo, Worl", byteString.utf8()); } @Test public void getByte() throws Exception { ByteString byteString = factory.decodeHex("ab12"); assertEquals(-85, byteString.getByte(0)); assertEquals(18, byteString.getByte(1)); } @Test public void getByteOutOfBounds() throws Exception { ByteString byteString = factory.decodeHex("ab12"); try { byteString.getByte(2); fail(); } catch (IndexOutOfBoundsException expected) { } } @Test public void startsWithByteString() throws Exception { ByteString byteString = factory.decodeHex("112233"); assertTrue(byteString.startsWith(ByteString.decodeHex(""))); assertTrue(byteString.startsWith(ByteString.decodeHex("11"))); assertTrue(byteString.startsWith(ByteString.decodeHex("1122"))); assertTrue(byteString.startsWith(ByteString.decodeHex("112233"))); assertFalse(byteString.startsWith(ByteString.decodeHex("2233"))); assertFalse(byteString.startsWith(ByteString.decodeHex("11223344"))); assertFalse(byteString.startsWith(ByteString.decodeHex("112244"))); } @Test public void endsWithByteString() throws Exception { ByteString byteString = factory.decodeHex("112233"); assertTrue(byteString.endsWith(ByteString.decodeHex(""))); assertTrue(byteString.endsWith(ByteString.decodeHex("33"))); assertTrue(byteString.endsWith(ByteString.decodeHex("2233"))); assertTrue(byteString.endsWith(ByteString.decodeHex("112233"))); assertFalse(byteString.endsWith(ByteString.decodeHex("1122"))); assertFalse(byteString.endsWith(ByteString.decodeHex("00112233"))); assertFalse(byteString.endsWith(ByteString.decodeHex("002233"))); } @Test public void startsWithByteArray() throws Exception { ByteString byteString = factory.decodeHex("112233"); assertTrue(byteString.startsWith(ByteString.decodeHex("").toByteArray())); assertTrue(byteString.startsWith(ByteString.decodeHex("11").toByteArray())); assertTrue(byteString.startsWith(ByteString.decodeHex("1122").toByteArray())); assertTrue(byteString.startsWith(ByteString.decodeHex("112233").toByteArray())); assertFalse(byteString.startsWith(ByteString.decodeHex("2233").toByteArray())); assertFalse(byteString.startsWith(ByteString.decodeHex("11223344").toByteArray())); assertFalse(byteString.startsWith(ByteString.decodeHex("112244").toByteArray())); } @Test public void endsWithByteArray() throws Exception { ByteString byteString = factory.decodeHex("112233"); assertTrue(byteString.endsWith(ByteString.decodeHex("").toByteArray())); assertTrue(byteString.endsWith(ByteString.decodeHex("33").toByteArray())); assertTrue(byteString.endsWith(ByteString.decodeHex("2233").toByteArray())); assertTrue(byteString.endsWith(ByteString.decodeHex("112233").toByteArray())); assertFalse(byteString.endsWith(ByteString.decodeHex("1122").toByteArray())); assertFalse(byteString.endsWith(ByteString.decodeHex("00112233").toByteArray())); assertFalse(byteString.endsWith(ByteString.decodeHex("002233").toByteArray())); } @Test public void indexOfByteString() throws Exception { ByteString byteString = factory.decodeHex("112233"); assertEquals(0, byteString.indexOf(ByteString.decodeHex("112233"))); assertEquals(0, byteString.indexOf(ByteString.decodeHex("1122"))); assertEquals(0, byteString.indexOf(ByteString.decodeHex("11"))); assertEquals(0, byteString.indexOf(ByteString.decodeHex("11"), 0)); assertEquals(0, byteString.indexOf(ByteString.decodeHex(""))); assertEquals(0, byteString.indexOf(ByteString.decodeHex(""), 0)); assertEquals(1, byteString.indexOf(ByteString.decodeHex("2233"))); assertEquals(1, byteString.indexOf(ByteString.decodeHex("22"))); assertEquals(1, byteString.indexOf(ByteString.decodeHex("22"), 1)); assertEquals(1, byteString.indexOf(ByteString.decodeHex(""), 1)); assertEquals(2, byteString.indexOf(ByteString.decodeHex("33"))); assertEquals(2, byteString.indexOf(ByteString.decodeHex("33"), 2)); assertEquals(2, byteString.indexOf(ByteString.decodeHex(""), 2)); assertEquals(3, byteString.indexOf(ByteString.decodeHex(""), 3)); assertEquals(-1, byteString.indexOf(ByteString.decodeHex("112233"), 1)); assertEquals(-1, byteString.indexOf(ByteString.decodeHex("44"))); assertEquals(-1, byteString.indexOf(ByteString.decodeHex("11223344"))); assertEquals(-1, byteString.indexOf(ByteString.decodeHex("112244"))); assertEquals(-1, byteString.indexOf(ByteString.decodeHex("112233"), 1)); assertEquals(-1, byteString.indexOf(ByteString.decodeHex("2233"), 2)); assertEquals(-1, byteString.indexOf(ByteString.decodeHex("33"), 3)); assertEquals(-1, byteString.indexOf(ByteString.decodeHex(""), 4)); } @Test public void indexOfWithOffset() throws Exception { ByteString byteString = factory.decodeHex("112233112233"); assertEquals(0, byteString.indexOf(ByteString.decodeHex("112233"), -1)); assertEquals(0, byteString.indexOf(ByteString.decodeHex("112233"), 0)); assertEquals(0, byteString.indexOf(ByteString.decodeHex("112233"))); assertEquals(3, byteString.indexOf(ByteString.decodeHex("112233"), 1)); assertEquals(3, byteString.indexOf(ByteString.decodeHex("112233"), 2)); assertEquals(3, byteString.indexOf(ByteString.decodeHex("112233"), 3)); assertEquals(-1, byteString.indexOf(ByteString.decodeHex("112233"), 4)); } @Test public void indexOfByteArray() throws Exception { ByteString byteString = factory.decodeHex("112233"); assertEquals(0, byteString.indexOf(ByteString.decodeHex("112233").toByteArray())); assertEquals(1, byteString.indexOf(ByteString.decodeHex("2233").toByteArray())); assertEquals(2, byteString.indexOf(ByteString.decodeHex("33").toByteArray())); assertEquals(-1, byteString.indexOf(ByteString.decodeHex("112244").toByteArray())); } @Test public void lastIndexOfByteString() throws Exception { ByteString byteString = factory.decodeHex("112233"); assertEquals(0, byteString.lastIndexOf(ByteString.decodeHex("112233"))); assertEquals(0, byteString.lastIndexOf(ByteString.decodeHex("1122"))); assertEquals(0, byteString.lastIndexOf(ByteString.decodeHex("11"))); assertEquals(0, byteString.lastIndexOf(ByteString.decodeHex("11"), 3)); assertEquals(0, byteString.lastIndexOf(ByteString.decodeHex("11"), 0)); assertEquals(0, byteString.lastIndexOf(ByteString.decodeHex(""), 0)); assertEquals(1, byteString.lastIndexOf(ByteString.decodeHex("2233"))); assertEquals(1, byteString.lastIndexOf(ByteString.decodeHex("22"))); assertEquals(1, byteString.lastIndexOf(ByteString.decodeHex("22"), 3)); assertEquals(1, byteString.lastIndexOf(ByteString.decodeHex("22"), 1)); assertEquals(1, byteString.lastIndexOf(ByteString.decodeHex(""), 1)); assertEquals(2, byteString.lastIndexOf(ByteString.decodeHex("33"))); assertEquals(2, byteString.lastIndexOf(ByteString.decodeHex("33"), 3)); assertEquals(2, byteString.lastIndexOf(ByteString.decodeHex("33"), 2)); assertEquals(2, byteString.lastIndexOf(ByteString.decodeHex(""), 2)); assertEquals(3, byteString.lastIndexOf(ByteString.decodeHex(""), 3)); assertEquals(3, byteString.lastIndexOf(ByteString.decodeHex(""))); assertEquals(-1, byteString.lastIndexOf(ByteString.decodeHex("112233"), -1)); assertEquals(-1, byteString.lastIndexOf(ByteString.decodeHex("112233"), -2)); assertEquals(-1, byteString.lastIndexOf(ByteString.decodeHex("44"))); assertEquals(-1, byteString.lastIndexOf(ByteString.decodeHex("11223344"))); assertEquals(-1, byteString.lastIndexOf(ByteString.decodeHex("112244"))); assertEquals(-1, byteString.lastIndexOf(ByteString.decodeHex("2233"), 0)); assertEquals(-1, byteString.lastIndexOf(ByteString.decodeHex("33"), 1)); assertEquals(-1, byteString.lastIndexOf(ByteString.decodeHex(""), -1)); } @Test public void lastIndexOfByteArray() throws Exception { ByteString byteString = factory.decodeHex("112233"); assertEquals(0, byteString.lastIndexOf(ByteString.decodeHex("112233").toByteArray())); assertEquals(1, byteString.lastIndexOf(ByteString.decodeHex("2233").toByteArray())); assertEquals(2, byteString.lastIndexOf(ByteString.decodeHex("33").toByteArray())); assertEquals(3, byteString.lastIndexOf(ByteString.decodeHex("").toByteArray())); } @SuppressWarnings("SelfEquals") @Test public void equals() throws Exception { ByteString byteString = factory.decodeHex("000102"); assertTrue(byteString.equals(byteString)); assertTrue(byteString.equals(ByteString.decodeHex("000102"))); assertTrue(factory.decodeHex("").equals(ByteString.EMPTY)); assertTrue(factory.decodeHex("").equals(ByteString.of())); assertTrue(ByteString.EMPTY.equals(factory.decodeHex(""))); assertTrue(ByteString.of().equals(factory.decodeHex(""))); assertFalse(byteString.equals(new Object())); assertFalse(byteString.equals(ByteString.decodeHex("000201"))); } private final String bronzeHorseman = "На берегу пустынных волн"; @Test public void utf8() throws Exception { ByteString byteString = factory.encodeUtf8(bronzeHorseman); assertByteArraysEquals(byteString.toByteArray(), bronzeHorseman.getBytes(Util.UTF_8)); assertTrue(byteString.equals(ByteString.of(bronzeHorseman.getBytes(Util.UTF_8)))); assertEquals(byteString.utf8(), bronzeHorseman); } @Test public void encodeNullCharset() throws Exception { try { ByteString.encodeString("hello", null); fail(); } catch (IllegalArgumentException expected) { } } @Test public void encodeNullString() throws Exception { try { ByteString.encodeString(null, Charset.forName("UTF-8")); fail(); } catch (IllegalArgumentException expected) { } } @Test public void decodeNullCharset() throws Exception { try { ByteString.of().string(null); fail(); } catch (IllegalArgumentException expected) { } } @Test public void encodeDecodeStringUtf8() throws Exception { Charset utf8 = Charset.forName("UTF-8"); ByteString byteString = ByteString.encodeString(bronzeHorseman, utf8); assertByteArraysEquals(byteString.toByteArray(), bronzeHorseman.getBytes(utf8)); assertEquals(byteString, ByteString.decodeHex("d09dd0b020d0b1d0b5d180d0b5d0b3d18320d0bfd183d181" + "d182d18bd0bdd0bdd18bd18520d0b2d0bed0bbd0bd")); assertEquals(bronzeHorseman, byteString.string(utf8)); } @Test public void encodeDecodeStringUtf16be() throws Exception { Charset utf16be = Charset.forName("UTF-16BE"); ByteString byteString = ByteString.encodeString(bronzeHorseman, utf16be); assertByteArraysEquals(byteString.toByteArray(), bronzeHorseman.getBytes(utf16be)); assertEquals(byteString, ByteString.decodeHex("041d043000200431043504400435043304430020043f0443" + "04410442044b043d043d044b044500200432043e043b043d")); assertEquals(bronzeHorseman, byteString.string(utf16be)); } @Test public void encodeDecodeStringUtf32be() throws Exception { Charset utf32be = Charset.forName("UTF-32BE"); ByteString byteString = ByteString.encodeString(bronzeHorseman, utf32be); assertByteArraysEquals(byteString.toByteArray(), bronzeHorseman.getBytes(utf32be)); assertEquals(byteString, ByteString.decodeHex("0000041d0000043000000020000004310000043500000440" + "000004350000043300000443000000200000043f0000044300000441000004420000044b0000043d0000043d" + "0000044b0000044500000020000004320000043e0000043b0000043d")); assertEquals(bronzeHorseman, byteString.string(utf32be)); } @Test public void encodeDecodeStringAsciiIsLossy() throws Exception { Charset ascii = Charset.forName("US-ASCII"); ByteString byteString = ByteString.encodeString(bronzeHorseman, ascii); assertByteArraysEquals(byteString.toByteArray(), bronzeHorseman.getBytes(ascii)); assertEquals(byteString, ByteString.decodeHex("3f3f203f3f3f3f3f3f203f3f3f3f3f3f3f3f3f203f3f3f3f")); assertEquals("?? ?????? ????????? ????", byteString.string(ascii)); } @Test public void decodeMalformedStringReturnsReplacementCharacter() throws Exception { Charset utf16be = Charset.forName("UTF-16BE"); String string = ByteString.decodeHex("04").string(utf16be); assertEquals("\ufffd", string); } @Test public void testHashCode() throws Exception { ByteString byteString = factory.decodeHex("0102"); assertEquals(byteString.hashCode(), byteString.hashCode()); assertEquals(byteString.hashCode(), ByteString.decodeHex("0102").hashCode()); } @Test public void read() throws Exception { InputStream in = new ByteArrayInputStream("abc".getBytes(Util.UTF_8)); assertEquals(ByteString.decodeHex("6162"), ByteString.read(in, 2)); assertEquals(ByteString.decodeHex("63"), ByteString.read(in, 1)); assertEquals(ByteString.of(), ByteString.read(in, 0)); } @Test public void readAndToLowercase() throws Exception { InputStream in = new ByteArrayInputStream("ABC".getBytes(Util.UTF_8)); assertEquals(ByteString.encodeUtf8("ab"), ByteString.read(in, 2).toAsciiLowercase()); assertEquals(ByteString.encodeUtf8("c"), ByteString.read(in, 1).toAsciiLowercase()); assertEquals(ByteString.EMPTY, ByteString.read(in, 0).toAsciiLowercase()); } @Test public void toAsciiLowerCaseNoUppercase() throws Exception { ByteString s = factory.encodeUtf8("a1_+"); assertEquals(s, s.toAsciiLowercase()); if (factory == Factory.BYTE_STRING) { assertSame(s, s.toAsciiLowercase()); } } @Test public void toAsciiAllUppercase() throws Exception { assertEquals(ByteString.encodeUtf8("ab"), factory.encodeUtf8("AB").toAsciiLowercase()); } @Test public void toAsciiStartsLowercaseEndsUppercase() throws Exception { assertEquals(ByteString.encodeUtf8("abcd"), factory.encodeUtf8("abCD").toAsciiLowercase()); } @Test public void readAndToUppercase() throws Exception { InputStream in = new ByteArrayInputStream("abc".getBytes(Util.UTF_8)); assertEquals(ByteString.encodeUtf8("AB"), ByteString.read(in, 2).toAsciiUppercase()); assertEquals(ByteString.encodeUtf8("C"), ByteString.read(in, 1).toAsciiUppercase()); assertEquals(ByteString.EMPTY, ByteString.read(in, 0).toAsciiUppercase()); } @Test public void toAsciiStartsUppercaseEndsLowercase() throws Exception { assertEquals(ByteString.encodeUtf8("ABCD"), factory.encodeUtf8("ABcd").toAsciiUppercase()); } @Test public void substring() throws Exception { ByteString byteString = factory.encodeUtf8("Hello, World!"); assertEquals(byteString.substring(0), byteString); assertEquals(byteString.substring(0, 5), ByteString.encodeUtf8("Hello")); assertEquals(byteString.substring(7), ByteString.encodeUtf8("World!")); assertEquals(byteString.substring(6, 6), ByteString.encodeUtf8("")); } @Test public void substringWithInvalidBounds() throws Exception { ByteString byteString = factory.encodeUtf8("Hello, World!"); try { byteString.substring(-1); fail(); } catch (IllegalArgumentException expected) { } try { byteString.substring(0, 14); fail(); } catch (IllegalArgumentException expected) { } try { byteString.substring(8, 7); fail(); } catch (IllegalArgumentException expected) { } } @Test public void write() throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); factory.decodeHex("616263").write(out); assertByteArraysEquals(new byte[] { 0x61, 0x62, 0x63 }, out.toByteArray()); } @Test public void encodeBase64() { assertEquals("", factory.encodeUtf8("").base64()); assertEquals("AA==", factory.encodeUtf8("\u0000").base64()); assertEquals("AAA=", factory.encodeUtf8("\u0000\u0000").base64()); assertEquals("AAAA", factory.encodeUtf8("\u0000\u0000\u0000").base64()); assertEquals("SG93IG1hbnkgbGluZXMgb2YgY29kZSBhcmUgdGhlcmU/ICdib3V0IDIgbWlsbGlvbi4=", factory.encodeUtf8("How many lines of code are there? 'bout 2 million.").base64()); } @Test public void encodeBase64Url() { assertEquals("", factory.encodeUtf8("").base64Url()); assertEquals("AA==", factory.encodeUtf8("\u0000").base64Url()); assertEquals("AAA=", factory.encodeUtf8("\u0000\u0000").base64Url()); assertEquals("AAAA", factory.encodeUtf8("\u0000\u0000\u0000").base64Url()); assertEquals("SG93IG1hbnkgbGluZXMgb2YgY29kZSBhcmUgdGhlcmU_ICdib3V0IDIgbWlsbGlvbi4=", factory.encodeUtf8("How many lines of code are there? 'bout 2 million.").base64Url()); } @Test public void ignoreUnnecessaryPadding() { assertEquals("", ByteString.decodeBase64("====").utf8()); assertEquals("\u0000\u0000\u0000", ByteString.decodeBase64("AAAA====").utf8()); } @Test public void decodeBase64() { assertEquals("", ByteString.decodeBase64("").utf8()); assertEquals(null, ByteString.decodeBase64("/===")); // Can't do anything with 6 bits! assertEquals(ByteString.decodeHex("ff"), ByteString.decodeBase64("//==")); assertEquals(ByteString.decodeHex("ff"), ByteString.decodeBase64("__==")); assertEquals(ByteString.decodeHex("ffff"), ByteString.decodeBase64("///=")); assertEquals(ByteString.decodeHex("ffff"), ByteString.decodeBase64("___=")); assertEquals(ByteString.decodeHex("ffffff"), ByteString.decodeBase64("////")); assertEquals(ByteString.decodeHex("ffffff"), ByteString.decodeBase64("____")); assertEquals(ByteString.decodeHex("ffffffffffff"), ByteString.decodeBase64("////////")); assertEquals(ByteString.decodeHex("ffffffffffff"), ByteString.decodeBase64("________")); assertEquals("What's to be scared about? It's just a little hiccup in the power...", ByteString.decodeBase64("V2hhdCdzIHRvIGJlIHNjYXJlZCBhYm91dD8gSXQncyBqdXN0IGEgbGl0dGxlIGhpY2" + "N1cCBpbiB0aGUgcG93ZXIuLi4=").utf8()); // Uses two encoding styles. Malformed, but supported as a side-effect. assertEquals(ByteString.decodeHex("ffffff"), ByteString.decodeBase64("__//")); } @Test public void decodeBase64WithWhitespace() { assertEquals("\u0000\u0000\u0000", ByteString.decodeBase64(" AA AA ").utf8()); assertEquals("\u0000\u0000\u0000", ByteString.decodeBase64(" AA A\r\nA ").utf8()); assertEquals("\u0000\u0000\u0000", ByteString.decodeBase64("AA AA").utf8()); assertEquals("\u0000\u0000\u0000", ByteString.decodeBase64(" AA AA ").utf8()); assertEquals("\u0000\u0000\u0000", ByteString.decodeBase64(" AA A\r\nA ").utf8()); assertEquals("\u0000\u0000\u0000", ByteString.decodeBase64("A AAA").utf8()); assertEquals("", ByteString.decodeBase64(" ").utf8()); } @Test public void encodeHex() throws Exception { assertEquals("000102", ByteString.of((byte) 0x0, (byte) 0x1, (byte) 0x2).hex()); } @Test public void decodeHex() throws Exception { assertEquals(ByteString.of((byte) 0x0, (byte) 0x1, (byte) 0x2), ByteString.decodeHex("000102")); } @Test public void decodeHexOddNumberOfChars() throws Exception { try { ByteString.decodeHex("aaa"); fail(); } catch (IllegalArgumentException expected) { } } @Test public void decodeHexInvalidChar() throws Exception { try { ByteString.decodeHex("a\u0000"); fail(); } catch (IllegalArgumentException expected) { } } @Test public void toStringOnEmpty() { assertEquals("[size=0]", factory.decodeHex("").toString()); } @Test public void toStringOnShortText() { assertEquals("[text=Tyrannosaur]", factory.encodeUtf8("Tyrannosaur").toString()); assertEquals("[text=təˈranəˌsôr]", factory.decodeHex("74c999cb8872616ec999cb8c73c3b472").toString()); } @Test public void toStringOnLongTextIsTruncated() { String raw = "Um, I'll tell you the problem with the scientific power that you're using here, " + "it didn't require any discipline to attain it. You read what others had done and you " + "took the next step. You didn't earn the knowledge for yourselves, so you don't take any " + "responsibility for it. You stood on the shoulders of geniuses to accomplish something " + "as fast as you could, and before you even knew what you had, you patented it, and " + "packaged it, and slapped it on a plastic lunchbox, and now you're selling it, you wanna " + "sell it."; assertEquals("[size=517 text=Um, I'll tell you the problem with the scientific power that " + "you…]", factory.encodeUtf8(raw).toString()); } @Test public void toStringOnTextWithNewlines() { // Instead of emitting a literal newline in the toString(), these are escaped as "\n". assertEquals("[text=a\\r\\nb\\nc\\rd\\\\e]", factory.encodeUtf8("a\r\nb\nc\rd\\e").toString()); } @Test public void toStringOnData() { ByteString byteString = factory.decodeHex("" + "60b420bb3851d9d47acb933dbe70399bf6c92da33af01d4fb770e98c0325f41d3ebaf8986da712c82bcd4d55" + "4bf0b54023c29b624de9ef9c2f931efc580f9afb"); assertEquals("[hex=" + "60b420bb3851d9d47acb933dbe70399bf6c92da33af01d4fb770e98c0325f41d3ebaf8986da712c82bcd4d55" + "4bf0b54023c29b624de9ef9c2f931efc580f9afb]", byteString.toString()); } @Test public void toStringOnLongDataIsTruncated() { ByteString byteString = factory.decodeHex("" + "60b420bb3851d9d47acb933dbe70399bf6c92da33af01d4fb770e98c0325f41d3ebaf8986da712c82bcd4d55" + "4bf0b54023c29b624de9ef9c2f931efc580f9afba1"); assertEquals("[size=65 hex=" + "60b420bb3851d9d47acb933dbe70399bf6c92da33af01d4fb770e98c0325f41d3ebaf8986da712c82bcd4d55" + "4bf0b54023c29b624de9ef9c2f931efc580f9afb…]", byteString.toString()); } @Test public void javaSerializationTestNonEmpty() throws Exception { ByteString byteString = factory.encodeUtf8(bronzeHorseman); assertEquivalent(byteString, TestUtil.reserialize(byteString)); } @Test public void javaSerializationTestEmpty() throws Exception { ByteString byteString = factory.decodeHex(""); assertEquivalent(byteString, TestUtil.reserialize(byteString)); } @Test public void compareToSingleBytes() throws Exception { List originalByteStrings = Arrays.asList( factory.decodeHex("00"), factory.decodeHex("01"), factory.decodeHex("7e"), factory.decodeHex("7f"), factory.decodeHex("80"), factory.decodeHex("81"), factory.decodeHex("fe"), factory.decodeHex("ff")); List sortedByteStrings = new ArrayList<>(originalByteStrings); Collections.shuffle(sortedByteStrings, new Random(0)); Collections.sort(sortedByteStrings); assertEquals(originalByteStrings, sortedByteStrings); } @Test public void compareToMultipleBytes() throws Exception { List originalByteStrings = Arrays.asList( factory.decodeHex(""), factory.decodeHex("00"), factory.decodeHex("0000"), factory.decodeHex("000000"), factory.decodeHex("00000000"), factory.decodeHex("0000000000"), factory.decodeHex("0000000001"), factory.decodeHex("000001"), factory.decodeHex("00007f"), factory.decodeHex("0000ff"), factory.decodeHex("000100"), factory.decodeHex("000101"), factory.decodeHex("007f00"), factory.decodeHex("00ff00"), factory.decodeHex("010000"), factory.decodeHex("010001"), factory.decodeHex("01007f"), factory.decodeHex("0100ff"), factory.decodeHex("010100"), factory.decodeHex("01010000"), factory.decodeHex("0101000000"), factory.decodeHex("0101000001"), factory.decodeHex("010101"), factory.decodeHex("7f0000"), factory.decodeHex("7f0000ffff"), factory.decodeHex("ffffff")); List sortedByteStrings = new ArrayList<>(originalByteStrings); Collections.shuffle(sortedByteStrings, new Random(0)); Collections.sort(sortedByteStrings); assertEquals(originalByteStrings, sortedByteStrings); } @Test public void asByteBuffer() { assertEquals(0x42, ByteString.of((byte) 0x41, (byte) 0x42, (byte) 0x43).asByteBuffer().get(1)); } } okio-okio-parent-1.16.0/okio/src/test/java/okio/DeflaterSinkTest.java000066400000000000000000000126131335667376100254610ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; import java.io.InputStream; import java.util.zip.Deflater; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; import org.junit.Test; import static okio.TestUtil.randomBytes; import static okio.TestUtil.repeat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; public final class DeflaterSinkTest { @Test public void deflateWithClose() throws Exception { Buffer data = new Buffer(); String original = "They're moving in herds. They do move in herds."; data.writeUtf8(original); Buffer sink = new Buffer(); DeflaterSink deflaterSink = new DeflaterSink(sink, new Deflater()); deflaterSink.write(data, data.size()); deflaterSink.close(); Buffer inflated = inflate(sink); assertEquals(original, inflated.readUtf8()); } @Test public void deflateWithSyncFlush() throws Exception { String original = "Yes, yes, yes. That's why we're taking extreme precautions."; Buffer data = new Buffer(); data.writeUtf8(original); Buffer sink = new Buffer(); DeflaterSink deflaterSink = new DeflaterSink(sink, new Deflater()); deflaterSink.write(data, data.size()); deflaterSink.flush(); Buffer inflated = inflate(sink); assertEquals(original, inflated.readUtf8()); } @Test public void deflateWellCompressed() throws IOException { String original = repeat('a', 1024 * 1024); Buffer data = new Buffer(); data.writeUtf8(original); Buffer sink = new Buffer(); DeflaterSink deflaterSink = new DeflaterSink(sink, new Deflater()); deflaterSink.write(data, data.size()); deflaterSink.close(); Buffer inflated = inflate(sink); assertEquals(original, inflated.readUtf8()); } @Test public void deflatePoorlyCompressed() throws IOException { ByteString original = randomBytes(1024 * 1024); Buffer data = new Buffer(); data.write(original); Buffer sink = new Buffer(); DeflaterSink deflaterSink = new DeflaterSink(sink, new Deflater()); deflaterSink.write(data, data.size()); deflaterSink.close(); Buffer inflated = inflate(sink); assertEquals(original, inflated.readByteString()); } @Test public void multipleSegmentsWithoutCompression() throws IOException { Buffer buffer = new Buffer(); Deflater deflater = new Deflater(); deflater.setLevel(Deflater.NO_COMPRESSION); DeflaterSink deflaterSink = new DeflaterSink(buffer, deflater); int byteCount = Segment.SIZE * 4; deflaterSink.write(new Buffer().writeUtf8(repeat('a', byteCount)), byteCount); deflaterSink.close(); assertEquals(repeat('a', byteCount), inflate(buffer).readUtf8(byteCount)); } @Test public void deflateIntoNonemptySink() throws Exception { String original = "They're moving in herds. They do move in herds."; // Exercise all possible offsets for the outgoing segment. for (int i = 0; i < Segment.SIZE; i++) { Buffer data = new Buffer().writeUtf8(original); Buffer sink = new Buffer().writeUtf8(repeat('a', i)); DeflaterSink deflaterSink = new DeflaterSink(sink, new Deflater()); deflaterSink.write(data, data.size()); deflaterSink.close(); sink.skip(i); Buffer inflated = inflate(sink); assertEquals(original, inflated.readUtf8()); } } /** * This test deflates a single segment of without compression because that's * the easiest way to force close() to emit a large amount of data to the * underlying sink. */ @Test public void closeWithExceptionWhenWritingAndClosing() throws IOException { MockSink mockSink = new MockSink(); mockSink.scheduleThrow(0, new IOException("first")); mockSink.scheduleThrow(1, new IOException("second")); Deflater deflater = new Deflater(); deflater.setLevel(Deflater.NO_COMPRESSION); DeflaterSink deflaterSink = new DeflaterSink(mockSink, deflater); deflaterSink.write(new Buffer().writeUtf8(repeat('a', Segment.SIZE)), Segment.SIZE); try { deflaterSink.close(); fail(); } catch (IOException expected) { assertEquals("first", expected.getMessage()); } mockSink.assertLogContains("close()"); } /** * Uses streaming decompression to inflate {@code deflated}. The input must * either be finished or have a trailing sync flush. */ private Buffer inflate(Buffer deflated) throws IOException { InputStream deflatedIn = deflated.inputStream(); Inflater inflater = new Inflater(); InputStream inflatedIn = new InflaterInputStream(deflatedIn, inflater); Buffer result = new Buffer(); byte[] buffer = new byte[8192]; while (!inflater.needsInput() || deflated.size() > 0 || deflatedIn.available() > 0) { int count = inflatedIn.read(buffer, 0, buffer.length); if (count != -1) { result.write(buffer, 0, count); } } return result; } } okio-okio-parent-1.16.0/okio/src/test/java/okio/GzipSinkTest.java000066400000000000000000000037401335667376100246450ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; import org.junit.Test; import static okio.TestUtil.repeat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; public final class GzipSinkTest { @Test public void gzipGunzip() throws Exception { Buffer data = new Buffer(); String original = "It's a UNIX system! I know this!"; data.writeUtf8(original); Buffer sink = new Buffer(); GzipSink gzipSink = new GzipSink(sink); gzipSink.write(data, data.size()); gzipSink.close(); Buffer inflated = gunzip(sink); assertEquals(original, inflated.readUtf8()); } @Test public void closeWithExceptionWhenWritingAndClosing() throws IOException { MockSink mockSink = new MockSink(); mockSink.scheduleThrow(0, new IOException("first")); mockSink.scheduleThrow(1, new IOException("second")); GzipSink gzipSink = new GzipSink(mockSink); gzipSink.write(new Buffer().writeUtf8(repeat('a', Segment.SIZE)), Segment.SIZE); try { gzipSink.close(); fail(); } catch (IOException expected) { assertEquals("first", expected.getMessage()); } mockSink.assertLogContains("close()"); } private Buffer gunzip(Buffer gzipped) throws IOException { Buffer result = new Buffer(); GzipSource source = new GzipSource(gzipped); while (source.read(result, Integer.MAX_VALUE) != -1) { } return result; } } okio-okio-parent-1.16.0/okio/src/test/java/okio/GzipSourceTest.java000066400000000000000000000173051335667376100252030ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; import java.util.zip.CRC32; import org.junit.Test; import static okio.Util.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public final class GzipSourceTest { @Test public void gunzip() throws Exception { Buffer gzipped = new Buffer(); gzipped.write(gzipHeader); gzipped.write(deflated); gzipped.write(gzipTrailer); assertGzipped(gzipped); } @Test public void gunzip_withHCRC() throws Exception { CRC32 hcrc = new CRC32(); ByteString gzipHeader = gzipHeaderWithFlags((byte) 0x02); hcrc.update(gzipHeader.toByteArray()); Buffer gzipped = new Buffer(); gzipped.write(gzipHeader); gzipped.writeShort(Util.reverseBytesShort((short) hcrc.getValue())); // little endian gzipped.write(deflated); gzipped.write(gzipTrailer); assertGzipped(gzipped); } @Test public void gunzip_withExtra() throws Exception { Buffer gzipped = new Buffer(); gzipped.write(gzipHeaderWithFlags((byte) 0x04)); gzipped.writeShort(Util.reverseBytesShort((short) 7)); // little endian extra length gzipped.write("blubber".getBytes(UTF_8), 0, 7); gzipped.write(deflated); gzipped.write(gzipTrailer); assertGzipped(gzipped); } @Test public void gunzip_withName() throws Exception { Buffer gzipped = new Buffer(); gzipped.write(gzipHeaderWithFlags((byte) 0x08)); gzipped.write("foo.txt".getBytes(UTF_8), 0, 7); gzipped.writeByte(0); // zero-terminated gzipped.write(deflated); gzipped.write(gzipTrailer); assertGzipped(gzipped); } @Test public void gunzip_withComment() throws Exception { Buffer gzipped = new Buffer(); gzipped.write(gzipHeaderWithFlags((byte) 0x10)); gzipped.write("rubbish".getBytes(UTF_8), 0, 7); gzipped.writeByte(0); // zero-terminated gzipped.write(deflated); gzipped.write(gzipTrailer); assertGzipped(gzipped); } /** * For portability, it is a good idea to export the gzipped bytes and try running gzip. Ex. * {@code echo gzipped | base64 --decode | gzip -l -v} */ @Test public void gunzip_withAll() throws Exception { Buffer gzipped = new Buffer(); gzipped.write(gzipHeaderWithFlags((byte) 0x1c)); gzipped.writeShort(Util.reverseBytesShort((short) 7)); // little endian extra length gzipped.write("blubber".getBytes(UTF_8), 0, 7); gzipped.write("foo.txt".getBytes(UTF_8), 0, 7); gzipped.writeByte(0); // zero-terminated gzipped.write("rubbish".getBytes(UTF_8), 0, 7); gzipped.writeByte(0); // zero-terminated gzipped.write(deflated); gzipped.write(gzipTrailer); assertGzipped(gzipped); } private void assertGzipped(Buffer gzipped) throws IOException { Buffer gunzipped = gunzip(gzipped); assertEquals("It's a UNIX system! I know this!", gunzipped.readUtf8()); } /** * Note that you cannot test this with old versions of gzip, as they interpret flag bit 1 as * CONTINUATION, not HCRC. For example, this is the case with the default gzip on osx. */ @Test public void gunzipWhenHeaderCRCIncorrect() throws Exception { Buffer gzipped = new Buffer(); gzipped.write(gzipHeaderWithFlags((byte) 0x02)); gzipped.writeShort((short) 0); // wrong HCRC! gzipped.write(deflated); gzipped.write(gzipTrailer); try { gunzip(gzipped); fail(); } catch (IOException e) { assertEquals("FHCRC: actual 0x0000261d != expected 0x00000000", e.getMessage()); } } @Test public void gunzipWhenCRCIncorrect() throws Exception { Buffer gzipped = new Buffer(); gzipped.write(gzipHeader); gzipped.write(deflated); gzipped.writeInt(Util.reverseBytesInt(0x1234567)); // wrong CRC gzipped.write(gzipTrailer.toByteArray(), 3, 4); try { gunzip(gzipped); fail(); } catch (IOException e) { assertEquals("CRC: actual 0x37ad8f8d != expected 0x01234567", e.getMessage()); } } @Test public void gunzipWhenLengthIncorrect() throws Exception { Buffer gzipped = new Buffer(); gzipped.write(gzipHeader); gzipped.write(deflated); gzipped.write(gzipTrailer.toByteArray(), 0, 4); gzipped.writeInt(Util.reverseBytesInt(0x123456)); // wrong length try { gunzip(gzipped); fail(); } catch (IOException e) { assertEquals("ISIZE: actual 0x00000020 != expected 0x00123456", e.getMessage()); } } @Test public void gunzipExhaustsSource() throws Exception { Buffer gzippedSource = new Buffer() .write(ByteString.decodeHex("1f8b08000000000000004b4c4a0600c241243503000000")); // 'abc' ExhaustableSource exhaustableSource = new ExhaustableSource(gzippedSource); BufferedSource gunzippedSource = Okio.buffer(new GzipSource(exhaustableSource)); assertEquals('a', gunzippedSource.readByte()); assertEquals('b', gunzippedSource.readByte()); assertEquals('c', gunzippedSource.readByte()); assertFalse(exhaustableSource.exhausted); assertEquals(-1, gunzippedSource.read(new Buffer(), 1)); assertTrue(exhaustableSource.exhausted); } @Test public void gunzipThrowsIfSourceIsNotExhausted() throws Exception { Buffer gzippedSource = new Buffer() .write(ByteString.decodeHex("1f8b08000000000000004b4c4a0600c241243503000000")); // 'abc' gzippedSource.writeByte('d'); // This byte shouldn't be here! BufferedSource gunzippedSource = Okio.buffer(new GzipSource(gzippedSource)); assertEquals('a', gunzippedSource.readByte()); assertEquals('b', gunzippedSource.readByte()); assertEquals('c', gunzippedSource.readByte()); try { gunzippedSource.readByte(); fail(); } catch (IOException expected) { } } private ByteString gzipHeaderWithFlags(byte flags) { byte[] result = gzipHeader.toByteArray(); result[3] = flags; return ByteString.of(result); } private final ByteString gzipHeader = ByteString.decodeHex("1f8b0800000000000000"); // Deflated "It's a UNIX system! I know this!" private final ByteString deflated = ByteString.decodeHex( "f32c512f56485408f5f38c5028ae2c2e49cd5554f054c8cecb2f5728c9c82c560400"); private final ByteString gzipTrailer = ByteString.decodeHex("" + "8d8fad37" // Checksum of deflated. + "20000000" // 32 in little endian. ); private Buffer gunzip(Buffer gzipped) throws IOException { Buffer result = new Buffer(); GzipSource source = new GzipSource(gzipped); while (source.read(result, Integer.MAX_VALUE) != -1) { } return result; } /** This source keeps track of whether its read has returned -1. */ static class ExhaustableSource implements Source { private final Source source; private boolean exhausted; ExhaustableSource(Source source) { this.source = source; } @Override public long read(Buffer sink, long byteCount) throws IOException { long result = source.read(sink, byteCount); if (result == -1) exhausted = true; return result; } @Override public Timeout timeout() { return source.timeout(); } @Override public void close() throws IOException { source.close(); } } } okio-okio-parent-1.16.0/okio/src/test/java/okio/HashingSinkTest.java000066400000000000000000000101371335667376100253130ustar00rootroot00000000000000/* * Copyright (C) 2016 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import org.junit.Test; import static okio.HashingTest.HMAC_KEY; import static okio.HashingTest.HMAC_SHA1_abc; import static okio.HashingTest.HMAC_SHA256_abc; import static okio.HashingTest.HMAC_SHA512_abc; import static okio.HashingTest.MD5_abc; import static okio.HashingTest.SHA1_abc; import static okio.HashingTest.SHA256_abc; import static okio.HashingTest.SHA256_def; import static okio.HashingTest.SHA256_r32k; import static okio.HashingTest.r32k; import static okio.HashingTest.SHA512_abc; import static org.junit.Assert.assertEquals; public final class HashingSinkTest { private final Buffer source = new Buffer(); private final Buffer sink = new Buffer(); @Test public void md5() throws Exception { HashingSink hashingSink = HashingSink.md5(sink); source.writeUtf8("abc"); hashingSink.write(source, 3L); assertEquals(MD5_abc, hashingSink.hash()); } @Test public void sha1() throws Exception { HashingSink hashingSink = HashingSink.sha1(sink); source.writeUtf8("abc"); hashingSink.write(source, 3L); assertEquals(SHA1_abc, hashingSink.hash()); } @Test public void sha256() throws Exception { HashingSink hashingSink = HashingSink.sha256(sink); source.writeUtf8("abc"); hashingSink.write(source, 3L); assertEquals(SHA256_abc, hashingSink.hash()); } @Test public void sha512() throws Exception { HashingSink hashingSink = HashingSink.sha512(sink); source.writeUtf8("abc"); hashingSink.write(source, 3L); assertEquals(SHA512_abc, hashingSink.hash()); } @Test public void hmacSha1() throws Exception { HashingSink hashingSink = HashingSink.hmacSha1(sink, HMAC_KEY); source.writeUtf8("abc"); hashingSink.write(source, 3L); assertEquals(HMAC_SHA1_abc, hashingSink.hash()); } @Test public void hmacSha256() throws Exception { HashingSink hashingSink = HashingSink.hmacSha256(sink, HMAC_KEY); source.writeUtf8("abc"); hashingSink.write(source, 3L); assertEquals(HMAC_SHA256_abc, hashingSink.hash()); } @Test public void hmacSha512() throws Exception { HashingSink hashingSink = HashingSink.hmacSha512(sink, HMAC_KEY); source.writeUtf8("abc"); hashingSink.write(source, 3L); assertEquals(HMAC_SHA512_abc, hashingSink.hash()); } @Test public void multipleWrites() throws Exception { HashingSink hashingSink = HashingSink.sha256(sink); source.writeUtf8("a"); hashingSink.write(source, 1L); source.writeUtf8("b"); hashingSink.write(source, 1L); source.writeUtf8("c"); hashingSink.write(source, 1L); assertEquals(SHA256_abc, hashingSink.hash()); } @Test public void multipleHashes() throws Exception { HashingSink hashingSink = HashingSink.sha256(sink); source.writeUtf8("abc"); hashingSink.write(source, 3L); assertEquals(SHA256_abc, hashingSink.hash()); source.writeUtf8("def"); hashingSink.write(source, 3L); assertEquals(SHA256_def, hashingSink.hash()); } @Test public void multipleSegments() throws Exception { HashingSink hashingSink = HashingSink.sha256(sink); source.write(r32k); hashingSink.write(source, r32k.size()); assertEquals(SHA256_r32k, hashingSink.hash()); } @Test public void readFromPrefixOfBuffer() throws Exception { source.writeUtf8("z"); source.write(r32k); source.skip(1); source.writeUtf8(TestUtil.repeat('z', Segment.SIZE * 2 - 1)); HashingSink hashingSink = HashingSink.sha256(sink); hashingSink.write(source, r32k.size()); assertEquals(SHA256_r32k, hashingSink.hash()); } } okio-okio-parent-1.16.0/okio/src/test/java/okio/HashingSourceTest.java000066400000000000000000000104021335667376100256420ustar00rootroot00000000000000/* * Copyright (C) 2016 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import org.junit.Test; import static okio.HashingTest.HMAC_KEY; import static okio.HashingTest.HMAC_SHA1_abc; import static okio.HashingTest.HMAC_SHA256_abc; import static okio.HashingTest.MD5_abc; import static okio.HashingTest.SHA1_abc; import static okio.HashingTest.SHA256_abc; import static okio.HashingTest.SHA256_def; import static okio.HashingTest.SHA256_r32k; import static okio.HashingTest.r32k; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; public final class HashingSourceTest { private final Buffer source = new Buffer(); private final Buffer sink = new Buffer(); @Test public void md5() throws Exception { HashingSource hashingSource = HashingSource.md5(source); source.writeUtf8("abc"); assertEquals(3L, hashingSource.read(sink, Long.MAX_VALUE)); assertEquals(MD5_abc, hashingSource.hash()); } @Test public void sha1() throws Exception { HashingSource hashingSource = HashingSource.sha1(source); source.writeUtf8("abc"); assertEquals(3L, hashingSource.read(sink, Long.MAX_VALUE)); assertEquals(SHA1_abc, hashingSource.hash()); } @Test public void sha256() throws Exception { HashingSource hashingSource = HashingSource.sha256(source); source.writeUtf8("abc"); assertEquals(3L, hashingSource.read(sink, Long.MAX_VALUE)); assertEquals(SHA256_abc, hashingSource.hash()); } @Test public void hmacSha1() throws Exception { HashingSource hashingSource = HashingSource.hmacSha1(source, HMAC_KEY); source.writeUtf8("abc"); assertEquals(3L, hashingSource.read(sink, Long.MAX_VALUE)); assertEquals(HMAC_SHA1_abc, hashingSource.hash()); } @Test public void hmacSha256() throws Exception { HashingSource hashingSource = HashingSource.hmacSha256(source, HMAC_KEY); source.writeUtf8("abc"); assertEquals(3L, hashingSource.read(sink, Long.MAX_VALUE)); assertEquals(HMAC_SHA256_abc, hashingSource.hash()); } @Test public void multipleReads() throws Exception { HashingSource hashingSource = HashingSource.sha256(source); BufferedSource bufferedSource = Okio.buffer(hashingSource); source.writeUtf8("a"); assertEquals('a', bufferedSource.readUtf8CodePoint()); source.writeUtf8("b"); assertEquals('b', bufferedSource.readUtf8CodePoint()); source.writeUtf8("c"); assertEquals('c', bufferedSource.readUtf8CodePoint()); assertEquals(SHA256_abc, hashingSource.hash()); } @Test public void multipleHashes() throws Exception { HashingSource hashingSource = HashingSource.sha256(source); source.writeUtf8("abc"); assertEquals(3L, hashingSource.read(sink, Long.MAX_VALUE)); assertEquals(SHA256_abc, hashingSource.hash()); source.writeUtf8("def"); assertEquals(3L, hashingSource.read(sink, Long.MAX_VALUE)); assertEquals(SHA256_def, hashingSource.hash()); } @Test public void multipleSegments() throws Exception { HashingSource hashingSource = HashingSource.sha256(source); BufferedSource bufferedSource = Okio.buffer(hashingSource); source.write(r32k); assertEquals(r32k, bufferedSource.readByteString()); assertEquals(SHA256_r32k, hashingSource.hash()); } @Test public void readIntoSuffixOfBuffer() throws Exception { HashingSource hashingSource = HashingSource.sha256(source); source.write(r32k); sink.writeUtf8(TestUtil.repeat('z', Segment.SIZE * 2 - 1)); assertEquals(r32k.size(), hashingSource.read(sink, Long.MAX_VALUE)); assertEquals(SHA256_r32k, hashingSource.hash()); } @Test public void hmacEmptyKey() throws Exception { try { HashingSource.hmacSha256(source, ByteString.EMPTY); fail(); } catch (IllegalArgumentException expected) { } } } okio-okio-parent-1.16.0/okio/src/test/java/okio/HashingTest.java000066400000000000000000000126011335667376100244640ustar00rootroot00000000000000/* * Copyright 2014 Square Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import org.junit.Test; import static org.junit.Assert.assertEquals; public final class HashingTest { public static final ByteString HMAC_KEY = ByteString.decodeHex("0102030405060708"); public static final ByteString MD5_abc = ByteString.decodeHex( "900150983cd24fb0d6963f7d28e17f72"); public static final ByteString SHA1_abc = ByteString.decodeHex( "a9993e364706816aba3e25717850c26c9cd0d89d"); public static final ByteString HMAC_SHA1_abc = ByteString.decodeHex( "987af8649982ff7d9fbb1b8aa35099146997af51"); public static final ByteString SHA256_abc = ByteString.decodeHex( "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); public static final ByteString SHA256_empty = ByteString.decodeHex( "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); public static final ByteString SHA256_def = ByteString.decodeHex( "cb8379ac2098aa165029e3938a51da0bcecfc008fd6795f401178647f96c5b34"); public static final ByteString SHA256_r32k = ByteString.decodeHex( "3a640aa4d129671cb36a4bfbed652d732bce6b7ea83ccdd080c485b8c9ef479d"); public static final ByteString SHA512_abc = ByteString.decodeHex( "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"); public static final ByteString SHA512_empty = ByteString.decodeHex( "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"); public static final ByteString HMAC_SHA256_empty = ByteString.decodeHex( "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); public static final ByteString HMAC_SHA256_abc = ByteString.decodeHex( "446d1715583cf1c30dfffbec0df4ff1f9d39d493211ab4c97ed6f3f0eb579b47"); public static final ByteString HMAC_SHA512_empty = ByteString.decodeHex( "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"); public static final ByteString HMAC_SHA512_abc = ByteString.decodeHex( "24391790e7131050b05b606f2079a8983313894a1642a5ed97d094e7cabd00cfaa857d92c1f320ca3b6aaabb84c7155d6f1b10940dc133ded1b40baee8900be6"); public static final ByteString r32k = TestUtil.randomBytes(32768); @Test public void byteStringMd5() { assertEquals(MD5_abc, ByteString.encodeUtf8("abc").md5()); } @Test public void byteStringSha1() { assertEquals(SHA1_abc, ByteString.encodeUtf8("abc").sha1()); } @Test public void byteStringSha256() { assertEquals(SHA256_abc, ByteString.encodeUtf8("abc").sha256()); } @Test public void byteStringSha512() { assertEquals(SHA512_abc, ByteString.encodeUtf8("abc").sha512()); } @Test public void byteStringHmacSha1() { assertEquals(HMAC_SHA1_abc, ByteString.encodeUtf8("abc").hmacSha1(HMAC_KEY)); } @Test public void byteStringHmacSha256() { assertEquals(HMAC_SHA256_abc, ByteString.encodeUtf8("abc").hmacSha256(HMAC_KEY)); } @Test public void byteStringHmacSha512() { assertEquals(HMAC_SHA512_abc, ByteString.encodeUtf8("abc").hmacSha512(HMAC_KEY)); } @Test public void bufferMd5() { assertEquals(MD5_abc, new Buffer().writeUtf8("abc").md5()); } @Test public void bufferSha1() { assertEquals(SHA1_abc, new Buffer().writeUtf8("abc").sha1()); } @Test public void bufferSha256() { assertEquals(SHA256_abc, new Buffer().writeUtf8("abc").sha256()); } @Test public void bufferSha512() { assertEquals(SHA512_abc, new Buffer().writeUtf8("abc").sha512()); } @Test public void hashEmptySha256Buffer() { assertEquals(SHA256_empty, new Buffer().sha256()); } @Test public void hashEmptySha512Buffer() { assertEquals(SHA512_empty, new Buffer().sha512()); } @Test public void bufferHmacSha1() { assertEquals(HMAC_SHA1_abc, new Buffer().writeUtf8("abc").hmacSha1(HMAC_KEY)); } @Test public void bufferHmacSha256() { assertEquals(HMAC_SHA256_abc, new Buffer().writeUtf8("abc").hmacSha256(HMAC_KEY)); } @Test public void bufferHmacSha512() { assertEquals(HMAC_SHA512_abc, new Buffer().writeUtf8("abc").hmacSha512(HMAC_KEY)); } @Test public void hmacSha256EmptyBuffer() { assertEquals(HMAC_SHA256_empty, new Buffer().sha256()); } @Test public void hmacSha512EmptyBuffer() { assertEquals(HMAC_SHA512_empty, new Buffer().sha512()); } @Test public void bufferHashIsNotDestructive() { Buffer buffer = new Buffer(); buffer.writeUtf8("abc"); assertEquals(SHA256_abc, buffer.sha256()); assertEquals("abc", buffer.readUtf8()); buffer.writeUtf8("def"); assertEquals(SHA256_def, buffer.sha256()); assertEquals("def", buffer.readUtf8()); buffer.write(r32k); assertEquals(SHA256_r32k, buffer.sha256()); assertEquals(r32k, buffer.readByteString()); } } okio-okio-parent-1.16.0/okio/src/test/java/okio/InflaterSourceTest.java000066400000000000000000000133661335667376100260410ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.EOFException; import java.io.IOException; import java.util.zip.DeflaterOutputStream; import java.util.zip.Inflater; import org.junit.Test; import static okio.TestUtil.randomBytes; import static okio.TestUtil.repeat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; public final class InflaterSourceTest { @Test public void inflate() throws Exception { Buffer deflated = decodeBase64("eJxzz09RyEjNKVAoLdZRKE9VL0pVyMxTKMlIVchIzEspVshPU0jNS8/MS00tK" + "tYDAF6CD5s="); Buffer inflated = inflate(deflated); assertEquals("God help us, we're in the hands of engineers.", inflated.readUtf8()); } @Test public void inflateTruncated() throws Exception { Buffer deflated = decodeBase64("eJxzz09RyEjNKVAoLdZRKE9VL0pVyMxTKMlIVchIzEspVshPU0jNS8/MS00tK" + "tYDAF6CDw=="); try { inflate(deflated); fail(); } catch (EOFException expected) { } } @Test public void inflateWellCompressed() throws Exception { Buffer deflated = decodeBase64("eJztwTEBAAAAwqCs61/CEL5AAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8B" + "tFeWvE=\n"); String original = repeat('a', 1024 * 1024); Buffer inflated = inflate(deflated); assertEquals(original, inflated.readUtf8()); } @Test public void inflatePoorlyCompressed() throws Exception { ByteString original = randomBytes(1024 * 1024); Buffer deflated = deflate(original); Buffer inflated = inflate(deflated); assertEquals(original, inflated.readByteString()); } @Test public void inflateIntoNonemptySink() throws Exception { for (int i = 0; i < Segment.SIZE; i++) { Buffer inflated = new Buffer().writeUtf8(repeat('a', i)); Buffer deflated = decodeBase64( "eJxzz09RyEjNKVAoLdZRKE9VL0pVyMxTKMlIVchIzEspVshPU0jNS8/MS00tKtYDAF6CD5s="); InflaterSource source = new InflaterSource(deflated, new Inflater()); while (source.read(inflated, Integer.MAX_VALUE) != -1) { } inflated.skip(i); assertEquals("God help us, we're in the hands of engineers.", inflated.readUtf8()); } } @Test public void inflateSingleByte() throws Exception { Buffer inflated = new Buffer(); Buffer deflated = decodeBase64( "eJxzz09RyEjNKVAoLdZRKE9VL0pVyMxTKMlIVchIzEspVshPU0jNS8/MS00tKtYDAF6CD5s="); InflaterSource source = new InflaterSource(deflated, new Inflater()); source.read(inflated, 1); source.close(); assertEquals("G", inflated.readUtf8()); assertEquals(0, inflated.size()); } @Test public void inflateByteCount() throws Exception { Buffer inflated = new Buffer(); Buffer deflated = decodeBase64( "eJxzz09RyEjNKVAoLdZRKE9VL0pVyMxTKMlIVchIzEspVshPU0jNS8/MS00tKtYDAF6CD5s="); InflaterSource source = new InflaterSource(deflated, new Inflater()); source.read(inflated, 11); source.close(); assertEquals("God help us", inflated.readUtf8()); assertEquals(0, inflated.size()); } private Buffer decodeBase64(String s) { return new Buffer().write(ByteString.decodeBase64(s)); } /** Use DeflaterOutputStream to deflate source. */ private Buffer deflate(ByteString source) throws IOException { Buffer result = new Buffer(); Sink sink = Okio.sink(new DeflaterOutputStream(result.outputStream())); sink.write(new Buffer().write(source), source.size()); sink.close(); return result; } /** Returns a new buffer containing the inflated contents of {@code deflated}. */ private Buffer inflate(Buffer deflated) throws IOException { Buffer result = new Buffer(); InflaterSource source = new InflaterSource(deflated, new Inflater()); while (source.read(result, Integer.MAX_VALUE) != -1) { } return result; } } okio-okio-parent-1.16.0/okio/src/test/java/okio/LargeStreamsTest.java000066400000000000000000000103731335667376100255000ustar00rootroot00000000000000/* * Copyright (C) 2016 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; import java.io.OutputStream; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.zip.Deflater; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import org.junit.Test; import static okio.TestUtil.randomSource; import static org.junit.Assert.assertEquals; /** Slow running tests that run a large amount of data through a stream. */ public final class LargeStreamsTest { /** 4 GiB plus 1 byte. This is greater than what can be expressed in an unsigned int. */ public static final long FOUR_GIB_PLUS_ONE = 0x100000001L; /** SHA-256 of {@code TestUtil.randomSource(FOUR_GIB_PLUS_ONE)}. */ public static final ByteString SHA256_RANDOM_FOUR_GIB_PLUS_1 = ByteString.decodeHex( "9654947a655c5efc445502fd1bf11117d894b7812b7974fde8ca4a02c5066315"); @Test public void test() throws Exception { Pipe pipe = new Pipe(1024 * 1024); Future future = readAllAndCloseAsync(randomSource(FOUR_GIB_PLUS_ONE), pipe.sink()); HashingSink hashingSink = HashingSink.sha256(Okio.blackhole()); readAllAndClose(pipe.source(), hashingSink); assertEquals(FOUR_GIB_PLUS_ONE, (long) future.get()); assertEquals(SHA256_RANDOM_FOUR_GIB_PLUS_1, hashingSink.hash()); } @Test public void gzipSource() throws Exception { Pipe pipe = new Pipe(1024 * 1024); OutputStream gzipOut = new GZIPOutputStream(Okio.buffer(pipe.sink()).outputStream()) { { // Disable compression to speed up a slow test. Improved from 141s to 33s on one machine. def.setLevel(Deflater.NO_COMPRESSION); } }; Future future = readAllAndCloseAsync( randomSource(FOUR_GIB_PLUS_ONE), Okio.sink(gzipOut)); HashingSink hashingSink = HashingSink.sha256(Okio.blackhole()); GzipSource gzipSource = new GzipSource(pipe.source()); readAllAndClose(gzipSource, hashingSink); assertEquals(FOUR_GIB_PLUS_ONE, (long) future.get()); assertEquals(SHA256_RANDOM_FOUR_GIB_PLUS_1, hashingSink.hash()); } @Test public void gzipSink() throws Exception { Pipe pipe = new Pipe(1024 * 1024); GzipSink gzipSink = new GzipSink(pipe.sink()); // Disable compression to speed up a slow test. Improved from 141s to 35s on one machine. gzipSink.deflater().setLevel(Deflater.NO_COMPRESSION); Future future = readAllAndCloseAsync(randomSource(FOUR_GIB_PLUS_ONE), gzipSink); HashingSink hashingSink = HashingSink.sha256(Okio.blackhole()); GZIPInputStream gzipIn = new GZIPInputStream(Okio.buffer(pipe.source()).inputStream()); readAllAndClose(Okio.source(gzipIn), hashingSink); assertEquals(FOUR_GIB_PLUS_ONE, (long) future.get()); assertEquals(SHA256_RANDOM_FOUR_GIB_PLUS_1, hashingSink.hash()); } /** Reads all bytes from {@code source} and writes them to {@code sink}. */ private Long readAllAndClose(Source source, Sink sink) throws IOException { long result = 0L; Buffer buffer = new Buffer(); for (long count; (count = source.read(buffer, Segment.SIZE)) != -1L; result += count) { sink.write(buffer, count); } source.close(); sink.close(); return result; } /** Calls {@link #readAllAndClose} on a background thread. */ private Future readAllAndCloseAsync(final Source source, final Sink sink) { ExecutorService executor = Executors.newSingleThreadExecutor(); try { return executor.submit(new Callable() { @Override public Long call() throws Exception { return readAllAndClose(source, sink); } }); } finally { executor.shutdown(); } } } okio-okio-parent-1.16.0/okio/src/test/java/okio/MockSink.java000066400000000000000000000040601335667376100237610ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** A scriptable sink. Like Mockito, but worse and requiring less configuration. */ final class MockSink implements Sink { private final List log = new ArrayList(); private final Map callThrows = new LinkedHashMap(); public void assertLog(String... messages) { assertEquals(Arrays.asList(messages), log); } public void assertLogContains(String message) { assertTrue(log.contains(message)); } public void scheduleThrow(int call, IOException e) { callThrows.put(call, e); } private void throwIfScheduled() throws IOException { IOException exception = callThrows.get(log.size() - 1); if (exception != null) throw exception; } @Override public void write(Buffer source, long byteCount) throws IOException { log.add("write(" + source + ", " + byteCount + ")"); source.skip(byteCount); throwIfScheduled(); } @Override public void flush() throws IOException { log.add("flush()"); throwIfScheduled(); } @Override public Timeout timeout() { log.add("timeout()"); return Timeout.NONE; } @Override public void close() throws IOException { log.add("close()"); throwIfScheduled(); } } okio-okio-parent-1.16.0/okio/src/test/java/okio/NioTest.java000066400000000000000000000117631335667376100236400ustar00rootroot00000000000000/* * Copyright (C) 2018 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.File; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.nio.file.StandardOpenOption; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertTrue; /** Test interop between our beloved Okio and java.nio. */ public final class NioTest { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Test public void sourceIsOpen() throws Exception { BufferedSource source = Okio.buffer((Source) new Buffer()); assertTrue(source.isOpen()); source.close(); assertFalse(source.isOpen()); } @Test public void sinkIsOpen() throws Exception { BufferedSink sink = Okio.buffer((Sink) new Buffer()); assertTrue(sink.isOpen()); sink.close(); assertFalse(sink.isOpen()); } @Test public void writableChannelNioFile() throws Exception { File file = temporaryFolder.newFile(); FileChannel fileChannel = FileChannel.open(file.toPath(), StandardOpenOption.WRITE); testWritableByteChannel(fileChannel); BufferedSource emitted = Okio.buffer(Okio.source(file)); assertEquals("defghijklmnopqrstuvw", emitted.readUtf8()); emitted.close(); } @Test public void writableChannelBuffer() throws Exception { Buffer buffer = new Buffer(); testWritableByteChannel(buffer); assertEquals("defghijklmnopqrstuvw", buffer.readUtf8()); } @Test public void writableChannelBufferedSink() throws Exception { Buffer buffer = new Buffer(); BufferedSink bufferedSink = Okio.buffer((Sink) buffer); testWritableByteChannel(bufferedSink); assertEquals("defghijklmnopqrstuvw", buffer.readUtf8()); } @Test public void readableChannelNioFile() throws Exception { File file = temporaryFolder.newFile(); BufferedSink initialData = Okio.buffer(Okio.sink(file)); initialData.writeUtf8("abcdefghijklmnopqrstuvwxyz"); initialData.close(); FileChannel fileChannel = FileChannel.open(file.toPath(), StandardOpenOption.READ); testReadableByteChannel(fileChannel); } @Test public void readableChannelBuffer() throws Exception { Buffer buffer = new Buffer(); buffer.writeUtf8("abcdefghijklmnopqrstuvwxyz"); testReadableByteChannel(buffer); } @Test public void readableChannelBufferedSource() throws Exception { Buffer buffer = new Buffer(); BufferedSource bufferedSource = Okio.buffer((Source) buffer); buffer.writeUtf8("abcdefghijklmnopqrstuvwxyz"); testReadableByteChannel(bufferedSource); } /** * Does some basic writes to {@code channel}. We execute this against both Okio's channels and * also a standard implementation from the JDK to confirm that their behavior is consistent. */ private void testWritableByteChannel(WritableByteChannel channel) throws Exception { assertTrue(channel.isOpen()); ByteBuffer byteBuffer = ByteBuffer.allocate(1024); byteBuffer.put("abcdefghijklmnopqrstuvwxyz".getBytes(Util.UTF_8)); byteBuffer.flip(); byteBuffer.position(3); byteBuffer.limit(23); int byteCount = channel.write(byteBuffer); assertEquals(20, byteCount); assertEquals(23, byteBuffer.position()); assertEquals(23, byteBuffer.limit()); channel.close(); assertEquals(channel instanceof Buffer, channel.isOpen()); // Buffer.close() does nothing. } /** * Does some basic reads from {@code channel}. We execute this against both Okio's channels and * also a standard implementation from the JDK to confirm that their behavior is consistent. */ private void testReadableByteChannel(ReadableByteChannel channel) throws Exception { assertTrue(channel.isOpen()); ByteBuffer byteBuffer = ByteBuffer.allocate(1024); byteBuffer.position(3); byteBuffer.limit(23); int byteCount = channel.read(byteBuffer); assertEquals(20, byteCount); assertEquals(23, byteBuffer.position()); assertEquals(23, byteBuffer.limit()); channel.close(); assertEquals(channel instanceof Buffer, channel.isOpen()); // Buffer.close() does nothing. byteBuffer.flip(); byteBuffer.position(3); byte[] data = new byte[byteBuffer.remaining()]; byteBuffer.get(data); assertEquals("abcdefghijklmnopqrst", new String(data, Util.UTF_8)); } } okio-okio-parent-1.16.0/okio/src/test/java/okio/OkioTest.java000066400000000000000000000113441335667376100240070ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import static okio.TestUtil.repeat; import static okio.Util.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public final class OkioTest { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Test public void readWriteFile() throws Exception { File file = temporaryFolder.newFile(); BufferedSink sink = Okio.buffer(Okio.sink(file)); sink.writeUtf8("Hello, java.io file!"); sink.close(); assertTrue(file.exists()); assertEquals(20, file.length()); BufferedSource source = Okio.buffer(Okio.source(file)); assertEquals("Hello, java.io file!", source.readUtf8()); source.close(); } @Test public void appendFile() throws Exception { File file = temporaryFolder.newFile(); BufferedSink sink = Okio.buffer(Okio.appendingSink(file)); sink.writeUtf8("Hello, "); sink.close(); assertTrue(file.exists()); assertEquals(7, file.length()); sink = Okio.buffer(Okio.appendingSink(file)); sink.writeUtf8("java.io file!"); sink.close(); assertEquals(20, file.length()); BufferedSource source = Okio.buffer(Okio.source(file)); assertEquals("Hello, java.io file!", source.readUtf8()); source.close(); } @Test public void readWritePath() throws Exception { Path path = temporaryFolder.newFile().toPath(); BufferedSink sink = Okio.buffer(Okio.sink(path)); sink.writeUtf8("Hello, java.nio file!"); sink.close(); assertTrue(Files.exists(path)); assertEquals(21, Files.size(path)); BufferedSource source = Okio.buffer(Okio.source(path)); assertEquals("Hello, java.nio file!", source.readUtf8()); source.close(); } @Test public void sinkFromOutputStream() throws Exception { Buffer data = new Buffer(); data.writeUtf8("a"); data.writeUtf8(repeat('b', 9998)); data.writeUtf8("c"); ByteArrayOutputStream out = new ByteArrayOutputStream(); Sink sink = Okio.sink(out); sink.write(data, 3); assertEquals("abb", out.toString("UTF-8")); sink.write(data, data.size()); assertEquals("a" + repeat('b', 9998) + "c", out.toString("UTF-8")); } @Test public void sourceFromInputStream() throws Exception { InputStream in = new ByteArrayInputStream( ("a" + repeat('b', Segment.SIZE * 2) + "c").getBytes(UTF_8)); // Source: ab...bc Source source = Okio.source(in); Buffer sink = new Buffer(); // Source: b...bc. Sink: abb. assertEquals(3, source.read(sink, 3)); assertEquals("abb", sink.readUtf8(3)); // Source: b...bc. Sink: b...b. assertEquals(Segment.SIZE, source.read(sink, 20000)); assertEquals(repeat('b', Segment.SIZE), sink.readUtf8()); // Source: b...bc. Sink: b...bc. assertEquals(Segment.SIZE - 1, source.read(sink, 20000)); assertEquals(repeat('b', Segment.SIZE - 2) + "c", sink.readUtf8()); // Source and sink are empty. assertEquals(-1, source.read(sink, 1)); } @Test public void sourceFromInputStreamBounds() throws Exception { Source source = Okio.source(new ByteArrayInputStream(new byte[100])); try { source.read(new Buffer(), -1); fail(); } catch (IllegalArgumentException expected) { } } @Test public void bufferSinkThrowsOnNull() { try { Okio.buffer((Sink) null); fail(); } catch (NullPointerException expected) { assertEquals("sink == null", expected.getMessage()); } } @Test public void bufferSourceThrowsOnNull() { try { Okio.buffer((Source) null); fail(); } catch (NullPointerException expected) { assertEquals("source == null", expected.getMessage()); } } @Test public void blackhole() throws Exception { Buffer data = new Buffer(); data.writeUtf8("blackhole"); Sink blackhole = Okio.blackhole(); blackhole.write(data, 5); assertEquals("hole", data.readUtf8()); } } okio-okio-parent-1.16.0/okio/src/test/java/okio/OptionsTest.java000066400000000000000000000232001335667376100245330ustar00rootroot00000000000000/* * Copyright (C) 2018 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; public final class OptionsTest { /** Confirm that options prefers the first-listed option, not the longest or shortest one. */ @Test public void optionOrderTakesPrecedence() { assertSelect("abcdefg", 0, "abc", "abcdef"); assertSelect("abcdefg", 0, "abcdef", "abc"); } @Test public void simpleOptionsTrie() { assertEquals(trieString(utf8Options("hotdog", "hoth", "hot")), "" + "hot\n" + " -> 2\n" + " d\n" + " og -> 0\n" + " h -> 1\n"); } @Test public void realisticOptionsTrie() { // These are the fields of OkHttpClient in 3.10. Options options = utf8Options( "dispatcher", "proxy", "protocols", "connectionSpecs", "interceptors", "networkInterceptors", "eventListenerFactory", "proxySelector", // No index 7 in the trie because 'proxy' is a prefix! "cookieJar", "cache", "internalCache", "socketFactory", "sslSocketFactory", "certificateChainCleaner", "hostnameVerifier", "certificatePinner", "proxyAuthenticator", // No index 16 in the trie because 'proxy' is a prefix! "authenticator", "connectionPool", "dns", "followSslRedirects", "followRedirects", "retryOnConnectionFailure", "connectTimeout", "readTimeout", "writeTimeout", "pingInterval"); assertEquals(trieString(options), "" + "a\n" + " uthenticator -> 17\n" + "c\n" + " a\n" + " che -> 9\n" + " e\n" + " rtificate\n" + " C\n" + " hainCleaner -> 13\n" + " P\n" + " inner -> 15\n" + " o\n" + " n\n" + " nect\n" + " T\n" + " imeout -> 23\n" + " i\n" + " on\n" + " P\n" + " ool -> 18\n" + " S\n" + " pecs -> 3\n" + " o\n" + " kieJar -> 8\n" + "d\n" + " i\n" + " spatcher -> 0\n" + " n\n" + " s -> 19\n" + "e\n" + " ventListenerFactory -> 6\n" + "f\n" + " ollow\n" + " R\n" + " edirects -> 21\n" + " S\n" + " slRedirects -> 20\n" + "h\n" + " ostnameVerifier -> 14\n" + "i\n" + " nter\n" + " c\n" + " eptors -> 4\n" + " n\n" + " alCache -> 10\n" + "n\n" + " etworkInterceptors -> 5\n" + "p\n" + " i\n" + " ngInterval -> 26\n" + " r\n" + " o\n" + " t\n" + " ocols -> 2\n" + " x\n" + " y -> 1\n" + "r\n" + " e\n" + " a\n" + " dTimeout -> 24\n" + " t\n" + " ryOnConnectionFailure -> 22\n" + "s\n" + " o\n" + " cketFactory -> 11\n" + " s\n" + " lSocketFactory -> 12\n" + "w\n" + " riteTimeout -> 25\n"); assertSelect("", -1, options); assertSelect("a", -1, options); assertSelect("eventListenerFactor", -1, options); assertSelect("dnst", 19, options); assertSelect("proxyproxy", 1, options); assertSelect("prox", -1, options); assertSelect("dispatcher", 0, options); assertSelect("proxy", 1, options); assertSelect("protocols", 2, options); assertSelect("connectionSpecs", 3, options); assertSelect("interceptors", 4, options); assertSelect("networkInterceptors", 5, options); assertSelect("eventListenerFactory", 6, options); assertSelect("proxySelector", 1, options); // 'proxy' is a prefix. assertSelect("cookieJar", 8, options); assertSelect("cache", 9, options); assertSelect("internalCache", 10, options); assertSelect("socketFactory", 11, options); assertSelect("sslSocketFactory", 12, options); assertSelect("certificateChainCleaner", 13, options); assertSelect("hostnameVerifier", 14, options); assertSelect("certificatePinner", 15, options); assertSelect("proxyAuthenticator", 1, options); // 'proxy' is a prefix. assertSelect("authenticator", 17, options); assertSelect("connectionPool", 18, options); assertSelect("dns", 19, options); assertSelect("followSslRedirects", 20, options); assertSelect("followRedirects", 21, options); assertSelect("retryOnConnectionFailure", 22, options); assertSelect("connectTimeout", 23, options); assertSelect("readTimeout", 24, options); assertSelect("writeTimeout", 25, options); assertSelect("pingInterval", 26, options); } @Test public void emptyOptions() { Options options = utf8Options(); assertSelect("", -1, options); assertSelect("a", -1, options); assertSelect("abc", -1, options); } @Test public void emptyStringInOptionsTrie() { try { utf8Options(""); fail(); } catch (IllegalArgumentException expected) { } try { utf8Options("abc", ""); fail(); } catch (IllegalArgumentException expected) { } } @Test public void multipleIdenticalValues() { try { utf8Options("abc", "abc"); fail(); } catch (IllegalArgumentException expected) { assertEquals(expected.getMessage(), "duplicate option: [text=abc]"); } } @Test public void prefixesAreStripped() { Options options = utf8Options("abcA", "abc", "abcB"); assertEquals(trieString(options), "" + "abc\n" + " -> 1\n" + " A -> 0\n"); assertSelect("abc", 1, options); assertSelect("abcA", 0, options); assertSelect("abcB", 1, options); assertSelect("abcC", 1, options); assertSelect("ab", -1, options); } @Test public void multiplePrefixesAreStripped() { assertEquals(trieString(utf8Options("a", "ab", "abc", "abcd", "abcde")), "" + "a -> 0\n"); assertEquals(trieString(utf8Options("abc", "a", "ab", "abe", "abcd", "abcf")), "" + "a\n" + " -> 1\n" + " bc -> 0\n"); assertEquals(trieString(utf8Options("abc", "ab", "a")), "" + "a\n" + " -> 2\n" + " b\n" + " -> 1\n" + " c -> 0\n"); assertEquals(trieString(utf8Options("abcd", "abce", "abc", "abcf", "abcg")), "" + "abc\n" + " -> 2\n" + " d -> 0\n" + " e -> 1\n"); } private Options utf8Options(String... options) { ByteString[] byteStrings = new ByteString[options.length]; for (int i = 0; i < options.length; i++) { byteStrings[i] = ByteString.encodeUtf8(options[i]); } return Options.of(byteStrings); } private void assertSelect(String data, int expected, Options options) { Buffer buffer = new Buffer().writeUtf8(data); long dataSize = buffer.size; int actual = buffer.select(options); assertEquals(actual, expected); if (expected == -1) { assertEquals(buffer.size, dataSize); } else { assertEquals(buffer.size + options.get(expected).size(), dataSize); } } private void assertSelect(String data, int expected, String... options) { assertSelect(data, expected, utf8Options(options)); } private String trieString(Options options) { StringBuilder result = new StringBuilder(); printTrieNode(result, options, 0, ""); return result.toString(); } private void printTrieNode(StringBuilder out, Options options, int offset, String indent) { if (options.trie[offset + 1] != -1) { // Print the prefix. out.append(indent + "-> " + options.trie[offset + 1] + "\n"); } if (options.trie[offset] > 0) { // Print the select. int selectChoiceCount = options.trie[offset]; for (int i = 0; i < selectChoiceCount; i++) { out.append(indent + (char) options.trie[offset + 2 + i]); printTrieResult(out, options, options.trie[offset + 2 + selectChoiceCount + i], indent + " "); } } else { // Print the scan. int scanByteCount = -1 * options.trie[offset]; out.append(indent); for (int i = 0; i < scanByteCount; i++) { out.append((char) options.trie[offset + 2 + i]); } printTrieResult(out, options, options.trie[offset + 2 + scanByteCount], indent + TestUtil.repeat(' ', scanByteCount)); } } private void printTrieResult(StringBuilder out, Options options, int result, String indent) { if (result >= 0) { out.append(" -> " + result + "\n"); } else { out.append("\n"); printTrieNode(out, options, -1 * result, indent); } } } okio-okio-parent-1.16.0/okio/src/test/java/okio/PipeTest.java000066400000000000000000000301431335667376100240010ustar00rootroot00000000000000/* * Copyright (C) 2016 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; import java.io.InterruptedIOException; import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.junit.After; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public final class PipeTest { final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2); @After public void tearDown() throws Exception { executorService.shutdown(); } @Test public void test() throws Exception { Pipe pipe = new Pipe(6); pipe.sink().write(new Buffer().writeUtf8("abc"), 3L); Source source = pipe.source(); Buffer readBuffer = new Buffer(); assertEquals(3L, source.read(readBuffer, 6L)); assertEquals("abc", readBuffer.readUtf8()); pipe.sink().close(); assertEquals(-1L, source.read(readBuffer, 6L)); source.close(); } /** * A producer writes the first 16 MiB of bytes generated by {@code new Random(0)} to a sink, and a * consumer consumes them. Both compute hashes of their data to confirm that they're as expected. */ @Test public void largeDataset() throws Exception { final Pipe pipe = new Pipe(1000L); // An awkward size to force producer/consumer exchange. final long totalBytes = 16L * 1024L * 1024L; ByteString expectedHash = ByteString.decodeHex("7c3b224bea749086babe079360cf29f98d88262d"); // Write data to the sink. Future sinkHash = executorService.submit(new Callable() { @Override public ByteString call() throws Exception { HashingSink hashingSink = HashingSink.sha1(pipe.sink()); Random random = new Random(0); byte[] data = new byte[8192]; Buffer buffer = new Buffer(); for (long i = 0L; i < totalBytes; i += data.length) { random.nextBytes(data); buffer.write(data); hashingSink.write(buffer, buffer.size()); } hashingSink.close(); return hashingSink.hash(); } }); // Read data from the source. Future sourceHash = executorService.submit(new Callable() { @Override public ByteString call() throws Exception { Buffer blackhole = new Buffer(); HashingSink hashingSink = HashingSink.sha1(blackhole); Buffer buffer = new Buffer(); while (pipe.source().read(buffer, Long.MAX_VALUE) != -1) { hashingSink.write(buffer, buffer.size()); blackhole.clear(); } pipe.source().close(); return hashingSink.hash(); } }); assertEquals(expectedHash, sinkHash.get()); assertEquals(expectedHash, sourceHash.get()); } @Test public void sinkTimeout() throws Exception { Pipe pipe = new Pipe(3); pipe.sink().timeout().timeout(1000, TimeUnit.MILLISECONDS); pipe.sink().write(new Buffer().writeUtf8("abc"), 3L); double start = now(); try { pipe.sink().write(new Buffer().writeUtf8("def"), 3L); fail(); } catch (InterruptedIOException expected) { assertEquals("timeout", expected.getMessage()); } assertElapsed(1000.0, start); Buffer readBuffer = new Buffer(); assertEquals(3L, pipe.source().read(readBuffer, 6L)); assertEquals("abc", readBuffer.readUtf8()); } @Test public void sourceTimeout() throws Exception { Pipe pipe = new Pipe(3L); pipe.source().timeout().timeout(1000, TimeUnit.MILLISECONDS); double start = now(); Buffer readBuffer = new Buffer(); try { pipe.source().read(readBuffer, 6L); fail(); } catch (InterruptedIOException expected) { assertEquals("timeout", expected.getMessage()); } assertElapsed(1000.0, start); assertEquals(0, readBuffer.size()); } /** * The writer is writing 12 bytes as fast as it can to a 3 byte buffer. The reader alternates * sleeping 1000 ms, then reading 3 bytes. That should make for an approximate timeline like * this: * * 0: writer writes 'abc', blocks 0: reader sleeps until 1000 * 1000: reader reads 'abc', sleeps until 2000 * 1000: writer writes 'def', blocks * 2000: reader reads 'def', sleeps until 3000 * 2000: writer writes 'ghi', blocks * 3000: reader reads 'ghi', sleeps until 4000 * 3000: writer writes 'jkl', returns * 4000: reader reads 'jkl', returns * * Because the writer is writing to a buffer, it finishes before the reader does. */ @Test public void sinkBlocksOnSlowReader() throws Exception { final Pipe pipe = new Pipe(3L); executorService.execute(new Runnable() { @Override public void run() { try { Buffer buffer = new Buffer(); Thread.sleep(1000L); assertEquals(3, pipe.source().read(buffer, Long.MAX_VALUE)); assertEquals("abc", buffer.readUtf8()); Thread.sleep(1000L); assertEquals(3, pipe.source().read(buffer, Long.MAX_VALUE)); assertEquals("def", buffer.readUtf8()); Thread.sleep(1000L); assertEquals(3, pipe.source().read(buffer, Long.MAX_VALUE)); assertEquals("ghi", buffer.readUtf8()); Thread.sleep(1000L); assertEquals(3, pipe.source().read(buffer, Long.MAX_VALUE)); assertEquals("jkl", buffer.readUtf8()); } catch (IOException | InterruptedException e) { throw new AssertionError(); } } }); double start = now(); pipe.sink().write(new Buffer().writeUtf8("abcdefghijkl"), 12); assertElapsed(3000.0, start); } @Test public void sinkWriteFailsByClosedReader() throws Exception { final Pipe pipe = new Pipe(3L); executorService.schedule(new Runnable() { @Override public void run() { try { pipe.source().close(); } catch (IOException e) { throw new AssertionError(); } } }, 1000, TimeUnit.MILLISECONDS); double start = now(); try { pipe.sink().write(new Buffer().writeUtf8("abcdef"), 6); fail(); } catch (IOException expected) { assertEquals("source is closed", expected.getMessage()); assertElapsed(1000.0, start); } } @Test public void sinkFlushDoesntWaitForReader() throws Exception { Pipe pipe = new Pipe(100L); pipe.sink().write(new Buffer().writeUtf8("abc"), 3); pipe.sink().flush(); BufferedSource bufferedSource = Okio.buffer(pipe.source()); assertEquals("abc", bufferedSource.readUtf8(3)); } @Test public void sinkFlushFailsIfReaderIsClosedBeforeAllDataIsRead() throws Exception { Pipe pipe = new Pipe(100L); pipe.sink().write(new Buffer().writeUtf8("abc"), 3); pipe.source().close(); try { pipe.sink().flush(); fail(); } catch (IOException expected) { assertEquals("source is closed", expected.getMessage()); } } @Test public void sinkCloseFailsIfReaderIsClosedBeforeAllDataIsRead() throws Exception { Pipe pipe = new Pipe(100L); pipe.sink().write(new Buffer().writeUtf8("abc"), 3); pipe.source().close(); try { pipe.sink().close(); fail(); } catch (IOException expected) { assertEquals("source is closed", expected.getMessage()); } } @Test public void sinkClose() throws Exception { Pipe pipe = new Pipe(100L); pipe.sink().close(); try { pipe.sink().write(new Buffer().writeUtf8("abc"), 3); fail(); } catch (IllegalStateException expected) { assertEquals("closed", expected.getMessage()); } try { pipe.sink().flush(); fail(); } catch (IllegalStateException expected) { assertEquals("closed", expected.getMessage()); } } @Test public void sinkMultipleClose() throws Exception { Pipe pipe = new Pipe(100L); pipe.sink().close(); pipe.sink().close(); } @Test public void sinkCloseDoesntWaitForSourceRead() throws Exception { Pipe pipe = new Pipe(100L); pipe.sink().write(new Buffer().writeUtf8("abc"), 3); pipe.sink().close(); BufferedSource bufferedSource = Okio.buffer(pipe.source()); assertEquals("abc", bufferedSource.readUtf8()); assertTrue(bufferedSource.exhausted()); } @Test public void sourceClose() throws Exception { Pipe pipe = new Pipe(100L); pipe.source().close(); try { pipe.source().read(new Buffer(), 3); fail(); } catch (IllegalStateException expected) { assertEquals("closed", expected.getMessage()); } } @Test public void sourceMultipleClose() throws Exception { Pipe pipe = new Pipe(100L); pipe.source().close(); pipe.source().close(); } @Test public void sourceReadUnblockedByClosedSink() throws Exception { final Pipe pipe = new Pipe(3L); executorService.schedule(new Runnable() { @Override public void run() { try { pipe.sink().close(); } catch (IOException e) { throw new AssertionError(); } } }, 1000, TimeUnit.MILLISECONDS); double start = now(); Buffer readBuffer = new Buffer(); assertEquals(-1, pipe.source().read(readBuffer, Long.MAX_VALUE)); assertEquals(0, readBuffer.size()); assertElapsed(1000.0, start); } /** * The writer has 12 bytes to write. It alternates sleeping 1000 ms, then writing 3 bytes. The * reader is reading as fast as it can. That should make for an approximate timeline like this: * * 0: writer sleeps until 1000 * 0: reader blocks * 1000: writer writes 'abc', sleeps until 2000 * 1000: reader reads 'abc' * 2000: writer writes 'def', sleeps until 3000 * 2000: reader reads 'def' * 3000: writer writes 'ghi', sleeps until 4000 * 3000: reader reads 'ghi' * 4000: writer writes 'jkl', returns * 4000: reader reads 'jkl', returns */ @Test public void sourceBlocksOnSlowWriter() throws Exception { final Pipe pipe = new Pipe(100L); executorService.execute(new Runnable() { @Override public void run() { try { Thread.sleep(1000L); pipe.sink().write(new Buffer().writeUtf8("abc"), 3); Thread.sleep(1000L); pipe.sink().write(new Buffer().writeUtf8("def"), 3); Thread.sleep(1000L); pipe.sink().write(new Buffer().writeUtf8("ghi"), 3); Thread.sleep(1000L); pipe.sink().write(new Buffer().writeUtf8("jkl"), 3); } catch (IOException | InterruptedException e) { throw new AssertionError(); } } }); double start = now(); Buffer readBuffer = new Buffer(); assertEquals(3, pipe.source().read(readBuffer, Long.MAX_VALUE)); assertEquals("abc", readBuffer.readUtf8()); assertElapsed(1000.0, start); assertEquals(3, pipe.source().read(readBuffer, Long.MAX_VALUE)); assertEquals("def", readBuffer.readUtf8()); assertElapsed(2000.0, start); assertEquals(3, pipe.source().read(readBuffer, Long.MAX_VALUE)); assertEquals("ghi", readBuffer.readUtf8()); assertElapsed(3000.0, start); assertEquals(3, pipe.source().read(readBuffer, Long.MAX_VALUE)); assertEquals("jkl", readBuffer.readUtf8()); assertElapsed(4000.0, start); } /** Returns the nanotime in milliseconds as a double for measuring timeouts. */ private double now() { return System.nanoTime() / 1000000.0d; } /** * Fails the test unless the time from start until now is duration, accepting differences in * -50..+450 milliseconds. */ private void assertElapsed(double duration, double start) { assertEquals(duration, now() - start - 200d, 250.0); } } okio-okio-parent-1.16.0/okio/src/test/java/okio/ReadUtf8LineTest.java000066400000000000000000000152771335667376100253510ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.EOFException; import java.io.IOException; import java.util.Arrays; import java.util.List; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @RunWith(Parameterized.class) public final class ReadUtf8LineTest { private interface Factory { BufferedSource create(Buffer data); } @Parameterized.Parameters(name = "{0}") public static List parameters() { return Arrays.asList( new Object[] { new Factory() { @Override public BufferedSource create(Buffer data) { return data; } @Override public String toString() { return "Buffer"; } }}, new Object[] { new Factory() { @Override public BufferedSource create(Buffer data) { return new RealBufferedSource(data); } @Override public String toString() { return "RealBufferedSource"; } }}, new Object[] { new Factory() { @Override public BufferedSource create(Buffer data) { return new RealBufferedSource(new ForwardingSource(data) { @Override public long read(Buffer sink, long byteCount) throws IOException { return super.read(sink, Math.min(1, byteCount)); } }); } @Override public String toString() { return "Slow RealBufferedSource"; } }} ); } @Parameterized.Parameter public Factory factory; private Buffer data; private BufferedSource source; @Before public void setUp() { data = new Buffer(); source = factory.create(data); } @Test public void readLines() throws IOException { data.writeUtf8("abc\ndef\n"); assertEquals("abc", source.readUtf8LineStrict()); assertEquals("def", source.readUtf8LineStrict()); try { source.readUtf8LineStrict(); fail(); } catch (EOFException expected) { assertEquals("\\n not found: limit=0 content=…", expected.getMessage()); } } @Test public void readUtf8LineStrictWithLimits() throws IOException { int[] lens = {1, Segment.SIZE - 2, Segment.SIZE - 1, Segment.SIZE, Segment.SIZE * 10}; for (int len : lens) { data.writeUtf8(TestUtil.repeat('a', len)).writeUtf8("\n"); assertEquals(len, source.readUtf8LineStrict(len).length()); source.readUtf8(); data.writeUtf8(TestUtil.repeat('a', len)).writeUtf8("\n").writeUtf8(TestUtil.repeat('a', len)); assertEquals(len, source.readUtf8LineStrict(len).length()); source.readUtf8(); data.writeUtf8(TestUtil.repeat('a', len)).writeUtf8("\r\n"); assertEquals(len, source.readUtf8LineStrict(len).length()); source.readUtf8(); data.writeUtf8(TestUtil.repeat('a', len)).writeUtf8("\r\n").writeUtf8(TestUtil.repeat('a', len)); assertEquals(len, source.readUtf8LineStrict(len).length()); source.readUtf8(); } } @Test public void readUtf8LineStrictNoBytesConsumedOnFailure() throws IOException { data.writeUtf8("abc\n"); try { source.readUtf8LineStrict(2); fail(); } catch (EOFException expected) { assertTrue(expected.getMessage().startsWith("\\n not found: limit=2 content=61626")); } assertEquals("abc", source.readUtf8LineStrict(3)); } @Test public void readUtf8LineStrictEmptyString() throws IOException { data.writeUtf8("\r\nabc"); assertEquals("", source.readUtf8LineStrict(0)); assertEquals("abc", source.readUtf8()); } @Test public void readUtf8LineStrictNonPositive() throws IOException { data.writeUtf8("\r\n"); try { source.readUtf8LineStrict(-1); fail("Expected failure: limit must be greater than 0"); } catch (IllegalArgumentException expected) { } } @Test public void eofExceptionProvidesLimitedContent() throws IOException { data.writeUtf8("aaaaaaaabbbbbbbbccccccccdddddddde"); try { source.readUtf8LineStrict(); fail(); } catch (EOFException expected) { assertEquals("\\n not found: limit=33 content=616161616161616162626262626262626363636363636363" + "6464646464646464…", expected.getMessage()); } } @Test public void newlineAtEnd() throws IOException { data.writeUtf8("abc\n"); assertEquals("abc", source.readUtf8LineStrict(3)); assertTrue(source.exhausted()); data.writeUtf8("abc\r\n"); assertEquals("abc", source.readUtf8LineStrict(3)); assertTrue(source.exhausted()); data.writeUtf8("abc\r"); try { source.readUtf8LineStrict(3); fail(); } catch (EOFException expected) { assertEquals("\\n not found: limit=3 content=6162630d…", expected.getMessage()); } source.readUtf8(); data.writeUtf8("abc"); try { source.readUtf8LineStrict(3); fail(); } catch (EOFException expected) { assertEquals("\\n not found: limit=3 content=616263…", expected.getMessage()); } } @Test public void emptyLines() throws IOException { data.writeUtf8("\n\n\n"); assertEquals("", source.readUtf8LineStrict()); assertEquals("", source.readUtf8LineStrict()); assertEquals("", source.readUtf8LineStrict()); assertTrue(source.exhausted()); } @Test public void crDroppedPrecedingLf() throws IOException { data.writeUtf8("abc\r\ndef\r\nghi\rjkl\r\n"); assertEquals("abc", source.readUtf8LineStrict()); assertEquals("def", source.readUtf8LineStrict()); assertEquals("ghi\rjkl", source.readUtf8LineStrict()); } @Test public void bufferedReaderCompatible() throws IOException { data.writeUtf8("abc\ndef"); assertEquals("abc", source.readUtf8Line()); assertEquals("def", source.readUtf8Line()); assertEquals(null, source.readUtf8Line()); } @Test public void bufferedReaderCompatibleWithTrailingNewline() throws IOException { data.writeUtf8("abc\ndef\n"); assertEquals("abc", source.readUtf8Line()); assertEquals("def", source.readUtf8Line()); assertEquals(null, source.readUtf8Line()); } } okio-okio-parent-1.16.0/okio/src/test/java/okio/RealBufferedSinkTest.java000066400000000000000000000172651335667376100262710ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; import java.io.OutputStream; import org.junit.Test; import static okio.TestUtil.repeat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; /** * Tests solely for the behavior of RealBufferedSink's implementation. For generic * BufferedSink behavior use BufferedSinkTest. */ public final class RealBufferedSinkTest { @Test public void inputStreamCloses() throws Exception { RealBufferedSink sink = new RealBufferedSink(new Buffer()); OutputStream out = sink.outputStream(); out.close(); try { sink.writeUtf8("Hi!"); fail(); } catch (IllegalStateException e) { assertEquals("closed", e.getMessage()); } } @Test public void bufferedSinkEmitsTailWhenItIsComplete() throws IOException { Buffer sink = new Buffer(); BufferedSink bufferedSink = new RealBufferedSink(sink); bufferedSink.writeUtf8(repeat('a', Segment.SIZE - 1)); assertEquals(0, sink.size()); bufferedSink.writeByte(0); assertEquals(Segment.SIZE, sink.size()); assertEquals(0, bufferedSink.buffer().size()); } @Test public void bufferedSinkEmitMultipleSegments() throws IOException { Buffer sink = new Buffer(); BufferedSink bufferedSink = new RealBufferedSink(sink); bufferedSink.writeUtf8(repeat('a', Segment.SIZE * 4 - 1)); assertEquals(Segment.SIZE * 3, sink.size()); assertEquals(Segment.SIZE - 1, bufferedSink.buffer().size()); } @Test public void bufferedSinkFlush() throws IOException { Buffer sink = new Buffer(); BufferedSink bufferedSink = new RealBufferedSink(sink); bufferedSink.writeByte('a'); assertEquals(0, sink.size()); bufferedSink.flush(); assertEquals(0, bufferedSink.buffer().size()); assertEquals(1, sink.size()); } @Test public void bytesEmittedToSinkWithFlush() throws Exception { Buffer sink = new Buffer(); BufferedSink bufferedSink = new RealBufferedSink(sink); bufferedSink.writeUtf8("abc"); bufferedSink.flush(); assertEquals(3, sink.size()); } @Test public void bytesNotEmittedToSinkWithoutFlush() throws Exception { Buffer sink = new Buffer(); BufferedSink bufferedSink = new RealBufferedSink(sink); bufferedSink.writeUtf8("abc"); assertEquals(0, sink.size()); } @Test public void bytesEmittedToSinkWithEmit() throws Exception { Buffer sink = new Buffer(); BufferedSink bufferedSink = new RealBufferedSink(sink); bufferedSink.writeUtf8("abc"); bufferedSink.emit(); assertEquals(3, sink.size()); } @Test public void completeSegmentsEmitted() throws Exception { Buffer sink = new Buffer(); BufferedSink bufferedSink = new RealBufferedSink(sink); bufferedSink.writeUtf8(repeat('a', Segment.SIZE * 3)); assertEquals(Segment.SIZE * 3, sink.size()); } @Test public void incompleteSegmentsNotEmitted() throws Exception { Buffer sink = new Buffer(); BufferedSink bufferedSink = new RealBufferedSink(sink); bufferedSink.writeUtf8(repeat('a', Segment.SIZE * 3 - 1)); assertEquals(Segment.SIZE * 2, sink.size()); } @Test public void closeWithExceptionWhenWriting() throws IOException { MockSink mockSink = new MockSink(); mockSink.scheduleThrow(0, new IOException()); BufferedSink bufferedSink = new RealBufferedSink(mockSink); bufferedSink.writeByte('a'); try { bufferedSink.close(); fail(); } catch (IOException expected) { } mockSink.assertLog("write([text=a], 1)", "close()"); } @Test public void closeWithExceptionWhenClosing() throws IOException { MockSink mockSink = new MockSink(); mockSink.scheduleThrow(1, new IOException()); BufferedSink bufferedSink = new RealBufferedSink(mockSink); bufferedSink.writeByte('a'); try { bufferedSink.close(); fail(); } catch (IOException expected) { } mockSink.assertLog("write([text=a], 1)", "close()"); } @Test public void closeWithExceptionWhenWritingAndClosing() throws IOException { MockSink mockSink = new MockSink(); mockSink.scheduleThrow(0, new IOException("first")); mockSink.scheduleThrow(1, new IOException("second")); BufferedSink bufferedSink = new RealBufferedSink(mockSink); bufferedSink.writeByte('a'); try { bufferedSink.close(); fail(); } catch (IOException expected) { assertEquals("first", expected.getMessage()); } mockSink.assertLog("write([text=a], 1)", "close()"); } @Test public void operationsAfterClose() throws IOException { MockSink mockSink = new MockSink(); BufferedSink bufferedSink = new RealBufferedSink(mockSink); bufferedSink.writeByte('a'); bufferedSink.close(); // Test a sample set of methods. try { bufferedSink.writeByte('a'); fail(); } catch (IllegalStateException expected) { } try { bufferedSink.write(new byte[10]); fail(); } catch (IllegalStateException expected) { } try { bufferedSink.emitCompleteSegments(); fail(); } catch (IllegalStateException expected) { } try { bufferedSink.emit(); fail(); } catch (IllegalStateException expected) { } try { bufferedSink.flush(); fail(); } catch (IllegalStateException expected) { } // Test a sample set of methods on the OutputStream. OutputStream os = bufferedSink.outputStream(); try { os.write('a'); fail(); } catch (IOException expected) { } try { os.write(new byte[10]); fail(); } catch (IOException expected) { } // Permitted os.flush(); } @Test public void writeAll() throws IOException { MockSink mockSink = new MockSink(); BufferedSink bufferedSink = Okio.buffer(mockSink); bufferedSink.buffer().writeUtf8("abc"); assertEquals(3, bufferedSink.writeAll(new Buffer().writeUtf8("def"))); assertEquals(6, bufferedSink.buffer().size()); assertEquals("abcdef", bufferedSink.buffer().readUtf8(6)); mockSink.assertLog(); // No writes. } @Test public void writeAllExhausted() throws IOException { MockSink mockSink = new MockSink(); BufferedSink bufferedSink = Okio.buffer(mockSink); assertEquals(0, bufferedSink.writeAll(new Buffer())); assertEquals(0, bufferedSink.buffer().size()); mockSink.assertLog(); // No writes. } @Test public void writeAllWritesOneSegmentAtATime() throws IOException { Buffer write1 = new Buffer().writeUtf8(TestUtil.repeat('a', Segment.SIZE)); Buffer write2 = new Buffer().writeUtf8(TestUtil.repeat('b', Segment.SIZE)); Buffer write3 = new Buffer().writeUtf8(TestUtil.repeat('c', Segment.SIZE)); Buffer source = new Buffer().writeUtf8("" + TestUtil.repeat('a', Segment.SIZE) + TestUtil.repeat('b', Segment.SIZE) + TestUtil.repeat('c', Segment.SIZE)); MockSink mockSink = new MockSink(); BufferedSink bufferedSink = Okio.buffer(mockSink); assertEquals(Segment.SIZE * 3, bufferedSink.writeAll(source)); mockSink.assertLog( "write(" + write1 + ", " + write1.size() + ")", "write(" + write2 + ", " + write2.size() + ")", "write(" + write3 + ", " + write3.size() + ")"); } } okio-okio-parent-1.16.0/okio/src/test/java/okio/RealBufferedSourceTest.java000066400000000000000000000163561335667376100266250ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import org.junit.Test; import static okio.TestUtil.repeat; import static okio.Util.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; /** * Tests solely for the behavior of RealBufferedSource's implementation. For generic * BufferedSource behavior use BufferedSourceTest. */ public final class RealBufferedSourceTest { @Test public void inputStreamTracksSegments() throws Exception { Buffer source = new Buffer(); source.writeUtf8("a"); source.writeUtf8(repeat('b', Segment.SIZE)); source.writeUtf8("c"); InputStream in = new RealBufferedSource(source).inputStream(); assertEquals(0, in.available()); assertEquals(Segment.SIZE + 2, source.size()); // Reading one byte buffers a full segment. assertEquals('a', in.read()); assertEquals(Segment.SIZE - 1, in.available()); assertEquals(2, source.size()); // Reading as much as possible reads the rest of that buffered segment. byte[] data = new byte[Segment.SIZE * 2]; assertEquals(Segment.SIZE - 1, in.read(data, 0, data.length)); assertEquals(repeat('b', Segment.SIZE - 1), new String(data, 0, Segment.SIZE - 1, UTF_8)); assertEquals(2, source.size()); // Continuing to read buffers the next segment. assertEquals('b', in.read()); assertEquals(1, in.available()); assertEquals(0, source.size()); // Continuing to read reads from the buffer. assertEquals('c', in.read()); assertEquals(0, in.available()); assertEquals(0, source.size()); // Once we've exhausted the source, we're done. assertEquals(-1, in.read()); assertEquals(0, source.size()); } @Test public void inputStreamCloses() throws Exception { RealBufferedSource source = new RealBufferedSource(new Buffer()); InputStream in = source.inputStream(); in.close(); try { source.require(1); fail(); } catch (IllegalStateException e) { assertEquals("closed", e.getMessage()); } } @Test public void indexOfStopsReadingAtLimit() throws Exception { Buffer buffer = new Buffer().writeUtf8("abcdef"); BufferedSource bufferedSource = new RealBufferedSource(new ForwardingSource(buffer) { @Override public long read(Buffer sink, long byteCount) throws IOException { return super.read(sink, Math.min(1, byteCount)); } }); assertEquals(6, buffer.size()); assertEquals(-1, bufferedSource.indexOf((byte) 'e', 0, 4)); assertEquals(2, buffer.size()); } @Test public void requireTracksBufferFirst() throws Exception { Buffer source = new Buffer(); source.writeUtf8("bb"); BufferedSource bufferedSource = new RealBufferedSource(source); bufferedSource.buffer().writeUtf8("aa"); bufferedSource.require(2); assertEquals(2, bufferedSource.buffer().size()); assertEquals(2, source.size()); } @Test public void requireIncludesBufferBytes() throws Exception { Buffer source = new Buffer(); source.writeUtf8("b"); BufferedSource bufferedSource = new RealBufferedSource(source); bufferedSource.buffer().writeUtf8("a"); bufferedSource.require(2); assertEquals("ab", bufferedSource.buffer().readUtf8(2)); } @Test public void requireInsufficientData() throws Exception { Buffer source = new Buffer(); source.writeUtf8("a"); BufferedSource bufferedSource = new RealBufferedSource(source); try { bufferedSource.require(2); fail(); } catch (EOFException expected) { } } @Test public void requireReadsOneSegmentAtATime() throws Exception { Buffer source = new Buffer(); source.writeUtf8(repeat('a', Segment.SIZE)); source.writeUtf8(repeat('b', Segment.SIZE)); BufferedSource bufferedSource = new RealBufferedSource(source); bufferedSource.require(2); assertEquals(Segment.SIZE, source.size()); assertEquals(Segment.SIZE, bufferedSource.buffer().size()); } @Test public void skipReadsOneSegmentAtATime() throws Exception { Buffer source = new Buffer(); source.writeUtf8(repeat('a', Segment.SIZE)); source.writeUtf8(repeat('b', Segment.SIZE)); BufferedSource bufferedSource = new RealBufferedSource(source); bufferedSource.skip(2); assertEquals(Segment.SIZE, source.size()); assertEquals(Segment.SIZE - 2, bufferedSource.buffer().size()); } @Test public void skipTracksBufferFirst() throws Exception { Buffer source = new Buffer(); source.writeUtf8("bb"); BufferedSource bufferedSource = new RealBufferedSource(source); bufferedSource.buffer().writeUtf8("aa"); bufferedSource.skip(2); assertEquals(0, bufferedSource.buffer().size()); assertEquals(2, source.size()); } @Test public void operationsAfterClose() throws IOException { Buffer source = new Buffer(); BufferedSource bufferedSource = new RealBufferedSource(source); bufferedSource.close(); // Test a sample set of methods. try { bufferedSource.indexOf((byte) 1); fail(); } catch (IllegalStateException expected) { } try { bufferedSource.skip(1); fail(); } catch (IllegalStateException expected) { } try { bufferedSource.readByte(); fail(); } catch (IllegalStateException expected) { } try { bufferedSource.readByteString(10); fail(); } catch (IllegalStateException expected) { } // Test a sample set of methods on the InputStream. InputStream is = bufferedSource.inputStream(); try { is.read(); fail(); } catch (IOException expected) { } try { is.read(new byte[10]); fail(); } catch (IOException expected) { } } /** * We don't want readAll to buffer an unbounded amount of data. Instead it * should buffer a segment, write it, and repeat. */ @Test public void readAllReadsOneSegmentAtATime() throws IOException { Buffer write1 = new Buffer().writeUtf8(TestUtil.repeat('a', Segment.SIZE)); Buffer write2 = new Buffer().writeUtf8(TestUtil.repeat('b', Segment.SIZE)); Buffer write3 = new Buffer().writeUtf8(TestUtil.repeat('c', Segment.SIZE)); Buffer source = new Buffer().writeUtf8("" + TestUtil.repeat('a', Segment.SIZE) + TestUtil.repeat('b', Segment.SIZE) + TestUtil.repeat('c', Segment.SIZE)); MockSink mockSink = new MockSink(); BufferedSource bufferedSource = Okio.buffer((Source) source); assertEquals(Segment.SIZE * 3, bufferedSource.readAll(mockSink)); mockSink.assertLog( "write(" + write1 + ", " + write1.size() + ")", "write(" + write2 + ", " + write2.size() + ")", "write(" + write3 + ", " + write3.size() + ")"); } } okio-okio-parent-1.16.0/okio/src/test/java/okio/SegmentSharingTest.java000066400000000000000000000156421335667376100260310ustar00rootroot00000000000000/* * Copyright (C) 2015 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import org.junit.Test; import static okio.TestUtil.assertEquivalent; import static okio.TestUtil.bufferWithSegments; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; /** Tests behavior optimized by sharing segments between buffers and byte strings. */ public final class SegmentSharingTest { private static final String us = TestUtil.repeat('u', Segment.SIZE / 2 - 2); private static final String vs = TestUtil.repeat('v', Segment.SIZE / 2 - 1); private static final String ws = TestUtil.repeat('w', Segment.SIZE / 2); private static final String xs = TestUtil.repeat('x', Segment.SIZE / 2 + 1); private static final String ys = TestUtil.repeat('y', Segment.SIZE / 2 + 2); private static final String zs = TestUtil.repeat('z', Segment.SIZE / 2 + 3); @Test public void snapshotOfEmptyBuffer() throws Exception { ByteString snapshot = new Buffer().snapshot(); assertEquivalent(snapshot, ByteString.EMPTY); } @Test public void snapshotsAreEquivalent() throws Exception { ByteString byteString = bufferWithSegments(xs, ys, zs).snapshot(); assertEquivalent(byteString, bufferWithSegments(xs, ys + zs).snapshot()); assertEquivalent(byteString, bufferWithSegments(xs + ys + zs).snapshot()); assertEquivalent(byteString, ByteString.encodeUtf8(xs + ys + zs)); } @Test public void snapshotGetByte() throws Exception { ByteString byteString = bufferWithSegments(xs, ys, zs).snapshot(); assertEquals('x', byteString.getByte(0)); assertEquals('x', byteString.getByte(xs.length() - 1)); assertEquals('y', byteString.getByte(xs.length())); assertEquals('y', byteString.getByte(xs.length() + ys.length() - 1)); assertEquals('z', byteString.getByte(xs.length() + ys.length())); assertEquals('z', byteString.getByte(xs.length() + ys.length() + zs.length() - 1)); try { byteString.getByte(-1); fail(); } catch (IndexOutOfBoundsException expected) { } try { byteString.getByte(xs.length() + ys.length() + zs.length()); fail(); } catch (IndexOutOfBoundsException expected) { } } @Test public void snapshotWriteToOutputStream() throws Exception { ByteString byteString = bufferWithSegments(xs, ys, zs).snapshot(); Buffer out = new Buffer(); byteString.write(out.outputStream()); assertEquals(xs + ys + zs, out.readUtf8()); } /** * Snapshots share their backing byte arrays with the source buffers. Those byte arrays must not * be recycled, otherwise the new writer could corrupt the segment. */ @Test public void snapshotSegmentsAreNotRecycled() throws Exception { Buffer buffer = bufferWithSegments(xs, ys, zs); ByteString snapshot = buffer.snapshot(); assertEquals(xs + ys + zs, snapshot.utf8()); // While locking the pool, confirm that clearing the buffer doesn't release its segments. synchronized (SegmentPool.class) { SegmentPool.next = null; SegmentPool.byteCount = 0L; buffer.clear(); assertEquals(null, SegmentPool.next); } } /** * Clones share their backing byte arrays with the source buffers. Those byte arrays must not * be recycled, otherwise the new writer could corrupt the segment. */ @Test public void cloneSegmentsAreNotRecycled() throws Exception { Buffer buffer = bufferWithSegments(xs, ys, zs); Buffer clone = buffer.clone(); // While locking the pool, confirm that clearing the buffer doesn't release its segments. synchronized (SegmentPool.class) { SegmentPool.next = null; SegmentPool.byteCount = 0L; buffer.clear(); assertEquals(null, SegmentPool.next); clone.clear(); assertEquals(null, SegmentPool.next); } } @Test public void snapshotJavaSerialization() throws Exception { ByteString byteString = bufferWithSegments(xs, ys, zs).snapshot(); assertEquivalent(byteString, TestUtil.reserialize(byteString)); } @Test public void clonesAreEquivalent() throws Exception { Buffer bufferA = bufferWithSegments(xs, ys, zs); Buffer bufferB = bufferA.clone(); assertEquivalent(bufferA, bufferB); assertEquivalent(bufferA, bufferWithSegments(xs + ys, zs)); } /** Even though some segments are shared, clones can be mutated independently. */ @Test public void mutateAfterClone() throws Exception { Buffer bufferA = new Buffer(); bufferA.writeUtf8("abc"); Buffer bufferB = bufferA.clone(); bufferA.writeUtf8("def"); bufferB.writeUtf8("DEF"); assertEquals("abcdef", bufferA.readUtf8()); assertEquals("abcDEF", bufferB.readUtf8()); } @Test public void concatenateSegmentsCanCombine() throws Exception { Buffer bufferA = new Buffer().writeUtf8(ys).writeUtf8(us); assertEquals(ys, bufferA.readUtf8(ys.length())); Buffer bufferB = new Buffer().writeUtf8(vs).writeUtf8(ws); Buffer bufferC = bufferA.clone(); bufferA.write(bufferB, vs.length()); bufferC.writeUtf8(xs); assertEquals(us + vs, bufferA.readUtf8()); assertEquals(ws, bufferB.readUtf8()); assertEquals(us + xs, bufferC.readUtf8()); } @Test public void shareAndSplit() throws Exception { Buffer bufferA = new Buffer().writeUtf8("xxxx"); ByteString snapshot = bufferA.snapshot(); // Share the segment. Buffer bufferB = new Buffer(); bufferB.write(bufferA, 2); // Split the shared segment in two. bufferB.writeUtf8("yy"); // Append to the first half of the shared segment. assertEquals("xxxx", snapshot.utf8()); } @Test public void appendSnapshotToEmptyBuffer() throws Exception { Buffer bufferA = bufferWithSegments(xs, ys); ByteString snapshot = bufferA.snapshot(); Buffer bufferB = new Buffer(); bufferB.write(snapshot); assertEquivalent(bufferB, bufferA); } @Test public void appendSnapshotToNonEmptyBuffer() throws Exception { Buffer bufferA = bufferWithSegments(xs, ys); ByteString snapshot = bufferA.snapshot(); Buffer bufferB = new Buffer().writeUtf8(us); bufferB.write(snapshot); assertEquivalent(bufferB, new Buffer().writeUtf8(us + xs + ys)); } @Test public void copyToSegmentSharing() throws Exception { Buffer bufferA = bufferWithSegments(ws, xs + "aaaa", ys, "bbbb" + zs); Buffer bufferB = bufferWithSegments(us); bufferA.copyTo(bufferB, ws.length() + xs.length(), 4 + ys.length() + 4); assertEquivalent(bufferB, new Buffer().writeUtf8(us + "aaaa" + ys + "bbbb")); } } okio-okio-parent-1.16.0/okio/src/test/java/okio/SocketTimeoutTest.java000066400000000000000000000112601335667376100257020ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketTimeoutException; import java.util.concurrent.TimeUnit; import org.junit.Test; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public final class SocketTimeoutTest { // The size of the socket buffers to use. Less than half the data transferred during tests to // ensure send and receive buffers are flooded and any necessary blocking behavior takes place. private static final int SOCKET_BUFFER_SIZE = 256 * 1024; private static final int ONE_MB = 1024 * 1024; @Test public void readWithoutTimeout() throws Exception { Socket socket = socket(ONE_MB, 0); BufferedSource source = Okio.buffer(Okio.source(socket)); source.timeout().timeout(5000, TimeUnit.MILLISECONDS); source.require(ONE_MB); socket.close(); } @Test public void readWithTimeout() throws Exception { Socket socket = socket(0, 0); BufferedSource source = Okio.buffer(Okio.source(socket)); source.timeout().timeout(250, TimeUnit.MILLISECONDS); try { source.require(ONE_MB); fail(); } catch (SocketTimeoutException expected) { } socket.close(); } @Test public void writeWithoutTimeout() throws Exception { Socket socket = socket(0, ONE_MB); Sink sink = Okio.buffer(Okio.sink(socket)); sink.timeout().timeout(500, TimeUnit.MILLISECONDS); byte[] data = new byte[ONE_MB]; sink.write(new Buffer().write(data), data.length); sink.flush(); socket.close(); } @Test public void writeWithTimeout() throws Exception { Socket socket = socket(0, 0); Sink sink = Okio.sink(socket); sink.timeout().timeout(500, TimeUnit.MILLISECONDS); byte[] data = new byte[ONE_MB]; long start = System.nanoTime(); try { sink.write(new Buffer().write(data), data.length); sink.flush(); fail(); } catch (SocketTimeoutException expected) { } long elapsed = System.nanoTime() - start; socket.close(); assertTrue("elapsed: " + elapsed, TimeUnit.NANOSECONDS.toMillis(elapsed) >= 500); assertTrue("elapsed: " + elapsed, TimeUnit.NANOSECONDS.toMillis(elapsed) <= 750); } /** * Returns a socket that can read {@code readableByteCount} incoming bytes and * will accept {@code writableByteCount} written bytes. The socket will idle * for 5 seconds when the required data has been read and written. */ static Socket socket(final int readableByteCount, final int writableByteCount) throws IOException { final ServerSocket serverSocket = new ServerSocket(0); serverSocket.setReuseAddress(true); serverSocket.setReceiveBufferSize(SOCKET_BUFFER_SIZE); Thread peer = new Thread("peer") { @Override public void run() { Socket socket = null; try { socket = serverSocket.accept(); socket.setSendBufferSize(SOCKET_BUFFER_SIZE); writeFully(socket.getOutputStream(), readableByteCount); readFully(socket.getInputStream(), writableByteCount); Thread.sleep(5000); // Sleep 5 seconds so the peer can close the connection. } catch (Exception ignored) { } finally { try { if (socket != null) socket.close(); } catch (IOException ignored) { } } } }; peer.start(); Socket socket = new Socket(serverSocket.getInetAddress(), serverSocket.getLocalPort()); socket.setReceiveBufferSize(SOCKET_BUFFER_SIZE); socket.setSendBufferSize(SOCKET_BUFFER_SIZE); return socket; } private static void writeFully(OutputStream out, int byteCount) throws IOException { out.write(new byte[byteCount]); out.flush(); } private static byte[] readFully(InputStream in, int byteCount) throws IOException { int count = 0; byte[] result = new byte[byteCount]; while (count < byteCount) { int read = in.read(result, count, result.length - count); if (read == -1) throw new EOFException(); count += read; } return result; } } okio-okio-parent-1.16.0/okio/src/test/java/okio/TestUtil.java000066400000000000000000000160321335667376100240220ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Arrays; import java.util.Random; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; final class TestUtil { private TestUtil() { } static void assertByteArraysEquals(byte[] a, byte[] b) { assertEquals(Arrays.toString(a), Arrays.toString(b)); } static void assertByteArrayEquals(String expectedUtf8, byte[] b) { assertEquals(expectedUtf8, new String(b, Util.UTF_8)); } static ByteString randomBytes(int length) { Random random = new Random(0); byte[] randomBytes = new byte[length]; random.nextBytes(randomBytes); return ByteString.of(randomBytes); } static Source randomSource(final long size) { return new Source() { Random random = new Random(0); long bytesLeft = size; boolean closed; @Override public long read(Buffer sink, long byteCount) throws IOException { if (closed) throw new IllegalStateException("closed"); if (bytesLeft == 0) return -1L; if (byteCount > bytesLeft) byteCount = bytesLeft; // If we can read a full segment we can save a copy. if (byteCount >= Segment.SIZE) { Segment segment = sink.writableSegment(Segment.SIZE); random.nextBytes(segment.data); segment.limit += Segment.SIZE; sink.size += Segment.SIZE; bytesLeft -= Segment.SIZE; return Segment.SIZE; } else { byte[] data = new byte[(int) byteCount]; random.nextBytes(data); sink.write(data); bytesLeft -= byteCount; return byteCount; } } @Override public Timeout timeout() { return Timeout.NONE; } @Override public void close() throws IOException { closed = true; } }; } static String repeat(char c, int count) { char[] array = new char[count]; Arrays.fill(array, c); return new String(array); } @SuppressWarnings("SelfEquals") public static void assertEquivalent(ByteString b1, ByteString b2) { // Equals. assertTrue(b1.equals(b2)); assertTrue(b1.equals(b1)); assertTrue(b2.equals(b1)); // Hash code. assertEquals(b1.hashCode(), b2.hashCode()); assertEquals(b1.hashCode(), b1.hashCode()); assertEquals(b1.toString(), b2.toString()); // Content. assertEquals(b1.size(), b2.size()); byte[] b2Bytes = b2.toByteArray(); for (int i = 0; i < b2Bytes.length; i++) { byte b = b2Bytes[i]; assertEquals(b, b1.getByte(i)); } assertByteArraysEquals(b1.toByteArray(), b2Bytes); // Doesn't equal a different byte string. assertFalse(b1.equals(null)); assertFalse(b1.equals(new Object())); if (b2Bytes.length > 0) { byte[] b3Bytes = b2Bytes.clone(); b3Bytes[b3Bytes.length - 1]++; ByteString b3 = new ByteString(b3Bytes); assertFalse(b1.equals(b3)); assertFalse(b1.hashCode() == b3.hashCode()); } else { ByteString b3 = ByteString.encodeUtf8("a"); assertFalse(b1.equals(b3)); assertFalse(b1.hashCode() == b3.hashCode()); } } @SuppressWarnings("SelfEquals") public static void assertEquivalent(Buffer b1, Buffer b2) { // Equals. assertTrue(b1.equals(b2)); assertTrue(b1.equals(b1)); assertTrue(b2.equals(b1)); // Hash code. assertEquals(b1.hashCode(), b2.hashCode()); assertEquals(b1.hashCode(), b1.hashCode()); assertEquals(b1.toString(), b2.toString()); // Content. assertEquals(b1.size(), b2.size()); Buffer buffer = new Buffer(); b2.copyTo(buffer, 0, b2.size); byte[] b2Bytes = b2.readByteArray(); for (int i = 0; i < b2Bytes.length; i++) { byte b = b2Bytes[i]; assertEquals(b, b1.getByte(i)); } // Doesn't equal a different buffer. assertFalse(b1.equals(null)); assertFalse(b1.equals(new Object())); if (b2Bytes.length > 0) { byte[] b3Bytes = b2Bytes.clone(); b3Bytes[b3Bytes.length - 1]++; Buffer b3 = new Buffer().write(b3Bytes); assertFalse(b1.equals(b3)); assertFalse(b1.hashCode() == b3.hashCode()); } else { Buffer b3 = new Buffer().writeUtf8("a"); assertFalse(b1.equals(b3)); assertFalse(b1.hashCode() == b3.hashCode()); } } /** Serializes original to bytes, then deserializes those bytes and returns the result. */ @SuppressWarnings("unchecked") // Assume serialization doesn't change types. public static T reserialize(T original) throws Exception { Buffer buffer = new Buffer(); ObjectOutputStream out = new ObjectOutputStream(buffer.outputStream()); out.writeObject(original); ObjectInputStream in = new ObjectInputStream(buffer.inputStream()); return (T) in.readObject(); } /** * Returns a new buffer containing the data in {@code data} and a segment * layout determined by {@code dice}. */ public static Buffer bufferWithRandomSegmentLayout(Random dice, byte[] data) throws IOException { Buffer result = new Buffer(); // Writing to result directly will yield packed segments. Instead, write to // other buffers, then write those buffers to result. for (int pos = 0, byteCount; pos < data.length; pos += byteCount) { byteCount = (Segment.SIZE / 2) + dice.nextInt(Segment.SIZE / 2); if (byteCount > data.length - pos) byteCount = data.length - pos; int offset = dice.nextInt(Segment.SIZE - byteCount); Buffer segment = new Buffer(); segment.write(new byte[offset]); segment.write(data, pos, byteCount); segment.skip(offset); result.write(segment, byteCount); } return result; } /** * Returns a new buffer containing the contents of {@code segments}, attempting to isolate each * string to its own segment in the returned buffer. This clones buffers so that segments are * shared, preventing compaction from occurring. */ public static Buffer bufferWithSegments(String... segments) throws Exception { Buffer result = new Buffer(); for (String s : segments) { int offsetInSegment = s.length() < Segment.SIZE ? (Segment.SIZE - s.length()) / 2 : 0; Buffer buffer = new Buffer(); buffer.writeUtf8(repeat('_', offsetInSegment)); buffer.writeUtf8(s); buffer.skip(offsetInSegment); result.write(buffer.clone(), buffer.size); } return result; } } okio-okio-parent-1.16.0/okio/src/test/java/okio/Utf8Test.java000066400000000000000000000241321335667376100237330ustar00rootroot00000000000000/* * Copyright (C) 2014 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.EOFException; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public final class Utf8Test { @Test public void oneByteCharacters() throws Exception { assertEncoded("00", 0x00); // Smallest 1-byte character. assertEncoded("20", ' '); assertEncoded("7e", '~'); assertEncoded("7f", 0x7f); // Largest 1-byte character. } @Test public void twoByteCharacters() throws Exception { assertEncoded("c280", 0x0080); // Smallest 2-byte character. assertEncoded("c3bf", 0x00ff); assertEncoded("c480", 0x0100); assertEncoded("dfbf", 0x07ff); // Largest 2-byte character. } @Test public void threeByteCharacters() throws Exception { assertEncoded("e0a080", 0x0800); // Smallest 3-byte character. assertEncoded("e0bfbf", 0x0fff); assertEncoded("e18080", 0x1000); assertEncoded("e1bfbf", 0x1fff); assertEncoded("ed8080", 0xd000); assertEncoded("ed9fbf", 0xd7ff); // Largest character lower than the min surrogate. assertEncoded("ee8080", 0xe000); // Smallest character greater than the max surrogate. assertEncoded("eebfbf", 0xefff); assertEncoded("ef8080", 0xf000); assertEncoded("efbfbf", 0xffff); // Largest 3-byte character. } @Test public void fourByteCharacters() throws Exception { assertEncoded("f0908080", 0x010000); // Smallest surrogate pair. assertEncoded("f48fbfbf", 0x10ffff); // Largest code point expressible by UTF-16. } @Test public void danglingHighSurrogate() throws Exception { assertStringEncoded("3f", "\ud800"); // "?" } @Test public void lowSurrogateWithoutHighSurrogate() throws Exception { assertStringEncoded("3f", "\udc00"); // "?" } @Test public void highSurrogateFollowedByNonSurrogate() throws Exception { assertStringEncoded("3f61", "\ud800\u0061"); // "?a": Following character is too low. assertStringEncoded("3fee8080", "\ud800\ue000"); // "?\ue000": Following character is too high. } @Test public void doubleLowSurrogate() throws Exception { assertStringEncoded("3f3f", "\udc00\udc00"); // "??" } @Test public void doubleHighSurrogate() throws Exception { assertStringEncoded("3f3f", "\ud800\ud800"); // "??" } @Test public void highSurrogateLowSurrogate() throws Exception { assertStringEncoded("3f3f", "\udc00\ud800"); // "??" } @Test public void multipleSegmentString() throws Exception { String a = TestUtil.repeat('a', Segment.SIZE + Segment.SIZE + 1); Buffer encoded = new Buffer().writeUtf8(a); Buffer expected = new Buffer().write(a.getBytes(Util.UTF_8)); assertEquals(expected, encoded); } @Test public void stringSpansSegments() throws Exception { Buffer buffer = new Buffer(); String a = TestUtil.repeat('a', Segment.SIZE - 1); String b = "bb"; String c = TestUtil.repeat('c', Segment.SIZE - 1); buffer.writeUtf8(a); buffer.writeUtf8(b); buffer.writeUtf8(c); assertEquals(a + b + c, buffer.readUtf8()); } @Test public void readEmptyBufferThrowsEofException() throws Exception { Buffer buffer = new Buffer(); try { buffer.readUtf8CodePoint(); fail(); } catch (EOFException expected) { } } @Test public void readLeadingContinuationByteReturnsReplacementCharacter() throws Exception { Buffer buffer = new Buffer(); buffer.writeByte(0xbf); assertEquals(Buffer.REPLACEMENT_CHARACTER, buffer.readUtf8CodePoint()); assertTrue(buffer.exhausted()); } @Test public void readMissingContinuationBytesThrowsEofException() throws Exception { Buffer buffer = new Buffer(); buffer.writeByte(0xdf); try { buffer.readUtf8CodePoint(); fail(); } catch (EOFException expected) { } assertFalse(buffer.exhausted()); // Prefix byte wasn't consumed. } @Test public void readTooLargeCodepointReturnsReplacementCharacter() throws Exception { // 5-byte and 6-byte code points are not supported. Buffer buffer = new Buffer(); buffer.write(ByteString.decodeHex("f888808080")); assertEquals(Buffer.REPLACEMENT_CHARACTER, buffer.readUtf8CodePoint()); assertEquals(Buffer.REPLACEMENT_CHARACTER, buffer.readUtf8CodePoint()); assertEquals(Buffer.REPLACEMENT_CHARACTER, buffer.readUtf8CodePoint()); assertEquals(Buffer.REPLACEMENT_CHARACTER, buffer.readUtf8CodePoint()); assertEquals(Buffer.REPLACEMENT_CHARACTER, buffer.readUtf8CodePoint()); assertTrue(buffer.exhausted()); } @Test public void readNonContinuationBytesReturnsReplacementCharacter() throws Exception { // Use a non-continuation byte where a continuation byte is expected. Buffer buffer = new Buffer(); buffer.write(ByteString.decodeHex("df20")); assertEquals(Buffer.REPLACEMENT_CHARACTER, buffer.readUtf8CodePoint()); assertEquals(0x20, buffer.readUtf8CodePoint()); // Non-continuation character not consumed. assertTrue(buffer.exhausted()); } @Test public void readCodePointBeyondUnicodeMaximum() throws Exception { // A 4-byte encoding with data above the U+10ffff Unicode maximum. Buffer buffer = new Buffer(); buffer.write(ByteString.decodeHex("f4908080")); assertEquals(Buffer.REPLACEMENT_CHARACTER, buffer.readUtf8CodePoint()); assertTrue(buffer.exhausted()); } @Test public void readSurrogateCodePoint() throws Exception { Buffer buffer = new Buffer(); buffer.write(ByteString.decodeHex("eda080")); assertEquals(Buffer.REPLACEMENT_CHARACTER, buffer.readUtf8CodePoint()); assertTrue(buffer.exhausted()); buffer.write(ByteString.decodeHex("edbfbf")); assertEquals(Buffer.REPLACEMENT_CHARACTER, buffer.readUtf8CodePoint()); assertTrue(buffer.exhausted()); } @Test public void readOverlongCodePoint() throws Exception { // Use 2 bytes to encode data that only needs 1 byte. Buffer buffer = new Buffer(); buffer.write(ByteString.decodeHex("c080")); assertEquals(Buffer.REPLACEMENT_CHARACTER, buffer.readUtf8CodePoint()); assertTrue(buffer.exhausted()); } @Test public void writeSurrogateCodePoint() throws Exception { assertStringEncoded("ed9fbf", "\ud7ff"); // Below lowest surrogate is okay. assertStringEncoded("3f", "\ud800"); // Lowest surrogate gets '?'. assertStringEncoded("3f", "\udfff"); // Highest surrogate gets '?'. assertStringEncoded("ee8080", "\ue000"); // Above highest surrogate is okay. } @Test public void writeCodePointBeyondUnicodeMaximum() throws Exception { Buffer buffer = new Buffer(); try { buffer.writeUtf8CodePoint(0x110000); fail(); } catch (IllegalArgumentException expected) { } } @Test public void size() throws Exception { assertEquals(0, Utf8.size("")); assertEquals(3, Utf8.size("abc")); assertEquals(16, Utf8.size("təˈranəˌsôr")); } @Test public void sizeWithBounds() throws Exception { assertEquals(0, Utf8.size("", 0, 0)); assertEquals(0, Utf8.size("abc", 0, 0)); assertEquals(1, Utf8.size("abc", 1, 2)); assertEquals(2, Utf8.size("abc", 0, 2)); assertEquals(3, Utf8.size("abc", 0, 3)); assertEquals(16, Utf8.size("təˈranəˌsôr", 0, 11)); assertEquals(5, Utf8.size("təˈranəˌsôr", 3, 7)); } @Test public void sizeBoundsCheck() throws Exception { try { Utf8.size(null, 0, 0); fail(); } catch (IllegalArgumentException expected) { } try { Utf8.size("abc", -1, 2); fail(); } catch (IllegalArgumentException expected) { } try { Utf8.size("abc", 2, 1); fail(); } catch (IllegalArgumentException expected) { } try { Utf8.size("abc", 1, 4); fail(); } catch (IllegalArgumentException expected) { } } private void assertEncoded(String hex, int... codePoints) throws Exception { assertCodePointEncoded(hex, codePoints); assertCodePointDecoded(hex, codePoints); assertStringEncoded(hex, new String(codePoints, 0, codePoints.length)); } private void assertCodePointEncoded(String hex, int... codePoints) throws Exception { Buffer buffer = new Buffer(); for (int codePoint : codePoints) { buffer.writeUtf8CodePoint(codePoint); } assertEquals(buffer.readByteString(), ByteString.decodeHex(hex)); } private void assertCodePointDecoded(String hex, int... codePoints) throws Exception { Buffer buffer = new Buffer().write(ByteString.decodeHex(hex)); for (int codePoint : codePoints) { assertEquals(codePoint, buffer.readUtf8CodePoint()); } assertTrue(buffer.exhausted()); } private void assertStringEncoded(String hex, String string) throws Exception { ByteString expectedUtf8 = ByteString.decodeHex(hex); // Confirm our expectations are consistent with the platform. ByteString platformUtf8 = ByteString.of(string.getBytes("UTF-8")); assertEquals(expectedUtf8, platformUtf8); // Confirm our implementation matches those expectations. ByteString actualUtf8 = new Buffer().writeUtf8(string).readByteString(); assertEquals(expectedUtf8, actualUtf8); // Confirm we are consistent when writing one code point at a time. Buffer bufferUtf8 = new Buffer(); for (int i = 0; i < string.length(); ) { int c = string.codePointAt(i); bufferUtf8.writeUtf8CodePoint(c); i += Character.charCount(c); } assertEquals(expectedUtf8, bufferUtf8.readByteString()); // Confirm we are consistent when measuring lengths. assertEquals(expectedUtf8.size(), Utf8.size(string)); assertEquals(expectedUtf8.size(), Utf8.size(string, 0, string.length())); } } okio-okio-parent-1.16.0/okio/src/test/java/okio/WaitUntilNotifiedTest.java000066400000000000000000000115361335667376100265130ustar00rootroot00000000000000/* * Copyright (C) 2016 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio; import java.io.InterruptedIOException; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.junit.After; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public final class WaitUntilNotifiedTest { final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(0); @After public void tearDown() throws Exception { executorService.shutdown(); } @Test public synchronized void notified() throws Exception { Timeout timeout = new Timeout(); timeout.timeout(5000, TimeUnit.MILLISECONDS); double start = now(); executorService.schedule(new Runnable() { @Override public void run() { synchronized (WaitUntilNotifiedTest.this) { WaitUntilNotifiedTest.this.notify(); } } }, 1000, TimeUnit.MILLISECONDS); timeout.waitUntilNotified(this); assertElapsed(1000.0, start); } @Test public synchronized void timeout() throws Exception { Timeout timeout = new Timeout(); timeout.timeout(1000, TimeUnit.MILLISECONDS); double start = now(); try { timeout.waitUntilNotified(this); fail(); } catch (InterruptedIOException expected) { assertEquals("timeout", expected.getMessage()); } assertElapsed(1000.0, start); } @Test public synchronized void deadline() throws Exception { Timeout timeout = new Timeout(); timeout.deadline(1000, TimeUnit.MILLISECONDS); double start = now(); try { timeout.waitUntilNotified(this); fail(); } catch (InterruptedIOException expected) { assertEquals("timeout", expected.getMessage()); } assertElapsed(1000.0, start); } @Test public synchronized void deadlineBeforeTimeout() throws Exception { Timeout timeout = new Timeout(); timeout.timeout(5000, TimeUnit.MILLISECONDS); timeout.deadline(1000, TimeUnit.MILLISECONDS); double start = now(); try { timeout.waitUntilNotified(this); fail(); } catch (InterruptedIOException expected) { assertEquals("timeout", expected.getMessage()); } assertElapsed(1000.0, start); } @Test public synchronized void timeoutBeforeDeadline() throws Exception { Timeout timeout = new Timeout(); timeout.timeout(1000, TimeUnit.MILLISECONDS); timeout.deadline(5000, TimeUnit.MILLISECONDS); double start = now(); try { timeout.waitUntilNotified(this); fail(); } catch (InterruptedIOException expected) { assertEquals("timeout", expected.getMessage()); } assertElapsed(1000.0, start); } @Test public synchronized void deadlineAlreadyReached() throws Exception { Timeout timeout = new Timeout(); timeout.deadlineNanoTime(System.nanoTime()); double start = now(); try { timeout.waitUntilNotified(this); fail(); } catch (InterruptedIOException expected) { assertEquals("timeout", expected.getMessage()); } assertElapsed(0.0, start); } @Test public synchronized void threadInterrupted() throws Exception { Timeout timeout = new Timeout(); double start = now(); Thread.currentThread().interrupt(); try { timeout.waitUntilNotified(this); fail(); } catch (InterruptedIOException expected) { assertEquals("interrupted", expected.getMessage()); assertTrue(Thread.interrupted()); } assertElapsed(0.0, start); } @Test public synchronized void threadInterruptedOnThrowIfReached() throws Exception { Timeout timeout = new Timeout(); Thread.currentThread().interrupt(); try { timeout.throwIfReached(); fail(); } catch (InterruptedIOException expected) { assertEquals("interrupted", expected.getMessage()); assertTrue(Thread.interrupted()); } } /** Returns the nanotime in milliseconds as a double for measuring timeouts. */ private double now() { return System.nanoTime() / 1000000.0d; } /** * Fails the test unless the time from start until now is duration, accepting differences in * -50..+450 milliseconds. */ private void assertElapsed(double duration, double start) { assertEquals(duration, now() - start - 200d, 250.0); } } okio-okio-parent-1.16.0/pom.xml000066400000000000000000000113771335667376100163350ustar00rootroot00000000000000 4.0.0 org.sonatype.oss oss-parent 7 com.squareup.okio okio-parent 1.16.0 pom Okio (Parent) A modern I/O API for Java https://github.com/square/okio okio benchmarks samples UTF-8 1.7 1.10 1.4.1 4.11 https://github.com/square/okio/ scm:git:https://github.com/square/okio.git scm:git:git@github.com:square/okio.git okio-parent-1.16.0 GitHub Issues https://github.com/square/okio/issues Apache 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt com.google.code.findbugs jsr305 3.0.2 provided junit junit ${junit.version} org.codehaus.mojo animal-sniffer-annotations ${animal.sniffer.version} org.openjdk.jmh jmh-core ${jmh.version} org.openjdk.jmh jmh-generator-annprocess ${jmh.version} provided org.apache.maven.plugins maven-compiler-plugin 3.6.1 javac-with-errorprone true ${java.version} ${java.version} org.codehaus.plexus plexus-compiler-javac-errorprone 2.8.1 com.google.errorprone error_prone_core 2.0.16 org.apache.maven.plugins maven-checkstyle-plugin 2.17 com.puppycrawl.tools checkstyle 7.7 true checkstyle.xml true **/generated/*.java verify checkstyle org.apache.maven.plugins maven-release-plugin 2.5 true okio-okio-parent-1.16.0/samples/000077500000000000000000000000001335667376100164535ustar00rootroot00000000000000okio-okio-parent-1.16.0/samples/pom.xml000066400000000000000000000015051335667376100177710ustar00rootroot00000000000000 4.0.0 com.squareup.okio okio-parent 1.16.0 samples Okio Samples com.google.code.findbugs jsr305 provided com.squareup.okio okio ${project.version} okio-okio-parent-1.16.0/samples/src/000077500000000000000000000000001335667376100172425ustar00rootroot00000000000000okio-okio-parent-1.16.0/samples/src/main/000077500000000000000000000000001335667376100201665ustar00rootroot00000000000000okio-okio-parent-1.16.0/samples/src/main/java/000077500000000000000000000000001335667376100211075ustar00rootroot00000000000000okio-okio-parent-1.16.0/samples/src/main/java/okio/000077500000000000000000000000001335667376100220505ustar00rootroot00000000000000okio-okio-parent-1.16.0/samples/src/main/java/okio/samples/000077500000000000000000000000001335667376100235145ustar00rootroot00000000000000okio-okio-parent-1.16.0/samples/src/main/java/okio/samples/Randoms.java000066400000000000000000000046271335667376100257730ustar00rootroot00000000000000/* * Copyright (C) 2018 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package okio.samples; import java.io.IOException; import java.util.Random; import okio.Buffer; import okio.BufferedSource; import okio.Okio; import okio.Source; import okio.Timeout; public final class Randoms { public void run() throws IOException, InterruptedException { Random random = new Random(3782615686L); BufferedSource source = Okio.buffer(new RandomSource(random, 5)); System.out.println(source.readUtf8()); } static final class RandomSource implements Source { private final Random random; private long bytesLeft; RandomSource(Random random, long bytesLeft) { this.random = random; this.bytesLeft = bytesLeft; } @Override public long read(Buffer sink, long byteCount) throws IOException { if (bytesLeft == -1L) throw new IllegalStateException("closed"); if (bytesLeft == 0L) return -1L; if (byteCount > Integer.MAX_VALUE) byteCount = Integer.MAX_VALUE; if (byteCount > bytesLeft) byteCount = bytesLeft; // Random is most efficient when computing 32 bits of randomness. Start with that. int ints = (int) (byteCount / 4); for (int i = 0; i < ints; i++) { sink.writeInt(random.nextInt()); } // If we need 1, 2, or 3 bytes more, keep going. We'll discard 24, 16 or 8 random bits! int bytes = (int) (byteCount - ints * 4); if (bytes > 0) { int bits = random.nextInt(); for (int i = 0; i < bytes; i++) { sink.writeByte(bits & 0xff); bits >>>= 8; } } bytesLeft -= byteCount; return byteCount; } @Override public Timeout timeout() { return Timeout.NONE; } @Override public void close() throws IOException { bytesLeft = -1L; } } public static void main(String... args) throws Exception { new Randoms().run(); } }