Compare commits

...
70 Commits
Author SHA1 Message Date
Carl Lerche 9eb5bd50b8 Bump version to 0.4 2017-02-24 10:35:53 -08:00
Carl Lerche b46d3fd32e Tweak growth algorithm in BytesMut::reserve 2017-02-20 21:07:25 -08:00
Carl Lerche 8c11456185 Combine reserve and try_reclaim
Instead of providing a separate `try_reclaim` function, `reserve` will
attempt to reclaim the existing buffer before allocating.
2017-02-20 19:31:02 -08:00
Carl Lerche 128c56ddc4 Fix link to documentation 2017-02-20 14:33:35 -08:00
Carl Lerche 5c6eadfcb0 More docs and polish 2017-02-20 14:31:26 -08:00
Carl Lerche c6fe5a1e4f Add some missing Bytes impls and fns 2017-02-20 14:03:37 -08:00
Carl Lerche 99aafb0a22 Fix uploading master docs 2017-02-20 12:43:15 -08:00
Carl Lerche cf5a1bc4f1 Rewrite Bytes / BytesMut core implementation
The previous implementation didn't factor in a single `Bytes` handle
being stored in an `Arc`. This new implementation correctly impelments
both `Bytes` and `BytesMut` such that both are `Sync`.

The rewrite also increases the number of bytes that can be stored
inline.
2017-02-20 10:41:20 -08:00
Carl Lerche 0360f191f8 Finish up docs 2017-02-17 12:23:09 -08:00
Carl Lerche aa06b6dd6a More docs 2017-02-17 11:37:19 -08:00
Carl Lerche 5048eec143 Docs & polish for Buf 2017-02-16 22:15:17 -08:00
Carl Lerche 268226051d Implement Hash and Borrow for Bytes / BytesMut 2017-02-16 16:52:56 -08:00
Carl Lerche 646624c130 Polish API surface 2017-02-16 16:44:38 -08:00
Carl Lerche bababa8797 Remove Take/TakeMut 2017-02-16 10:36:00 -08:00
Carl Lerche 0e0066e8a0 Write docs and remove unecessary fns and types 2017-02-16 10:26:48 -08:00
Carl Lerche 53d1c788e0 Start writing docs for bytes 2017-02-15 14:43:11 -08:00
Carl Lerche 4c6ebeba87 Provide two versions of drain_to and split_off
* `drain_to` and `split_off` take &self and return Bytes.
* `drain_to_mut` and `split_off_mut` take &mut self and return BytesMut
2017-02-15 12:46:27 -08:00
Carl Lerche 8da9e81469 Tweak CI settings 2017-02-15 09:45:30 -08:00
Carl Lerche 36c9a8c287 Support static refs and inline short byte slices 2017-02-15 09:38:55 -08:00
Carl Lerche 44d40d34d7 Cleanup Bytes 2017-02-10 12:04:32 -08:00
Aaron J. ToddandCarl Lerche 70fed562eb Update byteorder to 1.0 2017-02-06 11:58:44 -08:00
Carl Lerche accc8a460d Add explicit inlines 2017-02-03 11:22:16 -08:00
Carl Lerche 8b01298806 Support older Rust versions 2017-01-26 14:54:25 -08:00
Carl Lerche a8320da0f8 Lazily allocate the Arc 2017-01-26 12:56:38 -08:00
Carl Lerche 93c08064bb Fix BytesMut refcounting 2016-11-22 10:31:17 -08:00
Rick Richardson 2b796d40e9 added clone to ByteBuf and BytesMut along with simple clone test 2016-11-21 15:43:59 -08:00
Carl Lerche 12d0804f17 Fix building docs 2016-11-18 10:14:03 -08:00
Carl Lerche 4886b44516 Add more conversion impls 2016-11-02 22:27:00 -07:00
Carl Lerche a367a723d8 Impl IntoBuf for Bytes and BytesMut 2016-11-02 14:33:42 -07:00
Carl Lerche 11fe277c0d Remove default for SliceBuf<T> 2016-11-02 14:31:44 -07:00
Carl Lerche 57e84f267b Restructure and trim down the library
This commit is a significant overhaul of the library in an effort to head
towards a stable API. The rope implementation as well as a number of buffer
implementations have been removed from the library and will live at
https://github.com/carllerche/bytes-more while they incubate.

**Bytes / BytesMut**

`Bytes` is now an atomic ref counted byte slice. As it is contigous, it offers
a richer API than before.

`BytesMut` is a mutable variant. It is safe by ensuring that it is the only
handle to a given byte slice.

**AppendBuf -> ByteBuf**

`AppendBuf` has been replaced by `ByteBuf`. The API is not identical, but is
close enough to be considered a suitable replacement.

**Removed types**

The following types have been removed in favor of living in bytes-more

* RingBuf
* BlockBuf
* `Bytes` as a rope implementation
* ReadExt
* WriteExt
2016-11-02 14:23:45 -07:00
Carl Lerche d717fde5ca Fix remaining 2016-10-25 20:09:54 -07:00
Carl Lerche b42d94c33b Add BoundBuf 2016-10-14 20:42:32 -07:00
Carl Lerche 8d2508bfeb Tweak Bytes helpers 2016-10-14 20:42:16 -07:00
Carl Lerche ec7d7f27fe Add Bytes::from_vec 2016-10-14 20:07:57 -07:00
Carl Lerche d7306d949b Rename slice_buf.rs -> slice.rs 2016-10-14 20:07:42 -07:00
Carl Lerche a4bfc63de7 Tweak Sink / Source 2016-10-07 15:37:12 -07:00
Carl Lerche 6f97d04077 Add IntoBuf impls for non-ref types 2016-10-07 14:44:29 -07:00
Carl Lerche e00c08c6c7 Impl IntoBuf for &'static [u8] 2016-10-07 12:55:18 -07:00
Carl Lerche e1c7f183ca Impl IntoBuf for () 2016-09-30 09:58:41 -07:00
Carl Lerche 4ca7e0fabf Add IntoBuf trait 2016-09-25 22:40:39 -07:00
Carl Lerche b10992a5e8 Refactor RingBuf 2016-09-25 22:40:39 -07:00
Carl Lerche b1dc10e907 Rename & refactor ByteBuf -> SliceBuf 2016-09-25 22:40:35 -07:00
Carl Lerche c16ad2bc9d Add some docs 2016-09-23 14:53:14 -07:00
Carl Lerche a7d38e29e5 Remove extra lifetime sigils 2016-09-23 14:51:48 -07:00
Carl Lerche 98e0d954b5 Reorganize crate 2016-09-23 12:05:32 -07:00
Carl Lerche d05bfb6346 Add more Buf helpers 2016-09-23 07:53:17 -07:00
Carl Lerche 3c58b0c75c Add take fn to Buf & MutBuf 2016-09-21 20:41:21 -07:00
Carl Lerche 4105901244 Rename RingBuf::new -> with_capacity 2016-09-20 13:42:24 -07:00
Nikolay Kim 1f188b5628 fix dropping front block in BlockBuf 2016-09-13 15:30:44 -07:00
Stefan BühlerandCarl Lerche f693e038d9 Fix buffer overflow in Sink for Vec<u8>
Fixes #46
2016-09-03 13:25:40 -07:00
Carl Lerche d0d27bd540 Bump min supported Rust version 2016-09-03 13:24:52 -07:00
Carl Lerche 046c864543 Integrate with byteorder 2016-08-31 12:07:44 -07:00
Carl Lerche 38abb8074b Create ByteBuf with MutByteBuf::with_capacity 2016-08-23 14:22:39 -07:00
Carl Lerche be23af6bb7 Remove stable heap for now 2016-08-11 01:38:15 -07:00
Carl Lerche fbebb19a02 Simplify allocation strategy for now
Not having `unsafe_no_drop_flag` caused some weirdness with optimizing buffers
and bytes. For now, remeove it.
2016-08-11 01:36:16 -07:00
Carl Lerche 04e0ac75e2 Huge overhaul of bytes
* Get rid of `ByteStr` trait
* `Bytes` is not a concrete type
* Add `BlockBuf`
* Delete lots of cruft
* Performance work
2016-08-10 15:45:31 -07:00
Carl Lerche b2efe63c70 Get rid of SliceBuf 2016-08-05 22:49:41 -07:00
Carl Lerche 16b4266c3c Improve Buf/MutBuf impl for Cursor 2016-08-05 22:19:37 -07:00
Carl Lerche b6a424d892 Get rid of BufError 2016-08-05 21:54:29 -07:00
Carl Lerche 6529f6392a Remove traits mod 2016-08-05 21:42:07 -07:00
Carl Lerche 3f68c4bd27 Bump minimum supported Rust version 2016-07-23 10:03:30 -07:00
Carl Lerche eec203c118 Use latest stable-heap git 2016-07-23 10:00:15 -07:00
Carl Lerche 41b722dee8 Allow checking out AppendBufs from Pool 2016-07-23 09:54:18 -07:00
Carl Lerche d650404bb8 Add an AppendBuf 2016-07-23 09:49:59 -07:00
Carl Lerche 1c2234f7fe Initial stab at a buffer pool 2016-07-20 22:47:41 +02:00
Carl Lerche dc25c7564e Refactor heap allocation 2016-07-20 13:27:41 +02:00
Carl Lerche b10b1cd2e2 Bump version to v0.4.0-dev 2016-07-19 14:10:07 +02:00
Carl Lerche 3ca009577b Bytes reader should be Send 2016-06-19 21:32:18 +01:00
Carl Lerche 270d2e2844 Freshen up buf API 2016-06-08 14:48:49 -07:00
43 changed files with 4990 additions and 3392 deletions
+52 -12
View File
@@ -1,17 +1,57 @@
---
dist: trusty
language: rust
sudo: false
rust:
- nightly
- 1.1.0
script:
- cargo test
- cargo doc --no-deps
after_success:
- test $TRAVIS_PULL_REQUEST == "false" && test $TRAVIS_BRANCH == "master" && bash deploy.sh
services: docker
sudo: required
rust: stable
env:
global:
secure: "mBLJANLvtmyWCXw4zMquptqHQnws0pF+C/u4zL1Jfwz8T4UnUjmBUMxSOgSEIzrOM3qb+CTCjY2/j6BM21+/Zfdl8k8CvFWtkqQUPwIfrtwddCgI+P8Hlrk8G43drz/8XAbZ7dOl+Ovwhr0xnSD9ImfyXJec1kDWhubmgyt47Fs="
- CRATE_NAME=bytes
# Default job
- TARGET=x86_64-unknown-linux-gnu
- secure: "f17G5kb6uAQlAG9+GknFFYAmngGBqy9h+3FtNbp3mXTI0FOLltz00Ul5kGPysE4eagypm/dWOuvBkNjN01jhE6fCbekmInEsobIuanatrk6TvXT6caJqykxhPJC2cUoq8pKnMqEOuucEqPPUH6Qy6Hz4/2cRu5JV22Uv9dtS29Q="
matrix:
include:
# Run build on oldest supported rust version. Do not change the rust
# version without a Github issue first.
#
# This job will also build and deploy the docs to gh-pages.
- env: TARGET=x86_64-unknown-linux-gnu
rust: 1.10.0
after_success:
- |
pip install 'travis-cargo<0.2' --user &&
export PATH=$HOME/.local/bin:$PATH
- travis-cargo doc
- travis-cargo doc-upload
# Run tests on some extra platforms
- env: TARGET=i686-unknown-linux-gnu
- env: TARGET=armv7-unknown-linux-gnueabihf
- env: TARGET=powerpc-unknown-linux-gnu
- env: TARGET=powerpc64-unknown-linux-gnu
before_install: set -e
install:
- sh ci/install.sh
- source ~/.cargo/env || true
script:
- bash ci/script.sh
after_script: set +e
before_deploy:
- sh ci/before_deploy.sh
cache: cargo
before_cache:
# Travis can't cache files that are not readable by "others"
- chmod -R a+r $HOME/.cargo
notifications:
email:
on_success: never
+7 -13
View File
@@ -1,7 +1,7 @@
[package]
name = "bytes"
version = "0.3.0"
version = "0.4.0"
license = "MIT"
authors = ["Carl Lerche <[email protected]>"]
description = "Types and traits for working with bytes"
@@ -9,7 +9,7 @@ documentation = "https://carllerche.github.io/bytes/bytes"
homepage = "https://github.com/carllerche/bytes"
repository = "https://github.com/carllerche/bytes"
readme = "README.md"
keywords = ["buffers", "rope", "io"]
keywords = ["buffers", "zero-copy", "io"]
exclude = [
".gitignore",
".travis.yml",
@@ -17,16 +17,10 @@ exclude = [
"bench/**/*",
"test/**/*"
]
categories = ["network-programming", "data-structures"]
[dependencies]
byteorder = "1.0.0"
[dev-dependencies]
rand = "0.3.5"
[[bench]]
name = "bench"
path = "bench/bench.rs"
[[test]]
name = "test"
path = "test/test.rs"
tokio-core = "0.1.0"
-52
View File
@@ -1,52 +0,0 @@
Copyright (c) 2015 Carl Lerche
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---
Additionally, the Rope implementation is heavily inspired by ByteString found
in the Google protobuf library. The following applies to this code:
Copyright 2014, Google Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+201
View File
@@ -0,0 +1,201 @@
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 2017 Carl Lerche
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.
+25
View File
@@ -0,0 +1,25 @@
Copyright (c) 2017 Carl Lerche
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
+20 -4
View File
@@ -2,16 +2,32 @@
A utility library for working with bytes.
[![crates.io](http://meritbadge.herokuapp.com/bytes)](https://crates.io/crates/bytes)
[![Crates.io](https://img.shields.io/crates/v/bytes.svg?maxAge=2592000)](https://crates.io/crates/bytes)
[![Build Status](https://travis-ci.org/carllerche/bytes.svg?branch=master)](https://travis-ci.org/carllerche/bytes)
- [API documentation](http://carllerche.github.io/bytes/bytes/index.html)
[Documentation](https://carllerche.github.io/bytes/bytes/index.html)
## Usage
To use `bytes`, first add this to your `Cargo.toml`:
```toml
[dependencies.bytes]
git = "https://github.com/carllerche/bytes"
[dependencies]
bytes = "0.4"
```
Next, add this to your crate:
```rust
extern crate bytes;
use bytes::{Bytes, BytesMut, Buf, BufMut};
```
# License
`bytes` is primarily distributed under the terms of both the MIT license and the
Apache License (Version 2.0), with portions covered by various BSD-like
licenses.
See LICENSE-APACHE, and LICENSE-MIT for details.
-34
View File
@@ -1,34 +0,0 @@
#![feature(test, core)]
use bytes::ByteBuf;
use bytes::traits::*;
use iobuf::{RWIobuf};
use test::Bencher;
extern crate bytes;
extern crate iobuf;
extern crate test;
const SIZE:usize = 4_096;
#[bench]
pub fn bench_byte_buf_fill_4kb(b: &mut Bencher) {
b.iter(|| {
let mut buf = ByteBuf::mut_with_capacity(SIZE);
for _ in 0..SIZE {
buf.write_slice(&[0]);
}
});
}
#[bench]
pub fn bench_rw_iobuf_fill_4kb(b: &mut Bencher) {
b.iter(|| {
let mut buf = RWIobuf::new(SIZE);
for _ in 0..SIZE {
let _ = buf.fill(&[0]);
}
});
}
+210
View File
@@ -0,0 +1,210 @@
#![feature(test)]
extern crate tokio_core;
extern crate bytes;
extern crate test;
mod bench_easy_buf {
use test::{self, Bencher};
use tokio_core::io::EasyBuf;
#[bench]
fn alloc_small(b: &mut Bencher) {
b.iter(|| {
for _ in 0..1024 {
test::black_box(EasyBuf::with_capacity(12));
}
})
}
#[bench]
fn alloc_mid(b: &mut Bencher) {
b.iter(|| {
test::black_box(EasyBuf::with_capacity(128));
})
}
#[bench]
fn alloc_big(b: &mut Bencher) {
b.iter(|| {
test::black_box(EasyBuf::with_capacity(4096));
})
}
#[bench]
fn deref_front(b: &mut Bencher) {
let mut buf = EasyBuf::with_capacity(4096);
buf.get_mut().extend_from_slice(&[0; 1024][..]);
b.iter(|| {
for _ in 0..1024 {
test::black_box(buf.as_slice());
}
})
}
#[bench]
fn deref_mid(b: &mut Bencher) {
let mut buf = EasyBuf::with_capacity(4096);
buf.get_mut().extend_from_slice(&[0; 1024][..]);
let _a = buf.drain_to(512);
b.iter(|| {
for _ in 0..1024 {
test::black_box(buf.as_slice());
}
})
}
#[bench]
fn alloc_write_drain_to_mid(b: &mut Bencher) {
b.iter(|| {
let mut buf = EasyBuf::with_capacity(128);
buf.get_mut().extend_from_slice(&[0u8; 64]);
test::black_box(buf.drain_to(64));
})
}
#[bench]
fn drain_write_drain(b: &mut Bencher) {
let data = [0u8; 128];
b.iter(|| {
let mut buf = EasyBuf::with_capacity(1024);
let mut parts = Vec::with_capacity(8);
for _ in 0..8 {
buf.get_mut().extend_from_slice(&data[..]);
parts.push(buf.drain_to(128));
}
test::black_box(parts);
})
}
}
mod bench_bytes {
use test::{self, Bencher};
use bytes::{BytesMut, BufMut};
#[bench]
fn alloc_small(b: &mut Bencher) {
b.iter(|| {
for _ in 0..1024 {
test::black_box(BytesMut::with_capacity(12));
}
})
}
#[bench]
fn alloc_mid(b: &mut Bencher) {
b.iter(|| {
test::black_box(BytesMut::with_capacity(128));
})
}
#[bench]
fn alloc_big(b: &mut Bencher) {
b.iter(|| {
test::black_box(BytesMut::with_capacity(4096));
})
}
#[bench]
fn deref_unique(b: &mut Bencher) {
let mut buf = BytesMut::with_capacity(4096);
buf.put(&[0u8; 1024][..]);
b.iter(|| {
for _ in 0..1024 {
test::black_box(&buf[..]);
}
})
}
#[bench]
fn deref_unique_unroll(b: &mut Bencher) {
let mut buf = BytesMut::with_capacity(4096);
buf.put(&[0u8; 1024][..]);
b.iter(|| {
for _ in 0..128 {
test::black_box(&buf[..]);
test::black_box(&buf[..]);
test::black_box(&buf[..]);
test::black_box(&buf[..]);
test::black_box(&buf[..]);
test::black_box(&buf[..]);
test::black_box(&buf[..]);
test::black_box(&buf[..]);
}
})
}
#[bench]
fn deref_shared(b: &mut Bencher) {
let mut buf = BytesMut::with_capacity(4096);
buf.put(&[0u8; 1024][..]);
let _b2 = buf.split_off(1024);
b.iter(|| {
for _ in 0..1024 {
test::black_box(&buf[..]);
}
})
}
#[bench]
fn deref_inline(b: &mut Bencher) {
let mut buf = BytesMut::with_capacity(8);
buf.put(&[0u8; 8][..]);
b.iter(|| {
for _ in 0..1024 {
test::black_box(&buf[..]);
}
})
}
#[bench]
fn deref_two(b: &mut Bencher) {
let mut buf1 = BytesMut::with_capacity(8);
buf1.put(&[0u8; 8][..]);
let mut buf2 = BytesMut::with_capacity(4096);
buf2.put(&[0u8; 1024][..]);
b.iter(|| {
for _ in 0..512 {
test::black_box(&buf1[..]);
test::black_box(&buf2[..]);
}
})
}
#[bench]
fn alloc_write_drain_to_mid(b: &mut Bencher) {
b.iter(|| {
let mut buf = BytesMut::with_capacity(128);
buf.put_slice(&[0u8; 64]);
test::black_box(buf.drain_to(64));
})
}
#[bench]
fn drain_write_drain(b: &mut Bencher) {
let data = [0u8; 128];
b.iter(|| {
let mut buf = BytesMut::with_capacity(1024);
let mut parts = Vec::with_capacity(8);
for _ in 0..8 {
buf.put(&data[..]);
parts.push(buf.drain_to(128));
}
test::black_box(parts);
})
}
}
+23
View File
@@ -0,0 +1,23 @@
# This script takes care of packaging the build artifacts that will go in the
# release zipfile
$SRC_DIR = $PWD.Path
$STAGE = [System.Guid]::NewGuid().ToString()
Set-Location $ENV:Temp
New-Item -Type Directory -Name $STAGE
Set-Location $STAGE
$ZIP = "$SRC_DIR\$($Env:CRATE_NAME)-$($Env:APPVEYOR_REPO_TAG_NAME)-$($Env:TARGET).zip"
# TODO Update this to package the right artifacts
Copy-Item "$SRC_DIR\target\$($Env:TARGET)\release\hello.exe" '.\'
7z a "$ZIP" *
Push-AppveyorArtifact "$ZIP"
Remove-Item *.* -Force
Set-Location ..
Remove-Item $STAGE
Set-Location $SRC_DIR
+33
View File
@@ -0,0 +1,33 @@
# This script takes care of building your crate and packaging it for release
set -ex
main() {
local src=$(pwd) \
stage=
case $TRAVIS_OS_NAME in
linux)
stage=$(mktemp -d)
;;
osx)
stage=$(mktemp -d -t tmp)
;;
esac
test -f Cargo.lock || cargo generate-lockfile
# TODO Update this to build the artifacts that matter to you
cross rustc --bin hello --target $TARGET --release -- -C lto
# TODO Update this to package the right artifacts
cp target/$TARGET/release/hello $stage/
cd $stage
tar czf $src/$CRATE_NAME-$TRAVIS_TAG-$TARGET.tar.gz *
cd $src
rm -rf $stage
}
main
+31
View File
@@ -0,0 +1,31 @@
set -ex
main() {
curl https://sh.rustup.rs -sSf | \
sh -s -- -y --default-toolchain $TRAVIS_RUST_VERSION
local target=
if [ $TRAVIS_OS_NAME = linux ]; then
target=x86_64-unknown-linux-gnu
sort=sort
else
target=x86_64-apple-darwin
sort=gsort # for `sort --sort-version`, from brew's coreutils.
fi
# This fetches latest stable release
local tag=$(git ls-remote --tags --refs --exit-code https://github.com/japaric/cross \
| cut -d/ -f3 \
| grep -E '^v[0-9.]+$' \
| $sort --version-sort \
| tail -n1)
echo cross version: $tag
curl -LSfs https://japaric.github.io/trust/install.sh | \
sh -s -- \
--force \
--git japaric/cross \
--tag $tag \
--target $target
}
main
+18
View File
@@ -0,0 +1,18 @@
# This script takes care of testing your crate
set -ex
main() {
cross build --target $TARGET
if [ ! -z $DISABLE_TESTS ]; then
return
fi
cross test --target $TARGET
}
# we don't run the "test phase" when doing deploys
if [ -z $TRAVIS_TAG ]; then
main
fi
-18
View File
@@ -1,18 +0,0 @@
#!/bin/bash
rev=$(git rev-parse --short HEAD)
cd target/doc
git init
git config user.name "Carl Lerche"
git config user.email "[email protected]"
git remote add upstream "https://$GH_TOKEN@github.com/carllerche/bytes"
git fetch upstream && git reset upstream/gh-pages
touch .
git add -A .
git commit -m "rebuild pages at ${rev}"
git push -q upstream HEAD:gh-pages
-52
View File
@@ -1,52 +0,0 @@
use alloc::{Allocator, Mem, MemRef};
use std::{mem, ptr, usize};
use std::ops::DerefMut;
const MAX_ALLOC_SIZE: usize = usize::MAX;
pub struct Heap;
impl Heap {
pub fn allocate(&self, len: usize) -> MemRef {
// Make sure that the allocation is within the permitted range
if len > MAX_ALLOC_SIZE {
return MemRef::none();
}
let alloc_len = len +
mem::size_of::<Mem>() +
mem::size_of::<Vec<u8>>();
unsafe {
let mut vec: Vec<u8> = Vec::with_capacity(alloc_len);
vec.set_len(alloc_len);
let ptr = vec.deref_mut().as_mut_ptr();
ptr::write(ptr as *mut Vec<u8>, vec);
let ptr = ptr.offset(mem::size_of::<Vec<u8>>() as isize);
ptr::write(ptr as *mut Mem, Mem::new(len, mem::transmute(self as &Allocator)));
// Return the info
MemRef::new(ptr as *mut Mem)
}
}
pub fn deallocate(&self, mem: *mut Mem) {
unsafe {
let ptr = mem as *mut u8;
let _ = ptr::read(ptr.offset(-(mem::size_of::<Vec<u8>>() as isize)) as *const Vec<u8>);
}
}
}
impl Allocator for Heap {
fn allocate(&self, len: usize) -> MemRef {
Heap::allocate(self, len)
}
fn deallocate(&self, mem: *mut Mem) {
Heap::deallocate(self, mem)
}
}
-131
View File
@@ -1,131 +0,0 @@
mod heap;
pub use self::heap::{Heap};
use std::{mem, ptr};
use std::sync::atomic::{AtomicUsize, Ordering};
pub fn heap(len: usize) -> MemRef {
Heap.allocate(len)
}
/// Allocates memory to be used by Bufs or Bytes. Allows allocating memory
/// using alternate stratgies than the default Rust heap allocator. Also does
/// not require that allocations are continuous in memory.
///
/// For example, an alternate allocator could use a slab of 4kb chunks of
/// memory and return as many chunks as needed to satisfy the length
/// requirement.
pub trait Allocator: Sync + Send {
/// Allocate memory. May or may not be contiguous.
fn allocate(&self, len: usize) -> MemRef;
/// Deallocate a chunk of memory
fn deallocate(&self, mem: *mut Mem);
}
pub struct MemRef {
ptr: *mut u8,
}
impl MemRef {
pub fn new(mem: *mut Mem) -> MemRef {
let ptr = mem as *mut u8;
unsafe {
MemRef {
ptr: ptr.offset(mem::size_of::<Mem>() as isize),
}
}
}
#[inline]
pub fn none() -> MemRef {
MemRef { ptr: ptr::null_mut() }
}
#[inline]
pub fn is_none(&self) -> bool {
self.ptr.is_null()
}
#[inline]
pub fn ptr(&self) -> *mut u8 {
self.ptr
}
pub fn bytes(&self) -> &[u8] {
use std::slice;
unsafe {
slice::from_raw_parts(self.ptr(), self.mem().len)
}
}
#[inline]
pub fn bytes_mut(&mut self) -> &mut [u8] {
use std::slice;
unsafe {
slice::from_raw_parts_mut(self.ptr(), self.mem().len)
}
}
#[inline]
fn mem_ptr(&self) -> *mut Mem {
unsafe {
self.ptr.offset(-(mem::size_of::<Mem>() as isize)) as *mut Mem
}
}
#[inline]
fn mem(&self) -> &Mem {
unsafe {
mem::transmute(self.mem_ptr())
}
}
}
impl Clone for MemRef {
#[inline]
fn clone(&self) -> MemRef {
self.mem().refs.fetch_add(1, Ordering::Relaxed);
MemRef { ptr: self.ptr }
}
}
impl Drop for MemRef {
fn drop(&mut self) {
// Guard against the ref having already been dropped
if self.ptr.is_null() { return; }
// Decrement the ref count
if 1 == self.mem().refs.fetch_sub(1, Ordering::Relaxed) {
// Last ref dropped, free the memory
unsafe {
let alloc: &Allocator = mem::transmute(self.mem().allocator);
alloc.deallocate(self.mem_ptr());
}
}
}
}
unsafe impl Send for MemRef { }
unsafe impl Sync for MemRef { }
/// Memory allocated by an Allocator must be prefixed with Mem
pub struct Mem {
// TODO: It should be possible to reduce the size of this struct
allocator: *const Allocator,
refs: AtomicUsize,
len: usize,
}
impl Mem {
pub fn new(len: usize, allocator: *const Allocator) -> Mem {
Mem {
allocator: allocator,
refs: AtomicUsize::new(1),
len: len,
}
}
}
+1733
View File
File diff suppressed because it is too large Load Diff
-347
View File
@@ -1,347 +0,0 @@
use {alloc, Bytes, SeqByteStr, MAX_CAPACITY};
use traits::{Buf, MutBuf, MutBufExt, ByteStr};
use std::{cmp, fmt, ptr};
/*
*
* ===== ByteBuf =====
*
*/
/// A `Buf` backed by a contiguous region of memory.
///
/// This `Buf` is better suited for cases where there is a clear delineation
/// between reading and writing.
pub struct ByteBuf {
mem: alloc::MemRef,
cap: u32,
pos: u32,
lim: u32,
mark: Option<u32>,
}
impl ByteBuf {
/// Create a new `ByteBuf` by copying the contents of the given slice.
pub fn from_slice(bytes: &[u8]) -> ByteBuf {
let mut buf = ByteBuf::mut_with_capacity(bytes.len());
buf.write(bytes).ok().expect("unexpected failure");
buf.flip()
}
pub fn mut_with_capacity(capacity: usize) -> MutByteBuf {
assert!(capacity <= MAX_CAPACITY);
MutByteBuf { buf: ByteBuf::new(capacity as u32) }
}
pub fn none() -> ByteBuf {
ByteBuf {
mem: alloc::MemRef::none(),
cap: 0,
pos: 0,
lim: 0,
mark: None,
}
}
pub unsafe fn from_mem_ref(mem: alloc::MemRef, cap: u32, pos: u32, lim: u32) -> ByteBuf {
debug_assert!(pos <= lim && lim <= cap, "invalid arguments; cap={}; pos={}; lim={}", cap, pos, lim);
ByteBuf {
mem: mem,
cap: cap,
pos: pos,
lim: lim,
mark: None,
}
}
fn new(mut capacity: u32) -> ByteBuf {
// Handle 0 capacity case
if capacity == 0 {
return ByteBuf::none();
}
// Round the capacity to the closest power of 2
capacity = capacity.next_power_of_two();
// Allocate the memory
let mem = alloc::heap(capacity as usize);
// If the allocation failed, return a blank buf
if mem.is_none() {
return ByteBuf::none();
}
ByteBuf {
mem: mem,
cap: capacity,
pos: 0,
lim: capacity,
mark: None,
}
}
pub fn capacity(&self) -> usize {
self.cap as usize
}
pub fn flip(self) -> MutByteBuf {
let mut buf = MutByteBuf { buf: self };
buf.clear();
buf
}
/// Flips the buffer back to mutable, resetting the write position
/// to the byte after the previous write.
pub fn resume(mut self) -> MutByteBuf {
self.pos = self.lim;
self.lim = self.cap;
MutByteBuf { buf: self }
}
pub fn read_slice(&mut self, dst: &mut [u8]) -> usize {
let len = cmp::min(dst.len(), self.remaining());
let cnt = len as u32;
unsafe {
ptr::copy_nonoverlapping(
self.mem.ptr().offset(self.pos as isize),
dst.as_mut_ptr(),
len);
}
self.pos += cnt;
len
}
pub fn to_seq_byte_str(self) -> SeqByteStr {
unsafe {
let ByteBuf { mem, pos, lim, .. } = self;
SeqByteStr::from_mem_ref(
mem, pos, lim - pos)
}
}
#[inline]
pub fn to_bytes(self) -> Bytes {
Bytes::of(self.to_seq_byte_str())
}
/// Marks the current read location.
///
/// Together with `reset`, this can be used to read from a section of the
/// buffer multiple times. The marked location will be cleared when the
/// buffer is flipped.
pub fn mark(&mut self) {
self.mark = Some(self.pos);
}
/// Resets the read position to the previously marked position.
///
/// Together with `mark`, this can be used to read from a section of the
/// buffer multiple times.
///
/// # Panics
///
/// This method will panic if no mark has been set.
pub fn reset(&mut self) {
self.pos = self.mark.take().expect("no mark set");
}
#[inline]
fn pos(&self) -> usize {
self.pos as usize
}
#[inline]
fn lim(&self) -> usize {
self.lim as usize
}
#[inline]
fn remaining_u32(&self) -> u32 {
self.lim - self.pos
}
}
impl Buf for ByteBuf {
#[inline]
fn remaining(&self) -> usize {
self.remaining_u32() as usize
}
#[inline]
fn bytes<'a>(&'a self) -> &'a [u8] {
&self.mem.bytes()[self.pos()..self.lim()]
}
#[inline]
fn advance(&mut self, mut cnt: usize) {
cnt = cmp::min(cnt, self.remaining());
self.pos += cnt as u32;
}
#[inline]
fn read_slice(&mut self, dst: &mut [u8]) -> usize {
ByteBuf::read_slice(self, dst)
}
}
impl fmt::Debug for ByteBuf {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
self.bytes().fmt(fmt)
}
}
/*
*
* ===== ROByteBuf =====
*
*/
/// Same as `ByteBuf` but cannot be flipped to a `MutByteBuf`.
pub struct ROByteBuf {
buf: ByteBuf,
}
impl ROByteBuf {
pub unsafe fn from_mem_ref(mem: alloc::MemRef, cap: u32, pos: u32, lim: u32) -> ROByteBuf {
ROByteBuf {
buf: ByteBuf::from_mem_ref(mem, cap, pos, lim)
}
}
pub fn to_seq_byte_str(self) -> SeqByteStr {
self.buf.to_seq_byte_str()
}
pub fn to_bytes(self) -> Bytes {
self.buf.to_bytes()
}
/// Marks the current read location.
///
/// Together with `reset`, this can be used to read from a section of the
/// buffer multiple times.
pub fn mark(&mut self) {
self.buf.mark = Some(self.buf.pos);
}
/// Resets the read position to the previously marked position.
///
/// Together with `mark`, this can be used to read from a section of the
/// buffer multiple times.
///
/// # Panics
///
/// This method will panic if no mark has been set.
pub fn reset(&mut self) {
self.buf.pos = self.buf.mark.take().expect("no mark set");
}
}
impl Buf for ROByteBuf {
fn remaining(&self) -> usize {
self.buf.remaining()
}
fn bytes<'a>(&'a self) -> &'a [u8] {
self.buf.bytes()
}
fn advance(&mut self, cnt: usize) {
self.buf.advance(cnt)
}
fn read_slice(&mut self, dst: &mut [u8]) -> usize {
self.buf.read_slice(dst)
}
}
impl fmt::Debug for ROByteBuf {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
self.bytes().fmt(fmt)
}
}
/*
*
* ===== MutByteBuf =====
*
*/
pub struct MutByteBuf {
buf: ByteBuf,
}
impl MutByteBuf {
pub fn capacity(&self) -> usize {
self.buf.capacity() as usize
}
pub fn flip(self) -> ByteBuf {
let mut buf = self.buf;
buf.lim = buf.pos;
buf.pos = 0;
buf
}
pub fn clear(&mut self) {
self.buf.pos = 0;
self.buf.lim = self.buf.cap;
}
#[inline]
pub fn write_slice(&mut self, src: &[u8]) -> usize {
let cnt = src.len() as u32;
let rem = self.buf.remaining_u32();
if rem < cnt {
self.write_ptr(src.as_ptr(), rem)
} else {
self.write_ptr(src.as_ptr(), cnt)
}
}
#[inline]
fn write_ptr(&mut self, src: *const u8, len: u32) -> usize {
unsafe {
ptr::copy_nonoverlapping(
src,
self.buf.mem.ptr().offset(self.buf.pos as isize),
len as usize);
self.buf.pos += len;
len as usize
}
}
pub fn bytes<'a>(&'a self) -> &'a [u8] {
&self.buf.mem.bytes()[..self.buf.pos()]
}
}
impl MutBuf for MutByteBuf {
fn remaining(&self) -> usize {
self.buf.remaining()
}
unsafe fn advance(&mut self, cnt: usize) {
self.buf.advance(cnt)
}
unsafe fn mut_bytes<'a>(&'a mut self) -> &'a mut [u8] {
let pos = self.buf.pos();
let lim = self.buf.lim();
&mut self.buf.mem.bytes_mut()[pos..lim]
}
}
impl fmt::Debug for MutByteBuf {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
self.bytes().fmt(fmt)
}
}
-430
View File
@@ -1,430 +0,0 @@
mod byte;
mod ring;
mod sink;
mod slice;
mod source;
mod take;
pub use self::byte::{ByteBuf, MutByteBuf, ROByteBuf};
pub use self::ring::RingBuf;
pub use self::slice::{SliceBuf, MutSliceBuf};
pub use self::take::Take;
use {BufError, RopeBuf};
use std::{cmp, fmt, io, ptr, usize};
/// A trait for values that provide sequential read access to bytes.
pub trait Buf {
/// Returns the number of bytes that can be accessed from the Buf
fn remaining(&self) -> usize;
/// Returns a slice starting at the current Buf position and of length
/// between 0 and `Buf::remaining()`.
fn bytes<'a>(&'a self) -> &'a [u8];
/// Advance the internal cursor of the Buf
fn advance(&mut self, cnt: usize);
/// Returns true if there are any more bytes to consume
fn has_remaining(&self) -> bool {
self.remaining() > 0
}
/// Read bytes from the `Buf` into the given slice and advance the cursor by
/// the number of bytes read.
/// Returns the number of bytes read.
///
/// ```
/// use bytes::{SliceBuf, Buf};
///
/// let mut buf = SliceBuf::wrap(b"hello world");
/// let mut dst = [0; 5];
///
/// buf.read_slice(&mut dst);
/// assert_eq!(b"hello", &dst);
/// assert_eq!(6, buf.remaining());
/// ```
fn read_slice(&mut self, dst: &mut [u8]) -> usize {
let mut off = 0;
let len = cmp::min(dst.len(), self.remaining());
while off < len {
let cnt;
unsafe {
let src = self.bytes();
cnt = cmp::min(src.len(), len - off);
ptr::copy_nonoverlapping(
src.as_ptr(), dst[off..].as_mut_ptr(), cnt);
off += src.len();
}
self.advance(cnt);
}
len
}
/// Read a single byte from the `Buf`
fn read_byte(&mut self) -> Option<u8> {
let mut dst = [0];
if self.read_slice(&mut dst) == 0 {
return None;
}
Some(dst[0])
}
}
/// An extension trait providing extra functions applicable to all `Buf` values.
pub trait BufExt {
/// Read bytes from this Buf into the given sink and advance the cursor by
/// the number of bytes read.
fn read<S: Sink>(&mut self, dst: S) -> Result<usize, S::Error>;
}
/// A trait for values that provide sequential write access to bytes.
pub trait MutBuf : Sized {
/// Returns the number of bytes that can be written to the MutBuf
fn remaining(&self) -> usize;
/// Advance the internal cursor of the MutBuf
unsafe fn advance(&mut self, cnt: usize);
/// Returns true iff there is any more space for bytes to be written
fn has_remaining(&self) -> bool {
self.remaining() > 0
}
/// Returns a mutable slice starting at the current MutBuf position and of
/// length between 0 and `MutBuf::remaining()`.
///
/// The returned byte slice may represent uninitialized memory.
unsafe fn mut_bytes<'a>(&'a mut self) -> &'a mut [u8];
/// Write bytes from the given slice into the `MutBuf` and advance the
/// cursor by the number of bytes written.
/// Returns the number of bytes written.
///
/// ```
/// use bytes::{MutSliceBuf, Buf, MutBuf};
///
/// let mut dst = [0; 6];
///
/// {
/// let mut buf = MutSliceBuf::wrap(&mut dst);
/// buf.write_slice(b"hello");
///
/// assert_eq!(1, buf.remaining());
/// }
///
/// assert_eq!(b"hello\0", &dst);
/// ```
fn write_slice(&mut self, src: &[u8]) -> usize {
let mut off = 0;
let len = cmp::min(src.len(), self.remaining());
while off < len {
let cnt;
unsafe {
let dst = self.mut_bytes();
cnt = cmp::min(dst.len(), len - off);
ptr::copy_nonoverlapping(
src[off..].as_ptr(),
dst.as_mut_ptr(),
cnt);
off += cnt;
}
unsafe { self.advance(cnt); }
}
len
}
/// Write a single byte to the `MuBuf`
fn write_byte(&mut self, byte: u8) -> bool {
let src = [byte];
if self.write_slice(&src) == 0 {
return false;
}
true
}
}
/// An extension trait providing extra functions applicable to all `MutBuf` values.
pub trait MutBufExt {
/// Write bytes from the given source into the current `MutBuf` and advance
/// the cursor by the number of bytes written.
fn write<S: Source>(&mut self, src: S) -> Result<usize, S::Error>;
}
/*
*
* ===== *Ext impls =====
*
*/
impl<B: Buf> BufExt for B {
fn read<S: Sink>(&mut self, dst: S) -> Result<usize, S::Error> {
dst.sink(self)
}
}
impl<B: MutBuf> MutBufExt for B {
fn write<S: Source>(&mut self, src: S) -> Result<usize, S::Error> {
src.fill(self)
}
}
/*
*
* ===== Sink / Source =====
*
*/
/// A value that reads bytes from a Buf into itself
pub trait Sink {
type Error;
fn sink<B: Buf>(self, buf: &mut B) -> Result<usize, Self::Error>;
}
/// A value that writes bytes from itself into a `MutBuf`.
pub trait Source {
type Error;
fn fill<B: MutBuf>(self, buf: &mut B) -> Result<usize, Self::Error>;
}
impl<'a> Sink for &'a mut [u8] {
type Error = BufError;
fn sink<B: Buf>(self, buf: &mut B) -> Result<usize, BufError> {
Ok(buf.read_slice(self))
}
}
impl<'a> Sink for &'a mut Vec<u8> {
type Error = BufError;
fn sink<B: Buf>(self, buf: &mut B) -> Result<usize, BufError> {
use std::slice;
self.clear();
let rem = buf.remaining();
let cap = self.capacity();
// Ensure that the vec is big enough
if rem > self.capacity() {
self.reserve(rem - cap);
}
unsafe {
{
let dst = &mut self[..];
let cnt = buf.read_slice(slice::from_raw_parts_mut(dst.as_mut_ptr(), rem));
debug_assert!(cnt == rem);
}
self.set_len(rem);
}
Ok(rem)
}
}
impl<'a> Source for &'a [u8] {
type Error = BufError;
fn fill<B: MutBuf>(self, buf: &mut B) -> Result<usize, BufError> {
Ok(buf.write_slice(self))
}
}
impl<'a> Source for &'a Vec<u8> {
type Error = BufError;
fn fill<B: MutBuf>(self, buf: &mut B) -> Result<usize, BufError> {
Ok(buf.write_slice(self.as_ref()))
}
}
impl<'a, R: io::Read+'a> Source for &'a mut R {
type Error = io::Error;
fn fill<B: MutBuf>(self, buf: &mut B) -> Result<usize, io::Error> {
let mut cnt = 0;
while buf.has_remaining() {
let i = try!(self.read(unsafe { buf.mut_bytes() }));
if i == 0 {
break;
}
unsafe { buf.advance(i); }
cnt += i;
}
Ok(cnt)
}
}
/*
*
* ===== Buf impls =====
*
*/
impl Buf for Box<Buf+'static> {
fn remaining(&self) -> usize {
(**self).remaining()
}
fn bytes(&self) -> &[u8] {
(**self).bytes()
}
fn advance(&mut self, cnt: usize) {
(**self).advance(cnt);
}
fn read_slice(&mut self, dst: &mut [u8]) -> usize {
(**self).read_slice(dst)
}
}
impl fmt::Debug for Box<Buf+'static> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "Box<Buf> {{ remaining: {} }}", self.remaining())
}
}
impl Buf for io::Cursor<Vec<u8>> {
fn remaining(&self) -> usize {
self.get_ref().len() - self.position() as usize
}
fn bytes(&self) -> &[u8] {
let pos = self.position() as usize;
&(&self.get_ref())[pos..]
}
fn advance(&mut self, cnt: usize) {
let pos = self.position() as usize;
let pos = cmp::min(self.get_ref().len(), pos + cnt);
self.set_position(pos as u64);
}
}
impl MutBuf for Vec<u8> {
fn remaining(&self) -> usize {
usize::MAX - self.len()
}
unsafe fn advance(&mut self, cnt: usize) {
let len = self.len() + cnt;
if len > self.capacity() {
// Reserve additional
// TODO: Should this case panic?
let cap = self.capacity();
self.reserve(cap - len);
}
self.set_len(len);
}
unsafe fn mut_bytes(&mut self) -> &mut [u8] {
use std::slice;
if self.capacity() == self.len() {
self.reserve(64); // Grow the vec
}
let cap = self.capacity();
let len = self.len();
let ptr = self.as_mut_ptr();
&mut slice::from_raw_parts_mut(ptr, cap)[len..]
}
}
impl<'a> Buf for io::Cursor<&'a [u8]> {
fn remaining(&self) -> usize {
self.get_ref().len() - self.position() as usize
}
fn bytes(&self) -> &[u8] {
let pos = self.position() as usize;
&(&self.get_ref())[pos..]
}
fn advance(&mut self, cnt: usize) {
let pos = self.position() as usize;
let pos = cmp::min(self.get_ref().len(), pos + cnt);
self.set_position(pos as u64);
}
}
/*
*
* ===== Read impls =====
*
*/
macro_rules! impl_read {
($ty:ty) => {
impl io::Read for $ty {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if !self.has_remaining() {
return Ok(0);
}
Ok(self.read_slice(buf))
}
}
}
}
impl_read!(ByteBuf);
impl_read!(ROByteBuf);
impl_read!(RopeBuf);
impl_read!(Box<Buf+'static>);
macro_rules! impl_write {
($ty:ty) => {
impl io::Write for $ty {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
if !self.has_remaining() {
return Ok(0);
}
Ok(self.write_slice(buf))
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
}
}
impl_write!(MutByteBuf);
-250
View File
@@ -1,250 +0,0 @@
use {alloc, Buf, MutBuf};
use std::{cmp, fmt, io, ptr};
enum Mark {
NoMark,
At { pos: usize, len: usize },
}
/// Buf backed by a continous chunk of memory. Maintains a read cursor and a
/// write cursor. When reads and writes reach the end of the allocated buffer,
/// wraps around to the start.
///
/// This type is suited for use cases where reads and writes are intermixed.
pub struct RingBuf {
ptr: alloc::MemRef, // Pointer to the memory
cap: usize, // Capacity of the buffer
pos: usize, // Offset of read cursor
len: usize, // Number of bytes to read
mark: Mark, // Marked read position
}
// TODO: There are most likely many optimizations that can be made
impl RingBuf {
/// Allocates a new `RingBuf` with the specified capacity.
pub fn new(mut capacity: usize) -> RingBuf {
// Handle the 0 length buffer case
if capacity == 0 {
return RingBuf {
ptr: alloc::MemRef::none(),
cap: 0,
pos: 0,
len: 0,
mark: Mark::NoMark,
}
}
// Round to the next power of 2 for better alignment
capacity = capacity.next_power_of_two();
let mem = alloc::heap(capacity as usize);
RingBuf {
ptr: mem,
cap: capacity,
pos: 0,
len: 0,
mark: Mark::NoMark,
}
}
/// Returns `true` if the buf cannot accept any further writes.
pub fn is_full(&self) -> bool {
self.cap == self.len
}
/// Returns `true` if the buf cannot accept any further reads.
pub fn is_empty(&self) -> bool {
self.len == 0
}
/// Returns the number of bytes that the buf can hold.
pub fn capacity(&self) -> usize {
self.cap
}
/// Marks the current read location.
///
/// Together with `reset`, this can be used to read from a section of the
/// buffer multiple times. The mark will be cleared if it is overwritten
/// during a write.
pub fn mark(&mut self) {
self.mark = Mark::At { pos: self.pos, len: self.len };
}
/// Resets the read position to the previously marked position.
///
/// Together with `mark`, this can be used to read from a section of the
/// buffer multiple times.
///
/// # Panics
///
/// This method will panic if no mark has been set,
pub fn reset(&mut self){
match self.mark {
Mark::NoMark => panic!("no mark set"),
Mark::At {pos, len} => {
self.pos = pos;
self.len = len;
self.mark = Mark::NoMark;
}
}
}
/// Resets all internal state to the initial state.
pub fn clear(&mut self) {
self.pos = 0;
self.len = 0;
self.mark = Mark::NoMark;
}
/// Returns the number of bytes remaining to read.
fn read_remaining(&self) -> usize {
self.len
}
/// Returns the remaining write capacity until which the buf becomes full.
fn write_remaining(&self) -> usize {
self.cap - self.len
}
fn advance_reader(&mut self, mut cnt: usize) {
if self.cap == 0 {
return;
}
cnt = cmp::min(cnt, self.read_remaining());
self.pos += cnt;
self.pos %= self.cap;
self.len -= cnt;
}
fn advance_writer(&mut self, mut cnt: usize) {
cnt = cmp::min(cnt, self.write_remaining());
self.len += cnt;
// Adjust the mark to account for bytes written.
if let Mark::At { ref mut len, .. } = self.mark {
*len += cnt;
}
// Clear the mark if we've written past it.
if let Mark::At { len, .. } = self.mark {
if len > self.cap {
self.mark = Mark::NoMark;
}
}
}
}
impl Clone for RingBuf {
fn clone(&self) -> RingBuf {
use std::cmp;
let mut ret = RingBuf::new(self.cap);
ret.pos = self.pos;
ret.len = self.len;
unsafe {
let to = self.pos + self.len;
if to > self.cap {
ptr::copy(self.ptr.ptr() as *const u8, ret.ptr.ptr(), to % self.cap);
}
ptr::copy(
self.ptr.ptr().offset(self.pos as isize) as *const u8,
ret.ptr.ptr().offset(self.pos as isize),
cmp::min(self.len, self.cap - self.pos));
}
ret
}
// TODO: an improved version of clone_from is possible that potentially
// re-uses the buffer
}
impl fmt::Debug for RingBuf {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "RingBuf[.. {}]", self.len)
}
}
impl Buf for RingBuf {
fn remaining(&self) -> usize {
self.read_remaining()
}
fn bytes(&self) -> &[u8] {
let mut to = self.pos + self.len;
if to > self.cap {
to = self.cap
}
&self.ptr.bytes()[self.pos .. to]
}
fn advance(&mut self, cnt: usize) {
self.advance_reader(cnt)
}
}
impl MutBuf for RingBuf {
fn remaining(&self) -> usize {
self.write_remaining()
}
unsafe fn advance(&mut self, cnt: usize) {
self.advance_writer(cnt)
}
unsafe fn mut_bytes(&mut self) -> &mut [u8] {
if self.cap == 0 {
return self.ptr.bytes_mut();
}
let mut from;
let mut to;
from = self.pos + self.len;
from %= self.cap;
to = from + <Self as MutBuf>::remaining(&self);
if to >= self.cap {
to = self.cap;
}
&mut self.ptr.bytes_mut()[from..to]
}
}
impl io::Read for RingBuf {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if !Buf::has_remaining(self) {
return Ok(0);
}
Ok(self.read_slice(buf))
}
}
impl io::Write for RingBuf {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
if !MutBuf::has_remaining(self) {
return Ok(0);
}
Ok(self.write_slice(buf))
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
unsafe impl Send for RingBuf { }
View File
-59
View File
@@ -1,59 +0,0 @@
use std::cmp;
use {Buf, MutBuf};
// TODO: Rename -> Cursor. Use as buf for various byte strings
pub struct SliceBuf<'a> {
bytes: &'a [u8],
pos: usize
}
impl<'a> SliceBuf<'a> {
pub fn wrap(bytes: &'a [u8]) -> SliceBuf<'a> {
SliceBuf { bytes: bytes, pos: 0 }
}
}
impl<'a> Buf for SliceBuf<'a> {
fn remaining(&self) -> usize {
self.bytes.len() - self.pos
}
fn bytes<'b>(&'b self) -> &'b [u8] {
&self.bytes[self.pos..]
}
fn advance(&mut self, mut cnt: usize) {
cnt = cmp::min(cnt, self.remaining());
self.pos += cnt;
}
}
pub struct MutSliceBuf<'a> {
bytes: &'a mut [u8],
pos: usize
}
impl<'a> MutSliceBuf<'a> {
pub fn wrap(bytes: &'a mut [u8]) -> MutSliceBuf<'a> {
MutSliceBuf {
bytes: bytes,
pos: 0
}
}
}
impl<'a> MutBuf for MutSliceBuf<'a> {
fn remaining(&self) -> usize {
self.bytes.len() - self.pos
}
unsafe fn advance(&mut self, mut cnt: usize) {
cnt = cmp::min(cnt, self.remaining());
self.pos += cnt;
}
unsafe fn mut_bytes<'b>(&'b mut self) -> &'b mut [u8] {
&mut self.bytes[self.pos..]
}
}
View File
-79
View File
@@ -1,79 +0,0 @@
use buf::{Buf, MutBuf};
use std::{cmp, io};
#[derive(Debug)]
pub struct Take<T> {
inner: T,
limit: usize,
}
impl<T> Take<T> {
pub fn new(inner: T, limit: usize) -> Take<T> {
Take {
inner: inner,
limit: limit,
}
}
pub fn into_inner(self) -> T {
self.inner
}
pub fn get_ref(&self) -> &T {
&self.inner
}
pub fn get_mut(&mut self) -> &mut T {
&mut self.inner
}
pub fn limit(&self) -> usize {
self.limit
}
pub fn set_limit(&mut self, lim: usize) {
self.limit = lim
}
}
impl<T: Buf> Buf for Take<T> {
fn remaining(&self) -> usize {
cmp::min(self.inner.remaining(), self.limit)
}
fn bytes<'a>(&'a self) -> &'a [u8] {
&self.inner.bytes()[..self.limit]
}
fn advance(&mut self, cnt: usize) {
let cnt = cmp::min(cnt, self.limit);
self.limit -= cnt;
self.inner.advance(cnt);
}
}
impl<T: Buf> io::Read for Take<T> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if !self.has_remaining() {
return Ok(0);
}
Ok(self.read_slice(buf))
}
}
impl<T: MutBuf> MutBuf for Take<T> {
fn remaining(&self) -> usize {
cmp::min(self.inner.remaining(), self.limit)
}
unsafe fn mut_bytes<'a>(&'a mut self) -> &'a mut [u8] {
&mut self.inner.mut_bytes()[..self.limit]
}
unsafe fn advance(&mut self, cnt: usize) {
let cnt = cmp::min(cnt, self.limit);
self.limit -= cnt;
self.inner.advance(cnt);
}
}
+2155
View File
File diff suppressed because it is too large Load Diff
+81 -47
View File
@@ -1,54 +1,88 @@
#![crate_name = "bytes"]
#![deny(warnings)]
//! Provides abstractions for working with bytes.
//!
//! The `bytes` crate provides an efficient byte buffer structure
//! ([`Bytes`](struct.Bytes.html)) and traits for working with buffer
//! implementations ([`Buf`], [`BufMut`]).
//!
//! [`Buf`]: trait.Buf.html
//! [`BufMut`]: trait.BufMut.html
//!
//! # `Bytes`
//!
//! `Bytes` is an efficient container for storing and operating on continguous
//! slices of memory. It is intended for use primarily in networking code, but
//! could have applications elsewhere as well.
//!
//! `Bytes` values facilitate zero-copy network programming by allowing multiple
//! `Bytes` objects to point to the same underlying memory. This is managed by
//! using a reference count to track when the memory is no longer needed and can
//! be freed.
//!
//! A `Bytes` handle can be created directly from an existing byte store (such as &[u8]
//! or Vec<u8>), but usually a `BytesMut` is used first and written to. For
//! example:
//!
//! ```rust
//! use bytes::{BytesMut, BufMut, BigEndian};
//!
//! let mut buf = BytesMut::with_capacity(1024);
//! buf.put(&b"hello world"[..]);
//! buf.put_u16::<BigEndian>(1234);
//!
//! let a = buf.drain();
//! assert_eq!(a, b"hello world\x04\xD2"[..]);
//!
//! buf.put(&b"goodbye world"[..]);
//!
//! let b = buf.drain();
//! assert_eq!(b, b"goodbye world"[..]);
//!
//! assert_eq!(buf.capacity(), 998);
//! ```
//!
//! In the above example, only a single buffer of 1024 is allocated. The handles
//! `a` and `b` will share the underlying buffer and maintain indices tracking
//! the view into the buffer represented by the handle.
//!
//! See the [struct docs] for more details.
//!
//! [struct docs]: struct.Bytes.html
//!
//! # `Buf`, `BufMut`
//!
//! These two traits provide read and write access to buffers. The underlying
//! storage may or may not be in contiguous memory. For example, `Bytes` is a
//! buffer that guarantees contiguous memory, but a [rope] stores the bytes in
//! disjoint chunks. `Buf` and `BufMut` maintain cursors tracking the current
//! position in the underlying byte storage. When bytes are read or written, the
//! cursor is advanced.
//!
//! [rope]: https://en.wikipedia.org/wiki/Rope_(data_structure)
//!
//! ## Relation with `Read` and `Write`
//!
//! At first glance, it may seem that `Buf` and `BufMut` overlap in
//! functionality with `std::io::Ready` and `std::io::Write`. However, they
//! serve different purposes. A buffer is the value that is provided as an
//! argument to `Read::read` and `Write::write`. `Read` and `Write` may then
//! perform a syscall, which has the potential of failing. Operations on `Buf`
//! and `BufMut` are infallible.
pub mod alloc;
pub mod buf;
pub mod str;
#![deny(warnings, missing_docs)]
extern crate byteorder;
mod buf;
mod bytes;
pub use buf::{
Buf,
BufExt,
MutBuf,
MutBufExt,
ByteBuf,
MutByteBuf,
RingBuf,
ROByteBuf,
SliceBuf,
MutSliceBuf,
BufMut,
IntoBuf,
Source,
Sink,
Reader,
Writer,
Take,
};
pub use str::{
ByteStr,
Bytes,
Rope,
RopeBuf,
SeqByteStr,
SmallByteStr,
SmallByteStrBuf,
ToBytes,
};
use std::u32;
pub mod traits {
//! All traits are re-exported here to allow glob imports.
pub use {Buf, BufExt, MutBuf, MutBufExt, ByteStr, ToBytes};
}
const MAX_CAPACITY: usize = u32::MAX as usize;
/*
*
* ===== BufError =====
*
*/
#[derive(Copy, Clone, Debug)]
pub enum BufError {
Underflow,
Overflow,
}
pub use bytes::{Bytes, BytesMut};
pub use byteorder::{ByteOrder, BigEndian, LittleEndian};
-310
View File
@@ -1,310 +0,0 @@
use {ByteBuf, MutBuf, SmallByteStr, Source, BufError};
use traits::{Buf, ByteStr, ToBytes};
use std::{cmp, fmt, mem, ops, ptr};
use std::any::{Any, TypeId};
const INLINE: usize = 1;
/// A specialized `ByteStr` box.
pub struct Bytes {
vtable: usize,
data: *mut (),
}
impl Bytes {
pub fn from_slice(bytes: &[u8]) -> Bytes {
SmallByteStr::from_slice(bytes)
.map(|small| Bytes::of(small))
.unwrap_or_else(|| ByteBuf::from_slice(bytes).to_bytes())
}
pub fn of<B: ByteStr>(bytes: B) -> Bytes {
unsafe {
if inline::<B>() {
let vtable;
let data;
{
let obj: &ByteStrPriv = &bytes;
let obj: TraitObject = mem::transmute(obj);
let ptr: *const *mut () = mem::transmute(obj.data);
data = *ptr;
vtable = obj.vtable;
}
// Prevent drop from being called
mem::forget(bytes);
Bytes {
vtable: vtable as usize | INLINE,
data: data,
}
} else {
let obj: Box<ByteStrPriv> = Box::new(bytes);
let obj: TraitObject = mem::transmute(obj);
Bytes {
vtable: obj.vtable as usize,
data: obj.data,
}
}
}
}
pub fn empty() -> Bytes {
Bytes::of(SmallByteStr::zero())
}
/// If the underlying `ByteStr` is of type `B`, returns a reference to it
/// otherwise None.
pub fn downcast_ref<'a, B: ByteStr>(&'a self) -> Option<&'a B> {
if TypeId::of::<B>() == self.obj().get_type_id() {
unsafe {
if inline::<B>() {
return Some(mem::transmute(&self.data));
} else {
return Some(mem::transmute(self.data));
}
}
}
None
}
/// If the underlying `ByteStr` is of type `B`, returns the unwraped value,
/// otherwise, returns the original `Bytes` as `Err`.
pub fn try_unwrap<B: ByteStr>(self) -> Result<B, Bytes> {
if TypeId::of::<B>() == self.obj().get_type_id() {
unsafe {
// Underlying ByteStr value is of the correct type. Unwrap it
let ret;
if inline::<B>() {
// The value is inline, read directly from the pointer
ret = ptr::read(mem::transmute(&self.data));
} else {
ret = ptr::read(mem::transmute(self.data));
}
mem::forget(self);
Ok(ret)
}
} else {
Err(self)
}
}
fn obj(&self) -> &ByteStrPriv {
unsafe {
mem::transmute(self.to_trait_object())
}
}
fn obj_mut(&mut self) -> &mut ByteStrPriv {
unsafe {
mem::transmute(self.to_trait_object())
}
}
unsafe fn to_trait_object(&self) -> TraitObject {
if self.is_inline() {
TraitObject {
data: mem::transmute(&self.data),
vtable: mem::transmute(self.vtable - 1),
}
} else {
TraitObject {
data: self.data,
vtable: mem::transmute(self.vtable),
}
}
}
fn is_inline(&self) -> bool {
(self.vtable & INLINE) == INLINE
}
}
fn inline<B: ByteStr>() -> bool {
mem::size_of::<B>() <= 2 * mem::size_of::<usize>()
}
impl ByteStr for Bytes {
type Buf = Box<Buf+'static>;
fn buf(&self) -> Box<Buf+'static> {
self.obj().buf()
}
fn concat<B: ByteStr>(&self, other: &B) -> Bytes {
self.obj().concat(&Bytes::of(other.clone()))
}
fn len(&self) -> usize {
self.obj().len()
}
fn slice(&self, begin: usize, end: usize) -> Bytes {
self.obj().slice(begin, end)
}
fn split_at(&self, mid: usize) -> (Bytes, Bytes) {
self.obj().split_at(mid)
}
}
impl ToBytes for Bytes {
fn to_bytes(self) -> Bytes {
self
}
}
impl ops::Index<usize> for Bytes {
type Output = u8;
fn index(&self, index: usize) -> &u8 {
self.obj().index(index)
}
}
impl fmt::Debug for Bytes {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
super::debug(self, "Bytes", fmt)
}
}
impl Clone for Bytes {
fn clone(&self) -> Bytes {
self.obj().clone()
}
}
impl Drop for Bytes {
fn drop(&mut self) {
unsafe {
if self.is_inline() {
let obj = self.obj_mut();
obj.drop();
} else {
let _: Box<ByteStrPriv> =
mem::transmute(self.obj());
}
}
}
}
unsafe impl Send for Bytes { }
unsafe impl Sync for Bytes { }
impl<'a> Source for &'a Bytes {
type Error = BufError;
fn fill<B: MutBuf>(self, dst: &mut B) -> Result<usize, BufError> {
let mut src = ByteStr::buf(self);
let mut res = 0;
while src.has_remaining() && dst.has_remaining() {
let l;
unsafe {
let s = src.bytes();
let d = dst.mut_bytes();
l = cmp::min(s.len(), d.len());
ptr::copy_nonoverlapping(
s.as_ptr(),
d.as_mut_ptr(),
l);
}
src.advance(l);
unsafe { dst.advance(l); }
res += l;
}
Ok(res)
}
}
trait ByteStrPriv {
fn buf(&self) -> Box<Buf+'static>;
fn clone(&self) -> Bytes;
fn concat(&self, other: &Bytes) -> Bytes;
fn drop(&mut self);
fn get_type_id(&self) -> TypeId;
fn index(&self, index: usize) -> &u8;
fn len(&self) -> usize;
fn slice(&self, begin: usize, end: usize) -> Bytes;
fn split_at(&self, mid: usize) -> (Bytes, Bytes);
}
impl<B: ByteStr> ByteStrPriv for B {
fn buf(&self) -> Box<Buf+'static> {
Box::new(self.buf())
}
fn clone(&self) -> Bytes {
Bytes::of(self.clone())
}
fn concat(&self, other: &Bytes) -> Bytes {
self.concat(other)
}
fn drop(&mut self) {
unsafe {
ptr::read(mem::transmute(self))
}
}
fn get_type_id(&self) -> TypeId {
TypeId::of::<B>()
}
fn index(&self, index: usize) -> &u8 {
ops::Index::index(self, index)
}
fn len(&self) -> usize {
self.len()
}
fn slice(&self, begin: usize, end: usize) -> Bytes {
self.slice(begin, end)
}
fn split_at(&self, mid: usize) -> (Bytes, Bytes) {
self.split_at(mid)
}
}
// TODO: Figure out how to not depend on the memory layout of trait objects
// Blocked: rust-lang/rust#24050
#[repr(C)]
struct TraitObject {
data: *mut (),
vtable: *mut (),
}
#[test]
pub fn test_size_of() {
// TODO: One day, there shouldn't be a drop flag
let ptr_size = mem::size_of::<usize>();
let expect = ptr_size * 3;
assert_eq!(expect, mem::size_of::<Bytes>());
assert_eq!(expect + ptr_size, mem::size_of::<Option<Bytes>>());
}
-187
View File
@@ -1,187 +0,0 @@
mod bytes;
mod rope;
mod seq;
mod small;
pub use self::bytes::Bytes;
pub use self::rope::{Rope, RopeBuf};
pub use self::seq::SeqByteStr;
pub use self::small::{SmallByteStr, SmallByteStrBuf};
use {Buf};
use std::{cmp, fmt, ops};
use std::any::Any;
/// An immutable sequence of bytes. Operations will not mutate the original
/// value. Since only immutable access is permitted, operations do not require
/// copying (though, sometimes copying will happen as an optimization).
pub trait ByteStr : Clone + Sized + Send + Sync + Any + ToBytes + ops::Index<usize, Output=u8> + 'static {
// Until HKT lands, the buf must be bound by 'static
type Buf: Buf+'static;
/// Returns a read-only `Buf` for accessing the byte contents of the
/// `ByteStr`.
fn buf(&self) -> Self::Buf;
/// Returns a new `Bytes` value representing the concatenation of `self`
/// with the given `Bytes`.
fn concat<B: ByteStr+'static>(&self, other: &B) -> Bytes;
/// Returns the number of bytes in the ByteStr
fn len(&self) -> usize;
/// Returns true if the length of the `ByteStr` is 0
fn is_empty(&self) -> bool {
self.len() == 0
}
/// Returns a new ByteStr value containing the byte range between `begin`
/// (inclusive) and `end` (exclusive)
fn slice(&self, begin: usize, end: usize) -> Bytes;
/// Returns a new ByteStr value containing the byte range starting from
/// `begin` (inclusive) to the end of the byte str.
///
/// Equivalent to `bytes.slice(begin, bytes.len())`
fn slice_from(&self, begin: usize) -> Bytes {
self.slice(begin, self.len())
}
/// Returns a new ByteStr value containing the byte range from the start up
/// to `end` (exclusive).
///
/// Equivalent to `bytes.slice(0, end)`
fn slice_to(&self, end: usize) -> Bytes {
self.slice(0, end)
}
/// Divides the value into two `Bytes` at the given index.
///
/// The first will contain all bytes from `[0, mid]` (excluding the index
/// `mid` itself) and the second will contain all indices from `[mid, len)`
/// (excluding the index `len` itself).
///
/// Panics if `mid > len`.
fn split_at(&self, mid: usize) -> (Bytes, Bytes) {
(self.slice_to(mid), self.slice_from(mid))
}
}
macro_rules! impl_parteq {
($ty:ty) => {
impl<B: ByteStr> cmp::PartialEq<B> for $ty {
fn eq(&self, other: &B) -> bool {
if self.len() != other.len() {
return false;
}
let mut buf1 = self.buf();
let mut buf2 = self.buf();
while buf1.has_remaining() {
let len;
{
let b1 = buf1.bytes();
let b2 = buf2.bytes();
len = cmp::min(b1.len(), b2.len());
if b1[..len] != b2[..len] {
return false;
}
}
buf1.advance(len);
buf2.advance(len);
}
true
}
fn ne(&self, other: &B) -> bool {
return !self.eq(other)
}
}
}
}
impl_parteq!(SeqByteStr);
impl_parteq!(SmallByteStr);
impl_parteq!(Bytes);
impl_parteq!(Rope);
macro_rules! impl_eq {
($ty:ty) => {
impl cmp::Eq for $ty {}
}
}
impl_eq!(Bytes);
/*
*
* ===== ToBytes =====
*
*/
pub trait ToBytes {
/// Consumes the value and returns a `Bytes` instance containing
/// identical bytes
fn to_bytes(self) -> Bytes;
}
impl<'a> ToBytes for &'a [u8] {
fn to_bytes(self) -> Bytes {
Bytes::from_slice(self)
}
}
impl<'a> ToBytes for &'a Vec<u8> {
fn to_bytes(self) -> Bytes {
(&self[..]).to_bytes()
}
}
/*
*
* ===== Internal utilities =====
*
*/
fn debug<B: ByteStr>(bytes: &B, name: &str, fmt: &mut fmt::Formatter) -> fmt::Result {
let mut buf = bytes.buf();
try!(write!(fmt, "{}[len={}; ", name, bytes.len()));
let mut rem = 128;
while let Some(byte) = buf.read_byte() {
if rem > 0 {
if is_ascii(byte) {
try!(write!(fmt, "{}", byte as char));
} else {
try!(write!(fmt, "\\x{:02X}", byte));
}
rem -= 1;
} else {
try!(write!(fmt, " ... "));
break;
}
}
try!(write!(fmt, "]"));
Ok(())
}
fn is_ascii(byte: u8) -> bool {
match byte {
10 | 13 | 32...126 => true,
_ => false,
}
}
-585
View File
@@ -1,585 +0,0 @@
use {Bytes, ByteBuf, Source, BufError};
use traits::{Buf, ByteStr, MutBuf, MutBufExt, ToBytes};
use std::{cmp, mem, ops};
use std::sync::Arc;
// The implementation is mostly a port of the implementation found in the Java
// protobuf lib.
const CONCAT_BY_COPY_LEN: usize = 128;
const MAX_DEPTH: usize = 47;
// Used to decide when to rebalance the tree.
static MIN_LENGTH_BY_DEPTH: [usize; MAX_DEPTH] = [
1, 2, 3, 5, 8,
13, 21, 34, 55, 89,
144, 233, 377, 610, 987,
1_597, 2_584, 4_181, 6_765, 10_946,
17_711, 28_657, 46_368, 75_025, 121_393,
196_418, 317_811, 514_229, 832_040, 1_346_269,
2_178_309, 3_524_578, 5_702_887, 9_227_465, 14_930_352,
24_157_817, 39_088_169, 63_245_986, 102_334_155, 165_580_141,
267_914_296, 433_494_437, 701_408_733, 1_134_903_170, 1_836_311_903,
2_971_215_073, 4_294_967_295];
/// An immutable sequence of bytes formed by concatenation of other `ByteStr`
/// values, without copying the data in the pieces. The concatenation is
/// represented as a tree whose leaf nodes are each a `Bytes` value.
///
/// Most of the operation here is inspired by the now-famous paper [Ropes: an
/// Alternative to Strings. hans-j. boehm, russ atkinson and michael
/// plass](http://www.cs.rit.edu/usr/local/pub/jeh/courses/QUARTERS/FP/Labs/CedarRope/rope-paper.pdf).
///
/// Fundamentally the Rope algorithm represents the collection of pieces as a
/// binary tree. BAP95 uses a Fibonacci bound relating depth to a minimum
/// sequence length, sequences that are too short relative to their depth cause
/// a tree rebalance. More precisely, a tree of depth d is "balanced" in the
/// terminology of BAP95 if its length is at least F(d+2), where F(n) is the
/// n-the Fibonacci number. Thus for depths 0, 1, 2, 3, 4, 5,... we have
/// minimum lengths 1, 2, 3, 5, 8, 13,...
pub struct Rope {
inner: Arc<RopeInner>,
}
impl Rope {
pub fn from_slice(bytes: &[u8]) -> Rope {
Rope::new(Bytes::from_slice(bytes), Bytes::empty())
}
/// Returns a Rope consisting of the supplied Bytes as a single segment.
pub fn of<B: ByteStr + 'static>(bytes: B) -> Rope {
let bytes = Bytes::of(bytes);
match bytes.try_unwrap() {
Ok(rope) => rope,
Err(bytes) => Rope::new(bytes, Bytes::empty()),
}
}
fn new(left: Bytes, right: Bytes) -> Rope {
Rope { inner: Arc::new(RopeInner::new(left, right)) }
}
pub fn len(&self) -> usize {
self.inner.len as usize
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/*
*
* ===== Priv fns =====
*
*/
fn depth(&self) -> u16 {
self.inner.depth
}
fn left(&self) -> &Bytes {
&self.inner.left
}
fn right(&self) -> &Bytes {
&self.inner.right
}
fn pieces<'a>(&'a self) -> PieceIter<'a> {
PieceIter::new(&self.inner)
}
}
impl ByteStr for Rope {
type Buf = RopeBuf;
fn buf(&self) -> RopeBuf {
RopeBuf::new(self.clone())
}
fn concat<B: ByteStr+'static>(&self, other: &B) -> Bytes {
let left = Bytes::of(self.clone());
let right = Bytes::of(other.clone());
Bytes::of(concat(left, right))
}
fn len(&self) -> usize {
Rope::len(self)
}
fn slice(&self, begin: usize, end: usize) -> Bytes {
if begin >= end || begin >= self.len() {
return Bytes::empty()
}
let end = cmp::min(end, self.len());
let len = end - begin;
// Empty slice
if len == 0 {
return Bytes::empty();
}
// Full rope
if len == self.len() {
return Bytes::of(self.clone());
}
// == Proper substring ==
let left_len = self.inner.left.len();
if end <= left_len {
// Slice on the left
return self.inner.left.slice(begin, end);
}
if begin >= left_len {
// Slice on the right
return self.inner.right.slice(begin - left_len, end - left_len);
}
// Split slice
let left_slice = self.inner.left.slice_from(begin);
let right_slice = self.inner.right.slice_to(end - left_len);
Bytes::of(Rope::new(left_slice, right_slice))
}
}
impl ToBytes for Rope {
fn to_bytes(self) -> Bytes {
Bytes::of(self)
}
}
impl ops::Index<usize> for Rope {
type Output = u8;
fn index(&self, index: usize) -> &u8 {
assert!(index < self.len());
let left_len = self.inner.left.len();
if index < left_len {
self.inner.left.index(index)
} else {
self.inner.right.index(index - left_len)
}
}
}
impl Clone for Rope {
fn clone(&self) -> Rope {
Rope { inner: self.inner.clone() }
}
}
impl<'a> Source for &'a Rope {
type Error = BufError;
fn fill<B: MutBuf>(self, _buf: &mut B) -> Result<usize, BufError> {
unimplemented!();
}
}
/*
*
* ===== Helper Fns =====
*
*/
fn depth(bytes: &Bytes) -> u16 {
match bytes.downcast_ref::<Rope>() {
Some(rope) => rope.inner.depth,
None => 0,
}
}
fn is_balanced(bytes: &Bytes) -> bool {
if let Some(rope) = bytes.downcast_ref::<Rope>() {
return rope.len() >= MIN_LENGTH_BY_DEPTH[rope.depth() as usize];
}
true
}
fn concat(left: Bytes, right: Bytes) -> Rope {
if right.is_empty() {
return Rope::of(left);
}
if left.is_empty() {
return Rope::of(right);
}
let len = left.len() + right.len();
if len < CONCAT_BY_COPY_LEN {
return concat_bytes(&left, &right, len);
}
if let Some(left) = left.downcast_ref::<Rope>() {
let len = left.inner.right.len() + right.len();
if len < CONCAT_BY_COPY_LEN {
// Optimization from BAP95: As an optimization of the case
// where the ByteString is constructed by repeated concatenate,
// recognize the case where a short string is concatenated to a
// left-hand node whose right-hand branch is short. In the
// paper this applies to leaves, but we just look at the length
// here. This has the advantage of shedding references to
// unneeded data when substrings have been taken.
//
// When we recognize this case, we do a copy of the data and
// create a new parent node so that the depth of the result is
// the same as the given left tree.
let new_right = concat_bytes(&left.inner.right, &right, len);
return Rope::new(left.inner.left.clone(), Bytes::of(new_right));
}
if depth(left.left()) > depth(left.right()) && left.depth() > depth(&right) {
// Typically for concatenate-built strings the left-side is
// deeper than the right. This is our final attempt to
// concatenate without increasing the tree depth. We'll redo
// the the node on the RHS. This is yet another optimization
// for building the string by repeatedly concatenating on the
// right.
let new_right = Rope::new(left.right().clone(), right);
return Rope::new(left.left().clone(), Bytes::of(new_right));
}
}
// Fine, we'll add a node and increase the tree depth -- unless we
// rebalance ;^)
let depth = cmp::max(depth(&left), depth(&right)) + 1;
if len >= MIN_LENGTH_BY_DEPTH[depth as usize] {
// No need to rebalance
return Rope::new(left, right);
}
Balance::new().balance(left, right)
}
fn concat_bytes(left: &Bytes, right: &Bytes, len: usize) -> Rope {
let mut buf = ByteBuf::mut_with_capacity(len);
buf.write(left).ok().expect("unexpected error");
buf.write(right).ok().expect("unexpected error");
return Rope::of(buf.flip().to_bytes());
}
fn depth_for_len(len: usize) -> u16 {
match MIN_LENGTH_BY_DEPTH.binary_search(&len) {
Ok(idx) => idx as u16,
Err(idx) => {
// It wasn't an exact match, so convert to the index of the
// containing fragment, which is one less even than the insertion
// point.
idx as u16 - 1
}
}
}
/*
*
* ===== RopeBuf =====
*
*/
pub struct RopeBuf {
rem: usize,
// Only here for the ref count
#[allow(dead_code)]
rope: Rope,
// This must be done with unsafe code to avoid having a lifetime bound on
// RopeBuf but is safe due to Rope being held. As long as data doesn't
// escape (which it shouldn't) it is safe. Doing this properly would
// require HKT.
pieces: PieceIter<'static>,
leaf_buf: Option<Box<Buf+'static>>,
}
impl RopeBuf {
fn new(rope: Rope) -> RopeBuf {
// In order to get the lifetimes to work out, transmute to a 'static
// lifetime. Never allow the iter to escape the internals of RopeBuf.
let mut pieces: PieceIter<'static> =
unsafe { mem::transmute(rope.pieces()) };
// Get the next buf
let leaf_buf = pieces.next()
.map(|bytes| bytes.buf());
let len = rope.len();
RopeBuf {
rope: rope,
rem: len,
pieces: pieces,
leaf_buf: leaf_buf,
}
}
}
impl Buf for RopeBuf {
fn remaining(&self) -> usize {
self.rem
}
fn bytes(&self) -> &[u8] {
self.leaf_buf.as_ref()
.map(|b| b.bytes())
.unwrap_or(&[])
}
fn advance(&mut self, mut cnt: usize) {
cnt = cmp::min(cnt, self.rem);
// Advance the internal cursor
self.rem -= cnt;
// Advance the leaf buffer
while cnt > 0 {
{
let curr = self.leaf_buf.as_mut()
.expect("expected a value");
if curr.remaining() > cnt {
curr.advance(cnt);
break;
}
cnt -= curr.remaining();
}
self.leaf_buf = self.pieces.next()
.map(|bytes| bytes.buf());
}
}
}
/*
*
* ===== PieceIter =====
*
*/
// TODO: store stack inline if possible
struct PieceIter<'a> {
stack: Vec<&'a RopeInner>,
next: Option<&'a Bytes>,
}
impl<'a> PieceIter<'a> {
fn new(root: &'a RopeInner) -> PieceIter<'a> {
let mut iter = PieceIter {
stack: vec![],
next: None,
};
iter.next = iter.get_leaf_by_left(root);
iter
}
fn get_leaf_by_left(&mut self, mut root: &'a RopeInner) -> Option<&'a Bytes> {
loop {
self.stack.push(root);
let left = &root.left;
if left.is_empty() {
return None;
}
if let Some(rope) = left.downcast_ref::<Rope>() {
root = &*rope.inner;
continue;
}
return Some(left);
}
}
fn next_non_empty_leaf(&mut self) -> Option<&'a Bytes>{
loop {
if let Some(node) = self.stack.pop() {
if let Some(rope) = node.right.downcast_ref::<Rope>() {
let res = self.get_leaf_by_left(&rope.inner);
if res.is_none() {
continue;
}
return res;
}
if node.right.is_empty() {
continue;
}
return Some(&node.right);
}
return None;
}
}
}
impl<'a> Iterator for PieceIter<'a> {
type Item = &'a Bytes;
fn next(&mut self) -> Option<&'a Bytes> {
let ret = self.next.take();
if ret.is_some() {
self.next = self.next_non_empty_leaf();
}
ret
}
}
/*
*
* ===== Balance =====
*
*/
struct Balance {
stack: Vec<Bytes>,
}
impl Balance {
fn new() -> Balance {
Balance { stack: vec![] }
}
fn balance(&mut self, left: Bytes, right: Bytes) -> Rope {
self.do_balance(left);
self.do_balance(right);
let mut partial = self.stack.pop()
.expect("expected a value");
while !partial.is_empty() {
let new_left = self.stack.pop()
.expect("expected a value");
partial = Bytes::of(Rope::new(new_left, partial));
}
Rope::of(partial)
}
fn do_balance(&mut self, root: Bytes) {
// BAP95: Insert balanced subtrees whole. This means the result might not
// be balanced, leading to repeated rebalancings on concatenate. However,
// these rebalancings are shallow due to ignoring balanced subtrees, and
// relatively few calls to insert() result.
if is_balanced(&root) {
self.insert(root);
} else {
let rope = root.try_unwrap::<Rope>()
.ok().expect("expected a value");
self.do_balance(rope.left().clone());
self.do_balance(rope.right().clone());
}
}
// Push a string on the balance stack (BAP95). BAP95 uses an array and
// calls the elements in the array 'bins'. We instead use a stack, so the
// 'bins' of lengths are represented by differences between the elements of
// minLengthByDepth.
//
// If the length bin for our string, and all shorter length bins, are
// empty, we just push it on the stack. Otherwise, we need to start
// concatenating, putting the given string in the "middle" and continuing
// until we land in an empty length bin that matches the length of our
// concatenation.
fn insert(&mut self, bytes: Bytes) {
let depth_bin = depth_for_len(bytes.len());
let bin_end = MIN_LENGTH_BY_DEPTH[depth_bin as usize + 1];
// BAP95: Concatenate all trees occupying bins representing the length
// of our new piece or of shorter pieces, to the extent that is
// possible. The goal is to clear the bin which our piece belongs in,
// but that may not be entirely possible if there aren't enough longer
// bins occupied.
if let Some(len) = self.peek().map(|r| r.len()) {
if len >= bin_end {
self.stack.push(bytes);
return;
}
}
let bin_start = MIN_LENGTH_BY_DEPTH[depth_bin as usize];
// Concatenate the subtrees of shorter length
let mut new_tree = self.stack.pop()
.expect("expected a value");
while let Some(len) = self.peek().map(|r| r.len()) {
// If the head is big enough, break the loop
if len >= bin_start { break; }
let left = self.stack.pop()
.expect("expected a value");
new_tree = Bytes::of(Rope::new(left, new_tree));
}
// Concatenate the given string
new_tree = Bytes::of(Rope::new(new_tree, bytes));
// Continue concatenating until we land in an empty bin
while let Some(len) = self.peek().map(|r| r.len()) {
let depth_bin = depth_for_len(new_tree.len());
let bin_end = MIN_LENGTH_BY_DEPTH[depth_bin as usize + 1];
if len < bin_end {
let left = self.stack.pop()
.expect("expected a value");
new_tree = Bytes::of(Rope::new(left, new_tree));
} else {
break;
}
}
self.stack.push(new_tree);
}
fn peek(&self) -> Option<&Bytes> {
self.stack.last()
}
}
struct RopeInner {
left: Bytes,
right: Bytes,
depth: u16,
len: u32,
}
impl RopeInner {
fn new(left: Bytes, right: Bytes) -> RopeInner {
// If left is 0 then right must be zero
debug_assert!(!left.is_empty() || right.is_empty());
let len = left.len() + right.len();
let depth = cmp::max(depth(&left), depth(&right)) + 1;
RopeInner {
left: left,
right: right,
depth: depth,
len: len as u32,
}
}
}
-100
View File
@@ -1,100 +0,0 @@
use {alloc, ByteBuf, MutBufExt, ByteStr, ROByteBuf, Rope, Bytes, ToBytes};
use std::ops;
pub struct SeqByteStr {
mem: alloc::MemRef,
pos: u32,
len: u32,
}
impl SeqByteStr {
/// Create a new `SeqByteStr` from a byte slice.
///
/// The contents of the byte slice will be copied.
pub fn from_slice(bytes: &[u8]) -> SeqByteStr {
let mut buf = ByteBuf::mut_with_capacity(bytes.len());
if let Err(e) = buf.write(bytes) {
panic!("failed to copy bytes from slice; err={:?}", e);
}
buf.flip().to_seq_byte_str()
}
/// Creates a new `SeqByteStr` from a `MemRef`, an offset, and a length.
///
/// This function is unsafe as there are no guarantees that the given
/// arguments are valid.
pub unsafe fn from_mem_ref(mem: alloc::MemRef, pos: u32, len: u32) -> SeqByteStr {
SeqByteStr {
mem: mem,
pos: pos,
len: len,
}
}
}
impl ByteStr for SeqByteStr {
type Buf = ROByteBuf;
fn buf(&self) -> ROByteBuf {
unsafe {
let pos = self.pos;
let lim = pos + self.len;
ROByteBuf::from_mem_ref(self.mem.clone(), lim, pos, lim)
}
}
fn concat<B: ByteStr+'static>(&self, other: &B) -> Bytes {
Rope::of(self.clone()).concat(other)
}
fn len(&self) -> usize {
self.len as usize
}
fn slice(&self, begin: usize, end: usize) -> Bytes {
if begin >= end || begin >= self.len() {
return Bytes::empty()
}
let bytes = unsafe {
SeqByteStr::from_mem_ref(
self.mem.clone(),
self.pos + begin as u32,
(end - begin) as u32)
};
Bytes::of(bytes)
}
}
impl ToBytes for SeqByteStr {
fn to_bytes(self) -> Bytes {
Bytes::of(self)
}
}
impl ops::Index<usize> for SeqByteStr {
type Output = u8;
fn index(&self, index: usize) -> &u8 {
assert!(index < self.len());
unsafe {
&*self.mem.ptr()
.offset(index as isize + self.pos as isize)
}
}
}
impl Clone for SeqByteStr {
fn clone(&self) -> SeqByteStr {
SeqByteStr {
mem: self.mem.clone(),
pos: self.pos,
len: self.len,
}
}
}
-131
View File
@@ -1,131 +0,0 @@
use {Bytes, Rope};
use traits::{Buf, MutBuf, ByteStr, ToBytes};
use std::{cmp, ops};
/*
*
* ===== SmallByteStr =====
*
*/
#[cfg(target_pointer_width = "64")]
const MAX_LEN: usize = 7;
#[cfg(target_pointer_width = "32")]
const MAX_LEN: usize = 3;
#[derive(Clone, Copy)]
pub struct SmallByteStr {
len: u8,
bytes: [u8; MAX_LEN],
}
impl SmallByteStr {
pub fn zero() -> SmallByteStr {
use std::mem;
SmallByteStr {
len: 0,
bytes: unsafe { mem::zeroed() }
}
}
pub fn from_slice(bytes: &[u8]) -> Option<SmallByteStr> {
use std::{mem, ptr};
if bytes.len() > MAX_LEN {
return None;
}
let mut ret = SmallByteStr {
len: bytes.len() as u8,
bytes: unsafe { mem::zeroed() },
};
// Copy the memory
unsafe {
ptr::copy_nonoverlapping(
bytes.as_ptr(),
ret.bytes.as_mut_ptr(),
bytes.len());
}
Some(ret)
}
pub fn as_slice(&self) -> &[u8] {
&self.bytes[..self.len as usize]
}
}
impl ByteStr for SmallByteStr {
type Buf = SmallByteStrBuf;
fn buf(&self) -> SmallByteStrBuf {
SmallByteStrBuf { small: self.clone() }
}
fn concat<B: ByteStr+'static>(&self, other: &B) -> Bytes {
Rope::of(self.clone()).concat(other)
}
fn len(&self) -> usize {
self.len as usize
}
fn slice(&self, begin: usize, end: usize) -> Bytes {
Bytes::from_slice(&self.as_slice()[begin..end])
}
}
impl ToBytes for SmallByteStr {
fn to_bytes(self) -> Bytes {
Bytes::of(self)
}
}
impl ops::Index<usize> for SmallByteStr {
type Output = u8;
fn index(&self, index: usize) -> &u8 {
assert!(index < self.len());
&self.bytes[index]
}
}
#[derive(Clone)]
#[allow(missing_copy_implementations)]
pub struct SmallByteStrBuf {
small: SmallByteStr,
}
impl SmallByteStrBuf {
fn len(&self) -> usize {
(self.small.len & 0x0F) as usize
}
fn pos(&self) -> usize {
(self.small.len >> 4) as usize
}
}
impl Buf for SmallByteStrBuf {
fn remaining(&self) -> usize {
self.len() - self.pos()
}
fn bytes(&self) -> &[u8] {
&self.small.bytes[self.pos()..self.len()]
}
fn advance(&mut self, mut cnt: usize) {
cnt = cmp::min(cnt, self.remaining());
self.small.len += (cnt as u8) << 4;
}
}
#[test]
pub fn test_size_of() {
use std::mem;
assert_eq!(mem::size_of::<SmallByteStr>(), mem::size_of::<usize>());
}
-18
View File
@@ -1,18 +0,0 @@
use rand::random;
extern crate bytes;
extern crate rand;
mod test_buf;
mod test_buf_fill;
mod test_buf_take;
mod test_byte_buf;
mod test_bytes;
mod test_ring;
mod test_rope;
mod test_seq_byte_str;
mod test_small_byte_str;
fn gen_bytes(n: usize) -> Vec<u8> {
(0..n).map(|_| random()).collect()
}
-50
View File
@@ -1,50 +0,0 @@
use bytes::{Buf, MutBuf, MutBufExt};
use std::usize;
use std::io::{Cursor};
#[test]
pub fn test_fresh_cursor_vec() {
let mut buf = Cursor::new(b"hello".to_vec());
assert_eq!(buf.remaining(), 5);
assert_eq!(buf.bytes(), b"hello");
buf.advance(2);
assert_eq!(buf.remaining(), 3);
assert_eq!(buf.bytes(), b"llo");
buf.advance(3);
assert_eq!(buf.remaining(), 0);
assert_eq!(buf.bytes(), b"");
buf.advance(1);
assert_eq!(buf.remaining(), 0);
assert_eq!(buf.bytes(), b"");
}
#[test]
pub fn test_vec_as_mut_buf() {
let mut buf = Vec::with_capacity(64);
assert_eq!(buf.remaining(), usize::MAX);
unsafe {
assert!(buf.mut_bytes().len() >= 64);
}
buf.write(&b"zomg"[..]).unwrap();
assert_eq!(&buf, b"zomg");
assert_eq!(buf.remaining(), usize::MAX - 4);
assert_eq!(buf.capacity(), 64);
for _ in 0..16 {
buf.write(&b"zomg"[..]).unwrap();
}
assert_eq!(buf.len(), 68);
}
-47
View File
@@ -1,47 +0,0 @@
use bytes::*;
use std::io;
#[test]
pub fn test_filling_buf_from_reader() {
let mut reader = chunks(vec![b"foo", b"bar", b"baz"]);
let mut buf = ByteBuf::mut_with_capacity(1024);
assert_eq!(9, buf.write(&mut reader).unwrap());
assert_eq!(b"foobarbaz".to_bytes(), buf.flip().to_bytes());
}
fn chunks(chunks: Vec<&'static [u8]>) -> Chunked {
Chunked { chunks: chunks }
}
struct Chunked {
chunks: Vec<&'static [u8]>,
}
impl io::Read for Chunked {
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
use std::{cmp, ptr};
if self.chunks.is_empty() {
return Ok(0);
}
let src = self.chunks[0];
let len = cmp::min(src.len(), dst.len());
unsafe {
ptr::copy_nonoverlapping(
src[..len].as_ptr(),
dst[..len].as_mut_ptr(),
len);
}
if len < src.len() {
self.chunks[0] = &src[len..];
} else {
self.chunks.remove(0);
}
Ok(len)
}
}
-12
View File
@@ -1,12 +0,0 @@
use bytes::*;
use std::io::{Cursor, Read};
#[test]
pub fn test_take_from_buf() {
let mut buf = Take::new(Cursor::new(b"hello world".to_vec()), 5);
let mut res = vec![];
buf.read_to_end(&mut res);
assert_eq!(&res, b"hello");
}
-71
View File
@@ -1,71 +0,0 @@
use bytes::ByteBuf;
use bytes::traits::*;
#[test]
pub fn test_initial_buf_empty() {
let buf = ByteBuf::mut_with_capacity(100);
assert!(buf.capacity() == 128);
assert!(buf.remaining() == 128);
let buf = buf.flip();
assert!(buf.remaining() == 0);
let buf = buf.flip();
assert!(buf.remaining() == 128);
}
#[test]
pub fn test_byte_buf_bytes() {
let mut buf = ByteBuf::mut_with_capacity(32);
buf.write(&b"hello "[..]).unwrap();
assert_eq!(&b"hello "[..], buf.bytes());
buf.write(&b"world"[..]).unwrap();
assert_eq!(&b"hello world"[..], buf.bytes());
let buf = buf.flip();
assert_eq!(&b"hello world"[..], buf.bytes());
}
#[test]
pub fn test_byte_buf_read_write() {
let mut buf = ByteBuf::mut_with_capacity(32);
buf.write(&b"hello world"[..]).unwrap();
assert_eq!(21, buf.remaining());
buf.write(&b" goodbye"[..]).unwrap();
assert_eq!(13, buf.remaining());
let mut buf = buf.flip();
let mut dst = [0; 5];
buf.mark();
assert_eq!(5, buf.read(&mut dst[..]).unwrap());
assert_eq!(b"hello", &dst);
buf.reset();
assert_eq!(5, buf.read(&mut dst[..]).unwrap());
assert_eq!(b"hello", &dst);
assert_eq!(5, buf.read(&mut dst[..]).unwrap());
assert_eq!(b" worl", &dst);
let mut dst = [0; 2];
assert_eq!(2, buf.read(&mut dst[..]).unwrap());
assert_eq!(b"d ", &dst);
let mut dst = [0; 7];
assert_eq!(7, buf.read(&mut dst[..]).unwrap());
assert_eq!(b"goodbye", &dst);
let mut buf = buf.resume();
assert_eq!(13, buf.remaining());
buf.write(&b" have fun"[..]).unwrap();
assert_eq!(4, buf.remaining());
let buf = buf.flip();
assert_eq!(buf.bytes(), b"hello world goodbye have fun");
}
-42
View File
@@ -1,42 +0,0 @@
use bytes::*;
#[test]
pub fn test_debug_short_str_valid_ascii() {
let b = Bytes::from_slice(b"abcdefghij234");
let d = format!("{:?}", b);
assert_eq!(d, "Bytes[len=13; abcdefghij234]");
}
#[test]
pub fn test_debug_long_str_valid_ascii() {
let s = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. \
Duis volutpat eros in gravida malesuada. Phasellus lobortis \
maximus cursus. Praesent tristique orci non purus porta \
dapibus. Ut ut commodo risus, sed semper felis. Phasellus \
bibendum dui nunc, ac pharetra dui viverra a. Nunc imperdiet \
sed nulla ut condimentum. In hac habitasse platea dictumst. \
Interdum et malesuada fames ac ante ipsum primis in faucibus. \
Sed facilisis dictum malesuada. Sed tempor odio ullamcorper mi \
iaculis, eu tempus diam semper. Vivamus pulvinar metus ac erat \
aliquet aliquam.";
let b = Bytes::from_slice(s.as_bytes());
let d = format!("{:?}", b);
assert_eq!(d, "Bytes[len=556; Lorem ipsum dolor sit amet, \
consectetur adipiscing elit. Duis volutpat \
eros in gravida malesuada. Phasellus \
lobortis maximus cur ... ]");
}
#[test]
pub fn test_short_string_invalid_ascii() {
let b = Bytes::from_slice(b"foo\x00bar\xFFbaz");
let d = format!("{:?}", b);
println!("{:?}", b);
assert_eq!(d, "Bytes[len=11; foo\\x00bar\\xFFbaz]");
}
-138
View File
@@ -1,138 +0,0 @@
use bytes::RingBuf;
#[test]
pub fn test_initial_buf_empty() {
use bytes::traits::{Buf, BufExt, MutBuf, MutBufExt};
let mut buf = RingBuf::new(16);
assert_eq!(MutBuf::remaining(&buf), 16);
assert_eq!(Buf::remaining(&buf), 0);
let bytes_written = buf.write(&[1, 2, 3][..]).unwrap();
assert_eq!(bytes_written, 3);
let bytes_written = buf.write(&[][..]).unwrap();
assert_eq!(bytes_written, 0);
assert_eq!(MutBuf::remaining(&buf), 13);
assert_eq!(Buf::remaining(&buf), 3);
assert_eq!(buf.bytes(), [1, 2, 3]);
let mut out = [0u8; 3];
buf.mark();
let bytes_read = buf.read(&mut out[..]).unwrap();;
assert_eq!(bytes_read, 3);
assert_eq!(out, [1, 2, 3]);
buf.reset();
let bytes_read = buf.read(&mut out[..]).unwrap();;
assert_eq!(bytes_read, 3);
assert_eq!(out, [1, 2, 3]);
assert_eq!(MutBuf::remaining(&buf), 16);
assert_eq!(Buf::remaining(&buf), 0);
}
#[test]
fn test_wrapping_write() {
use bytes::traits::{BufExt, MutBufExt};
let mut buf = RingBuf::new(16);
let mut out = [0;10];
buf.write(&[42;12][..]).unwrap();
let bytes_read = buf.read(&mut out[..]).unwrap();
assert_eq!(bytes_read, 10);
let bytes_written = buf.write(&[23;8][..]).unwrap();
assert_eq!(bytes_written, 8);
buf.mark();
let bytes_read = buf.read(&mut out[..]).unwrap();
assert_eq!(bytes_read, 10);
assert_eq!(out, [42, 42, 23, 23, 23, 23, 23, 23, 23, 23]);
buf.reset();
let bytes_read = buf.read(&mut out[..]).unwrap();
assert_eq!(bytes_read, 10);
assert_eq!(out, [42, 42, 23, 23, 23, 23, 23, 23, 23, 23]);
}
#[test]
fn test_io_write_and_read() {
use std::io::{Read, Write};
let mut buf = RingBuf::new(16);
let mut out = [0;8];
let written = buf.write(&[1;8][..]).unwrap();
assert_eq!(written, 8);
buf.read(&mut out).unwrap();
assert_eq!(out, [1;8]);
let written = buf.write(&[2;8][..]).unwrap();
assert_eq!(written, 8);
let bytes_read = buf.read(&mut out).unwrap();
assert_eq!(bytes_read, 8);
assert_eq!(out, [2;8]);
}
#[test]
#[should_panic]
fn test_wrap_reset() {
use std::io::{Read, Write};
let mut buf = RingBuf::new(8);
buf.write(&[1, 2, 3, 4, 5, 6, 7]).unwrap();
buf.mark();
buf.read(&mut [0; 4]).unwrap();
buf.write(&[1, 2, 3, 4]).unwrap();
buf.reset();
}
#[test]
// Test that writes across a mark/reset are preserved.
fn test_mark_write() {
use std::io::{Read, Write};
let mut buf = RingBuf::new(8);
buf.write(&[1, 2, 3, 4, 5, 6, 7]).unwrap();
buf.mark();
buf.write(&[8]).unwrap();
buf.reset();
let mut buf2 = [0; 8];
buf.read(&mut buf2).unwrap();
assert_eq!(buf2, [1, 2, 3, 4, 5, 6, 7, 8]);
}
#[test]
// Test that "RingBuf::reset" does not reset the length of a
// full buffer to zero.
fn test_reset_full() {
use bytes::traits::MutBuf;
use std::io::Write;
let mut buf = RingBuf::new(8);
buf.write(&[1, 2, 3, 4, 5, 6, 7, 8]).unwrap();
assert_eq!(MutBuf::remaining(&buf), 0);
buf.mark();
buf.reset();
assert_eq!(MutBuf::remaining(&buf), 0);
}
#[test]
// Test that "RingBuf::clear" does the full reset
fn test_clear() {
use bytes::traits::{Buf, MutBuf};
use std::io::Write;
let mut buf = RingBuf::new(8);
buf.write(&[0; 8]).unwrap();
assert_eq!(MutBuf::remaining(&buf), 0);
assert_eq!(Buf::remaining(&buf), 8);
buf.clear();
assert_eq!(MutBuf::remaining(&buf), 8);
assert_eq!(Buf::remaining(&buf), 0);
}
-107
View File
@@ -1,107 +0,0 @@
use bytes::Rope;
use bytes::traits::*;
use super::gen_bytes;
const TEST_BYTES_1: &'static [u8] =
b"dblm4ng7jp4v9rdn1w6hhssmluoqrrrqj59rccl9
nkv2tm1t2da4jyku51ge7f8hv581gkki8lekmf5f
1l44whp4aiwbvhkziw02292on4noyvuwjzsloqyc
5n0iyn4l6o6tgjhlek00mynfzb1wgcwj4mqp6zdr
3625yy7rj7xuisal7b1a7xgq271abvt5ssxuj39v
njtetokxxrgxzp7ik9adnypkmmcn4270yv9l46m7
9mu2zmqmkxdmgia210vkdytb7ywfcyt2bvcsg9eq
5yqizxl6888zrksvaxhzs2v355jxu8gr21m33t83
qvoian1ra7c6pvxabshgngldxa408p18l1fdet2h";
const TEST_BYTES_2: &'static [u8] =
b"jmh14t79mllzj1ohxfj6fun7idwbks8oh35f83g6
ryaowe86mmou5t1xa91uyg8e95wcu5mje1mswien
tt4clgj029cw0pyuvfbvsgzdg1x7sr9qsjkf2b1t
h43smgp1ea22lph17f78cel0cc2kjoht5281xuy8
0ex9uaqwj4330jrp30stsk15j9bpqezu3w78ktit
ev5g6xsngr35q7pemdm9hihf0ebrw5fbwhm530lo
e0zyj1bm7yfyk7f2i45jhr3wu3bvb4hj8jve6db0
iewmr9weecaon9vdnqo5hen9iaiox5vsaxuo461m
8336ugp20u4sfky3kfawr0ome1tiqyx8chkerrjh
a95s0gypcsgo9jqxasqkoj08t4uq5moxmay5plg5
tlh6f9omhn0ezvi0w2n8hx7n6qk7rn1s3mjpnpl6
hvilp8awaa4tvsis66q4e5b3xwy2z1h2klpa87h7";
#[test]
pub fn test_rope_round_trip() {
let rope = Rope::from_slice(b"zomg");
assert_eq!(4, rope.len());
let mut dst = vec![];
rope.buf().read(&mut dst).unwrap();
assert_eq!(b"zomg", &dst[..]);
}
#[test]
pub fn test_rope_slice() {
let mut dst = vec![];
let bytes = Rope::from_slice(TEST_BYTES_1);
assert_eq!(TEST_BYTES_1.len(), bytes.len());
bytes.buf().read(&mut dst).unwrap();
assert_eq!(dst, TEST_BYTES_1);
let left = bytes.slice_to(250);
assert_eq!(250, left.len());
left.buf().read(&mut dst).unwrap();
assert_eq!(dst, &TEST_BYTES_1[..250]);
let right = bytes.slice_from(250);
assert_eq!(TEST_BYTES_1.len() - 250, right.len());
right.buf().read(&mut dst).unwrap();
assert_eq!(dst, &TEST_BYTES_1[250..]);
}
#[test]
pub fn test_rope_concat_two_byte_str() {
let mut dst = vec![];
let left = Rope::from_slice(TEST_BYTES_1);
let right = Rope::from_slice(TEST_BYTES_2);
let both = left.concat(&right);
assert_eq!(both.len(), TEST_BYTES_1.len() + TEST_BYTES_2.len());
both.buf().read(&mut dst).unwrap();
let mut expected = Vec::new();
expected.extend(TEST_BYTES_1.iter().cloned());
expected.extend(TEST_BYTES_2.iter().cloned());
assert_eq!(dst, expected);
}
#[test]
#[ignore]
pub fn test_slice_parity() {
let bytes = gen_bytes(2048 * 1024);
let start = 512 * 1024 - 3333;
let end = 512 * 1024 + 7777;
let _ = Rope::from_slice(&bytes).slice(start, end);
// stuff
}
#[test]
pub fn test_rope_equality() {
let a = &b"Mary had a little lamb, its fleece was white as snow; ".to_bytes()
.concat(&b"And everywhere that Mary went, the lamb was sure to go.".to_bytes());
let b = &b"Mary had a little lamb, ".to_bytes()
.concat(&b"its fleece was white as snow; ".to_bytes())
.concat(
&b"And everywhere that Mary went, ".to_bytes()
.concat(&b"the lamb was sure to go.".to_bytes()));
assert_eq!(a, b);
}
-33
View File
@@ -1,33 +0,0 @@
use bytes::SeqByteStr;
use bytes::traits::*;
use super::gen_bytes;
#[test]
pub fn test_slice_round_trip() {
let mut dst = vec![];
let src = gen_bytes(2000);
let s = SeqByteStr::from_slice(&src);
assert_eq!(2000, s.len());
s.buf().read(&mut dst).unwrap();
assert_eq!(dst, src);
}
#[test]
pub fn test_index() {
let src = gen_bytes(2000);
let s = SeqByteStr::from_slice(&src);
for i in 0..2000 {
assert_eq!(src[i], s[i]);
}
}
#[test]
#[should_panic]
pub fn test_index_out_of_range() {
let s = SeqByteStr::from_slice(&gen_bytes(2000));
let _ = s[2001];
}
-33
View File
@@ -1,33 +0,0 @@
use bytes::SmallByteStr;
use bytes::traits::*;
use super::gen_bytes;
#[test]
pub fn test_slice_round_trip() {
let mut dst = vec![];
let src = gen_bytes(3);
let s = SmallByteStr::from_slice(&src).unwrap();
assert_eq!(3, s.len());
s.buf().read(&mut dst).unwrap();
assert_eq!(dst, src);
}
#[test]
pub fn test_index() {
let src = gen_bytes(3);
let s = SmallByteStr::from_slice(&src).unwrap();
for i in 0..3 {
assert_eq!(src[i], s[i]);
}
}
#[test]
#[should_panic]
pub fn test_index_out_of_range() {
let s = SmallByteStr::from_slice(&gen_bytes(3)).unwrap();
let _ = s[2001];
}
+48
View File
@@ -0,0 +1,48 @@
extern crate bytes;
extern crate byteorder;
use bytes::Buf;
use std::io::Cursor;
#[test]
fn test_fresh_cursor_vec() {
let mut buf = Cursor::new(b"hello".to_vec());
assert_eq!(buf.remaining(), 5);
assert_eq!(buf.bytes(), b"hello");
buf.advance(2);
assert_eq!(buf.remaining(), 3);
assert_eq!(buf.bytes(), b"llo");
buf.advance(3);
assert_eq!(buf.remaining(), 0);
assert_eq!(buf.bytes(), b"");
buf.advance(1);
assert_eq!(buf.remaining(), 0);
assert_eq!(buf.bytes(), b"");
}
#[test]
fn test_get_u8() {
let mut buf = Cursor::new(b"\x21zomg");
assert_eq!(0x21, buf.get_u8());
}
#[test]
fn test_get_u16() {
let buf = b"\x21\x54zomg";
assert_eq!(0x2154, Cursor::new(buf).get_u16::<byteorder::BigEndian>());
assert_eq!(0x5421, Cursor::new(buf).get_u16::<byteorder::LittleEndian>());
}
#[test]
#[should_panic]
fn test_get_u16_buffer_underflow() {
let mut buf = Cursor::new(b"\x21");
buf.get_u16::<byteorder::BigEndian>();
}
+295
View File
@@ -0,0 +1,295 @@
extern crate bytes;
use bytes::{Bytes, BytesMut, BufMut};
const LONG: &'static [u8] = b"mary had a little lamb, little lamb, little lamb";
const SHORT: &'static [u8] = b"hello world";
fn inline_cap() -> usize {
use std::mem;
4 * mem::size_of::<usize>() - 1
}
fn is_sync<T: Sync>() {}
fn is_send<T: Send>() {}
#[test]
fn test_bounds() {
is_sync::<Bytes>();
is_sync::<BytesMut>();
is_send::<Bytes>();
is_send::<BytesMut>();
}
#[test]
fn from_slice() {
let a = Bytes::from(&b"abcdefgh"[..]);
assert_eq!(a, b"abcdefgh"[..]);
assert_eq!(a, &b"abcdefgh"[..]);
assert_eq!(a, Vec::from(&b"abcdefgh"[..]));
assert_eq!(b"abcdefgh"[..], a);
assert_eq!(&b"abcdefgh"[..], a);
assert_eq!(Vec::from(&b"abcdefgh"[..]), a);
let a = BytesMut::from(&b"abcdefgh"[..]);
assert_eq!(a, b"abcdefgh"[..]);
assert_eq!(a, &b"abcdefgh"[..]);
assert_eq!(a, Vec::from(&b"abcdefgh"[..]));
assert_eq!(b"abcdefgh"[..], a);
assert_eq!(&b"abcdefgh"[..], a);
assert_eq!(Vec::from(&b"abcdefgh"[..]), a);
}
#[test]
fn fmt() {
let a = format!("{:?}", Bytes::from(&b"abcdefg"[..]));
let b = format!("{:?}", b"abcdefg");
assert_eq!(a, b);
let a = format!("{:?}", BytesMut::from(&b"abcdefg"[..]));
assert_eq!(a, b);
}
#[test]
fn len() {
let a = Bytes::from(&b"abcdefg"[..]);
assert_eq!(a.len(), 7);
let a = BytesMut::from(&b"abcdefg"[..]);
assert_eq!(a.len(), 7);
let a = Bytes::from(&b""[..]);
assert!(a.is_empty());
let a = BytesMut::from(&b""[..]);
assert!(a.is_empty());
}
#[test]
fn index() {
let a = Bytes::from(&b"hello world"[..]);
assert_eq!(a[0..5], *b"hello");
}
#[test]
fn slice() {
let a = Bytes::from(&b"hello world"[..]);
let b = a.slice(3, 5);
assert_eq!(b, b"lo"[..]);
let b = a.slice_to(5);
assert_eq!(b, b"hello"[..]);
let b = a.slice_from(3);
assert_eq!(b, b"lo world"[..]);
}
#[test]
#[should_panic]
fn slice_oob_1() {
let a = Bytes::from(&b"hello world"[..]);
a.slice(5, inline_cap() + 1);
}
#[test]
#[should_panic]
fn slice_oob_2() {
let a = Bytes::from(&b"hello world"[..]);
a.slice(inline_cap() + 1, inline_cap() + 5);
}
#[test]
fn split_off() {
let mut hello = Bytes::from(&b"helloworld"[..]);
let world = hello.split_off(5);
assert_eq!(hello, &b"hello"[..]);
assert_eq!(world, &b"world"[..]);
let mut hello = BytesMut::from(&b"helloworld"[..]);
let world = hello.split_off(5);
assert_eq!(hello, &b"hello"[..]);
assert_eq!(world, &b"world"[..]);
}
#[test]
#[should_panic]
fn split_off_oob() {
let mut hello = Bytes::from(&b"helloworld"[..]);
hello.split_off(inline_cap() + 1);
}
#[test]
fn split_off_uninitialized() {
let mut bytes = BytesMut::with_capacity(1024);
let other = bytes.split_off(128);
assert_eq!(bytes.len(), 0);
assert_eq!(bytes.capacity(), 128);
assert_eq!(other.len(), 0);
assert_eq!(other.capacity(), 896);
}
#[test]
fn drain_to_1() {
// Inline
let mut a = Bytes::from(SHORT);
let b = a.drain_to(4);
assert_eq!(SHORT[4..], a);
assert_eq!(SHORT[..4], b);
// Allocated
let mut a = Bytes::from(LONG);
let b = a.drain_to(4);
assert_eq!(LONG[4..], a);
assert_eq!(LONG[..4], b);
let mut a = Bytes::from(LONG);
let b = a.drain_to(30);
assert_eq!(LONG[30..], a);
assert_eq!(LONG[..30], b);
}
#[test]
fn drain_to_2() {
let mut a = Bytes::from(LONG);
assert_eq!(LONG, a);
let b = a.drain_to(1);
assert_eq!(LONG[1..], a);
drop(b);
}
#[test]
#[should_panic]
fn drain_to_oob() {
let mut hello = Bytes::from(&b"helloworld"[..]);
hello.drain_to(inline_cap() + 1);
}
#[test]
#[should_panic]
fn drain_to_oob_mut() {
let mut hello = BytesMut::from(&b"helloworld"[..]);
hello.drain_to(inline_cap() + 1);
}
#[test]
fn drain_to_uninitialized() {
let mut bytes = BytesMut::with_capacity(1024);
let other = bytes.drain_to(128);
assert_eq!(bytes.len(), 0);
assert_eq!(bytes.capacity(), 896);
assert_eq!(other.len(), 0);
assert_eq!(other.capacity(), 128);
}
#[test]
fn fns_defined_for_bytes_mut() {
let mut bytes = BytesMut::from(&b"hello world"[..]);
bytes.as_ptr();
bytes.as_mut_ptr();
// Iterator
let v: Vec<u8> = bytes.iter().map(|b| *b).collect();
assert_eq!(&v[..], bytes);
}
#[test]
fn reserve_convert() {
// Inline -> Vec
let mut bytes = BytesMut::with_capacity(8);
bytes.put("hello");
bytes.reserve(40);
assert_eq!(bytes.capacity(), 45);
assert_eq!(bytes, "hello");
// Inline -> Inline
let mut bytes = BytesMut::with_capacity(inline_cap());
bytes.put("abcdefghijkl");
let a = bytes.drain_to(10);
bytes.reserve(inline_cap() - 3);
assert_eq!(inline_cap(), bytes.capacity());
assert_eq!(bytes, "kl");
assert_eq!(a, "abcdefghij");
// Vec -> Vec
let mut bytes = BytesMut::from(LONG);
bytes.reserve(64);
assert_eq!(bytes.capacity(), LONG.len() + 64);
// Arc -> Vec
let mut bytes = BytesMut::from(LONG);
let a = bytes.drain_to(30);
bytes.reserve(128);
assert_eq!(bytes.capacity(), (bytes.len() + 128).next_power_of_two());
drop(a);
}
#[test]
fn reserve_growth() {
let mut bytes = BytesMut::with_capacity(64);
bytes.put("hello world");
let _ = bytes.drain();
bytes.reserve(65);
assert_eq!(bytes.capacity(), 128);
}
#[test]
fn inline_storage() {
let mut bytes = BytesMut::with_capacity(inline_cap());
let zero = [0u8; 64];
bytes.put(&zero[0..inline_cap()]);
assert_eq!(*bytes, zero[0..inline_cap()]);
}
#[test]
fn stress() {
// Tests promoting a buffer from a vec -> shared in a concurrent situation
use std::sync::{Arc, Barrier};
use std::thread;
const THREADS: usize = 8;
const ITERS: usize = 1_000;
for i in 0..ITERS {
let data = [i as u8; 256];
let buf = Arc::new(BytesMut::from(&data[..]));
let barrier = Arc::new(Barrier::new(THREADS));
let mut joins = Vec::with_capacity(THREADS);
for _ in 0..THREADS {
let c = barrier.clone();
let buf = buf.clone();
joins.push(thread::spawn(move || {
c.wait();
let _buf = buf.clone();
}));
}
for th in joins {
th.join().unwrap();
}
assert_eq!(*buf, data[..]);
}
}
+58
View File
@@ -0,0 +1,58 @@
extern crate bytes;
extern crate byteorder;
use bytes::{BufMut, BytesMut};
use std::usize;
use std::fmt::Write;
#[test]
fn test_vec_as_mut_buf() {
let mut buf = Vec::with_capacity(64);
assert_eq!(buf.remaining_mut(), usize::MAX);
unsafe {
assert!(buf.bytes_mut().len() >= 64);
}
buf.put(&b"zomg"[..]);
assert_eq!(&buf, b"zomg");
assert_eq!(buf.remaining_mut(), usize::MAX - 4);
assert_eq!(buf.capacity(), 64);
for _ in 0..16 {
buf.put(&b"zomg"[..]);
}
assert_eq!(buf.len(), 68);
}
#[test]
fn test_put_u8() {
let mut buf = Vec::with_capacity(8);
buf.put::<u8>(33);
assert_eq!(b"\x21", &buf[..]);
}
#[test]
fn test_put_u16() {
let mut buf = Vec::with_capacity(8);
buf.put_u16::<byteorder::BigEndian>(8532);
assert_eq!(b"\x21\x54", &buf[..]);
buf.clear();
buf.put_u16::<byteorder::LittleEndian>(8532);
assert_eq!(b"\x54\x21", &buf[..]);
}
#[test]
fn test_clone() {
let mut buf = BytesMut::with_capacity(100);
buf.write_str("this is a test").unwrap();
let buf2 = buf.clone();
buf.write_str(" of our emergecy broadcast system").unwrap();
assert!(buf != buf2);
}