quick-protobuf-0.8.1/.cargo_vcs_info.json0000644000000001540000000000100140140ustar { "git": { "sha1": "3db41b4b6b42e69cb20ee0857f6898f053ed8ee7" }, "path_in_vcs": "quick-protobuf" }quick-protobuf-0.8.1/Cargo.lock0000644000000110150000000000100117650ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "glob" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "itoa" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "once_cell" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" [[package]] name = "proc-macro2" version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" dependencies = [ "unicode-ident", ] [[package]] name = "quick-protobuf" version = "0.8.1" dependencies = [ "byteorder", "lazy_static", "trybuild", ] [[package]] name = "quote" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] [[package]] name = "ryu" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] name = "serde" version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" [[package]] name = "serde_derive" version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "syn" version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "termcolor" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ "winapi-util", ] [[package]] name = "toml" version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ "serde", ] [[package]] name = "trybuild" version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea496675d71016e9bc76aa42d87f16aefd95447cc5818e671e12b2d7e269075d" dependencies = [ "glob", "once_cell", "serde", "serde_derive", "serde_json", "termcolor", "toml", ] [[package]] name = "unicode-ident" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" quick-protobuf-0.8.1/Cargo.toml0000644000000020330000000000100120100ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "quick-protobuf" version = "0.8.1" authors = ["Johann Tuffe "] description = "A pure Rust protobuf (de)serializer. Quick." documentation = "https://docs.rs/quick-protobuf" readme = "README.md" keywords = [ "protobuf", "parser", ] license = "MIT" repository = "https://github.com/tafia/quick-protobuf" [dependencies.byteorder] version = "1.3.4" default-features = false [dev-dependencies.lazy_static] version = "1.4.0" [dev-dependencies.trybuild] version = "1.0.71" [features] default = ["std"] std = ["byteorder/std"] quick-protobuf-0.8.1/Cargo.toml.orig000064400000000000000000000010411046102023000154670ustar 00000000000000[package] name = "quick-protobuf" description = "A pure Rust protobuf (de)serializer. Quick." version = "0.8.1" authors = ["Johann Tuffe "] keywords = ["protobuf", "parser"] license = "MIT" readme = "README.md" documentation = "https://docs.rs/quick-protobuf" repository = "https://github.com/tafia/quick-protobuf" edition = "2018" [dependencies] byteorder = { version = "1.3.4", default-features = false } [dev-dependencies] lazy_static = "1.4.0" trybuild = "1.0.71" [features] default = ["std"] std = ["byteorder/std"] quick-protobuf-0.8.1/README.md000064400000000000000000000155231046102023000140710ustar 00000000000000# quick-protobuf A pure Rust library to serialize/deserialize [protobuf](https://developers.google.com/protocol-buffers) files. [Documentation](https://docs.rs/quick-protobuf) ## Description This crate intends to provide a simple yet fast (minimal allocations) protobuf parser implementation. In general, you should probably NOT need to use this crate directly, else, you should use the modules automatically generated by [pb-rs](https://crates.io/crates/pb-rs) tool. ## Example - 1. Install **pb-rs** binary to convert your proto file into a **quick-protobuf** compatible source code ```sh cargo install pb-rs pb-rs /path/to/your/protobuf/file.proto # will generate a # /path/to/your/protobuf/file.rs ``` - 2. Add a dependency to quick-protobuf ```toml # Cargo.toml [dependencies] quick-protobuf = "0.6.2" ``` - 3. Have fun ```rust extern crate quick_protobuf; mod foo_bar; // (see 1.) use quick_protobuf::Reader; // We will suppose here that Foo and Bar are two messages defined in the .proto file // and converted into rust structs // // FooBar is the root message defined like this: // message FooBar { // repeated Foo foos = 1; // repeated Bar bars = 2; // } // FooBar is a message generated from a proto file // in parcicular it contains a `from_reader` function use foo_bar::FooBar; use quick_protobuf::{MessageRead, BytesReader}; fn main() { // bytes is a buffer on the data we want to deserialize // typically bytes is read from a `Read`: // r.read_to_end(&mut bytes).expect("cannot read bytes"); let bytes: Vec = ...; // In the most simple form, we want to deserialize from a `&[u8]` let foobar = deserialize_from_slice(&bytes).expect("Cannot convert into a `FooBar`"); // ... // ... // Alternatively, we can go lower level and work with a `BytesReader` // It gives more control of the bytes we are reading let mut reader = BytesReader::from_bytes(&bytes); // now using the generated module decoding is as easy as: let foobar = FooBar::from_reader(&mut reader, &bytes).expect("Cannot read FooBar"); // if instead the buffer contains a length delimited stream of message we could use: // while !r.is_eof() { // let foobar: FooBar = r.read_message(&bytes).expect(...); // ... // } println!("Found {} foos and {} bars", foobar.foos.len(), foobar.bars.len()); // Similarly, if we want to serialize the message you can use a `Writer` or use // `serialize_into_vec` let vec = serialize_into_vec(&foobar).expect("Cannot serialize `foobar`"); // ... or for more control (more than one message) let mut buf = Vec::new(); let mut writer = Writer::new(&mut buf); writer.write_message(&foobar).expect("Cannot write `foobar`); } ``` ## Message <-> struct The best way to check for all kind of generated code is to look for the codegen_example data: - definition: [data_types.proto](examples/pb_rs/data_types.proto) - generated code: [data_types.rs](examples/pb_rs/data_types.rs) #### Proto definition ``` enum FooEnum { FIRST_VALUE = 1; SECOND_VALUE = 2; } message BarMessage { required int32 b_required_int32 = 1; } message FooMessage { optional int32 f_int32 = 1; optional int64 f_int64 = 2; optional uint32 f_uint32 = 3; optional uint64 f_uint64 = 4; optional sint32 f_sint32 = 5; optional sint64 f_sint64 = 6; optional bool f_bool = 7; optional FooEnum f_FooEnum = 8; optional fixed64 f_fixed64 = 9; optional sfixed64 f_sfixed64 = 10; optional fixed32 f_fixed32 = 11; optional sfixed32 f_sfixed32 = 12; optional double f_double = 13; optional float f_float = 14; optional bytes f_bytes = 15; optional string f_string = 16; optional FooMessage f_self_message = 17; optional BarMessage f_bar_message = 18; repeated int32 f_repeated_int32 = 19; repeated int32 f_repeated_packed_int32 = 20 [ packed = true ]; } ``` #### Generated structs ```rust #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum FooEnum { FIRST_VALUE = 1, SECOND_VALUE = 2, } #[derive(Debug, Default, PartialEq, Clone)] pub struct BarMessage { // all fields are owned: no lifetime parameter pub b_required_int32: i32, } #[derive(Debug, Default, PartialEq, Clone)] pub struct FooMessage<'a> { // has borrowed fields: lifetime parameter pub f_int32: Option, pub f_int64: Option, pub f_uint32: Option, pub f_uint64: Option, pub f_sint32: Option, pub f_sint64: Option, pub f_bool: Option, pub f_FooEnum: Option, pub f_fixed64: Option, pub f_sfixed64: Option, pub f_fixed32: Option, pub f_sfixed32: Option, pub f_double: Option, pub f_float: Option, pub f_bytes: Option>, // bytes -> Cow<[u8]> pub f_string: Option> // string -> Cow pub f_self_message: Option>>, // reference cycle -> Boxed message pub f_bar_message: Option, pub f_repeated_int32: Vec, // repeated: Vec pub f_repeated_packed_int32: Vec, // repeated packed: Vec } ``` ### Leverage rust module system #### Nested Messages ``` message A { message B { // ... } } ``` As rust does not allow a struct and a module to share the same name, we use `mod_Name` for the nested messages. ```rust pub struct A { //... } pub mod mod_A { pub struct B { // ... } } ``` #### Package ``` package a.b; ``` Here we could have used the same name, but for consistency with nested messages, modules are prefixed with `mod_` as well. ```rust pub mod mod_a { pub mod mod_b { // ... } } ``` ## Why not rust-protobuf This library is an alternative to the widely used [rust-protobuf](https://github.com/stepancheg/rust-protobuf). #### Pros / Cons - Pros - [Much faster](benches/rust-protobuf), in particular when working with string, bytes and repeated packed fixed size fields (no extra allocation) - No need to install `protoc` on your machine - No trait objects: faster/simpler parser - Very simple generated modules (~10x smaller) so you can easily understand what is happening - Cons - Less popular - most rust-protobuf tests have been migrated here (see [v2](tests/rust_protobuf/v2/mod.rs) and [v3](tests/rust_protobuf/v3/mod.rs)) - quick-protobuf is being used by many people now and is very reliable - [some missing functionalities](https://github.com/tafia/quick-protobuf/issues/12) - Not a drop-in replacement of rust-protobuf - everything being explicit you have to handle more things yourself (e.g. `Option` unwrapping, `Cow` management) ## Contribution Any help is welcomed! (Pull requests of course, bug report, missing functionality etc...) ## Licence MIT quick-protobuf-0.8.1/benches/benches.rs000064400000000000000000000024351046102023000161740ustar 00000000000000#![feature(test)] extern crate quick_protobuf; extern crate test; #[macro_use] extern crate lazy_static; use test::{black_box, Bencher}; use quick_protobuf::{BytesReader, Writer}; const LEN: i32 = 10_000; lazy_static! { static ref BUFFER: Vec = { let mut buf = Vec::new(); { let mut writer = Writer::new(&mut buf); for i in 0..LEN { writer.write_int32(i).unwrap(); } } buf }; } #[bench] fn read_varint32(b: &mut Bencher) { b.iter(|| { let mut reader = BytesReader::from_bytes(&BUFFER); for _ in 0..LEN { let _ = black_box(reader.read_varint32(&BUFFER).unwrap()); } assert!(reader.is_eof()); }) } #[bench] fn read_varint64(b: &mut Bencher) { b.iter(|| { let mut reader = BytesReader::from_bytes(&BUFFER); for _ in 0..LEN { let _ = black_box(reader.read_varint64(&BUFFER).unwrap()); } assert!(reader.is_eof()); }) } #[bench] fn read_varint64_and_is_eof(b: &mut Bencher) { b.iter(|| { let mut reader = BytesReader::from_bytes(&BUFFER); for _ in 0..LEN { assert!(!reader.is_eof()); let _ = black_box(reader.read_varint64(&BUFFER).unwrap()); } }) } quick-protobuf-0.8.1/benches/perftest.rs000064400000000000000000000161061046102023000164210ustar 00000000000000#![feature(test)] extern crate quick_protobuf; extern crate test; mod perftest_data; use std::borrow::Cow; use std::cmp::min; use crate::perftest_data::*; use quick_protobuf::message::MessageWrite; use quick_protobuf::MessageRead; use quick_protobuf::{BytesReader, Reader, Writer}; use test::{black_box, Bencher}; #[bench] fn read_file(b: &mut Bencher) { let path = format!( "{}/benches/perftest_data/perftest_data.pbbin", env!("CARGO_MANIFEST_DIR") ); b.iter(|| { let mut reader = Reader::from_file(&path).unwrap(); reader.read(PerftestData::from_reader).unwrap().test1.len() }) } macro_rules! perfbench { ($gen:ident, $m:ident, $write:ident, $read:ident) => { #[bench] fn $write(b: &mut Bencher) { let v = $gen(); b.iter(|| { let mut buf = black_box(Vec::new()); let mut w = Writer::new(&mut buf); for i in &v { i.write_message(&mut w).unwrap(); } }) } #[bench] fn $read(b: &mut Bencher) { let v = $gen(); let mut buf = Vec::new(); { let mut w = Writer::new(&mut buf); for i in &v { i.write_message(&mut w).unwrap(); } } b.iter(|| { let mut r = BytesReader::from_bytes(&buf); while !r.is_eof() { let _ = black_box($m::from_reader(&mut r, &buf).unwrap()); } }) } }; } fn generate_test1() -> Vec { (0..500) .map(|i| Test1 { value: Some(i) }) .chain((0..200).map(|_| Test1 { value: None })) .collect() } perfbench!(generate_test1, Test1, write_test1, read_test1); fn generate_repeated_bool() -> Vec { (1..10) .map(|j| TestRepeatedBool { values: (0..100).map(|i| i % j == 0).collect(), }) .collect() } perfbench!( generate_repeated_bool, TestRepeatedBool, write_repeated_bool, read_repeated_bool ); fn generate_repeated_packed_int32() -> Vec { (1..40) .map(|j| TestRepeatedPackedInt32 { values: (0..100).map(|i| i * j).collect(), }) .collect() } perfbench!( generate_repeated_packed_int32, TestRepeatedPackedInt32, write_repeated_packed_int32, read_repeated_packed_int32 ); fn generate_repeated_packed_float() -> Vec> { (1..40) .map(|j| TestRepeatedPackedFloat { values: Cow::Owned((0..100).map(|i| (i * j) as f32).collect()), }) .collect() } perfbench!( generate_repeated_packed_float, TestRepeatedPackedFloat, write_repeated_packed_float, read_repeated_packed_float ); fn generate_repeated_messages() -> Vec { let mut messages = Vec::new(); messages.push(TestRepeatedMessages { messages1: vec![], messages2: vec![], messages3: vec![], }); for _ in 0..5 { let i1 = min(messages.len() % 3, messages.len() - 1); let i2 = min(messages.len() % 6, messages.len() - 1); let i3 = min(messages.len() % 9, messages.len() - 1); let m1 = messages[i1].clone(); let m2 = messages[i2].clone(); let m3 = messages[i3].clone(); messages.push(TestRepeatedMessages { messages1: vec![m1.clone()], messages2: vec![m1.clone(), m2.clone()], messages3: vec![m1.clone(), m2.clone(), m3.clone()], }); } messages } perfbench!( generate_repeated_messages, TestRepeatedMessages, write_repeated_messages, read_repeated_messages ); fn generate_optional_messages() -> Vec { let mut messages = Vec::new(); messages.push(TestOptionalMessages { message1: None, message2: None, message3: None, }); for _ in 0..10 { let i1 = min(messages.len() % 3, messages.len() - 1); let i2 = min(messages.len() % 6, messages.len() - 1); let i3 = min(messages.len() % 9, messages.len() - 1); let m1 = messages[i1].clone(); let m2 = messages[i2].clone(); let m3 = messages[i3].clone(); messages.push(TestOptionalMessages { message1: Some(Box::new(m1.clone())), message2: Some(Box::new(m2.clone())), message3: Some(Box::new(m3.clone())), }); } messages } perfbench!( generate_optional_messages, TestOptionalMessages, write_optional_messages, read_optional_messages ); fn generate_strings() -> Vec> { let mut s = "hello world from quick-protobuf!!!" .split(' ') .cycle() .map(|s| Cow::Borrowed(s)); (1..100) .map(|_| TestStrings { s1: s.by_ref().next(), s2: s.by_ref().next(), s3: s.by_ref().next(), }) .collect() } perfbench!(generate_strings, TestStrings, write_strings, read_strings); fn generate_small_bytes() -> Vec> { let mut s = "hello world from quick-protobuf!!!" .split(' ') .cycle() .map(|s| Cow::Borrowed(s.as_bytes())); (1..800) .map(|_| TestBytes { b1: s.by_ref().next(), }) .collect() } perfbench!( generate_small_bytes, TestBytes, write_small_bytes, read_small_bytes ); fn generate_large_bytes() -> Vec> { let mut s = "hello world from quick-protobuf!!!" .split(' ') .cycle() .map(|s| s.as_bytes()); (1..30) .map(|_| TestBytes { b1: Some(Cow::Owned(s.by_ref().take(500).fold( Vec::new(), |mut cur, nxt| { cur.extend_from_slice(nxt); cur }, ))), }) .collect() } perfbench!( generate_large_bytes, TestBytes, write_large_bytes, read_large_bytes ); fn generate_map() -> Vec> { let mut s = "hello world from quick-protobuf!!!".split(' ').cycle(); (1..30) .map(|_| TestMap { value: s .by_ref() .take(500) .map(|s| (Cow::Owned(s.to_string()), s.len() as u32)) .collect(), }) .collect() } perfbench!(generate_map, TestMap, write_map, read_map); fn generate_all() -> Vec> { vec![PerftestData { test1: generate_test1(), test_repeated_bool: generate_repeated_bool(), test_repeated_messages: generate_repeated_messages(), test_optional_messages: generate_optional_messages(), test_strings: generate_strings(), test_repeated_packed_int32: generate_repeated_packed_int32(), test_repeated_packed_float: generate_repeated_packed_float(), test_small_bytearrays: generate_small_bytes(), test_large_bytearrays: generate_large_bytes(), test_map: generate_map(), }] } perfbench!(generate_all, PerftestData, write_all, read_all); quick-protobuf-0.8.1/benches/perftest_data/mod.rs000064400000000000000000000001131046102023000201600ustar 00000000000000mod perftest_data; // reexport everything pub use self::perftest_data::*; quick-protobuf-0.8.1/benches/perftest_data/perftest_data.pbbin000064400000000000000000003123741046102023000227130ustar 00000000000000  è ”M       " "  " "   * aaaaa*5hi there)long long long long long long long string* firstsecondthird22  2  ßî(à2  ßî(àF:ƒ €xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxB„  € XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXquick-protobuf-0.8.1/benches/perftest_data/perftest_data.proto000064400000000000000000000025751046102023000227630ustar 00000000000000message Test1 { optional int32 value = 1; } message TestRepeatedBool { repeated bool values = 1; } message TestRepeatedPackedInt32 { repeated int32 values = 1 [ packed = true ]; } message TestRepeatedPackedFloat { repeated float values = 1 [ packed = true ]; } message TestRepeatedMessages { repeated TestRepeatedMessages messages1 = 1; repeated TestRepeatedMessages messages2 = 2; repeated TestRepeatedMessages messages3 = 3; } message TestOptionalMessages { optional TestOptionalMessages message1 = 1; optional TestOptionalMessages message2 = 2; optional TestOptionalMessages message3 = 3; } message TestStrings { optional string s1 = 1; optional string s2 = 2; optional string s3 = 3; } message TestBytes { optional bytes b1 = 1; } message TestMap { map value = 1; } message PerftestData { repeated Test1 test1 = 1; repeated TestRepeatedBool test_repeated_bool = 2; repeated TestRepeatedMessages test_repeated_messages = 3; repeated TestOptionalMessages test_optional_messages = 4; repeated TestStrings test_strings = 5; repeated TestRepeatedPackedInt32 test_repeated_packed_int32 = 6; repeated TestRepeatedPackedFloat test_repeated_packed_float = 7; repeated TestBytes test_small_bytearrays = 8; repeated TestBytes test_large_bytearrays = 9; repeated TestMap test_map = 10; } quick-protobuf-0.8.1/examples/pb_rs/a/b.rs000064400000000000000000000025341046102023000165420ustar 00000000000000// Automatically generated rust module for 'data_types_import.proto' file #![allow(non_snake_case)] #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(unused_imports)] #![allow(unknown_lints)] #![allow(clippy::all)] #![cfg_attr(rustfmt, rustfmt_skip)] use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result}; use quick_protobuf::sizeofs::*; use super::super::*; #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] pub struct ImportedMessage { pub i: Option, } impl<'a> MessageRead<'a> for ImportedMessage { fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { Ok(8) => msg.i = Some(r.read_bool(bytes)?), Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } } Ok(msg) } } impl MessageWrite for ImportedMessage { fn get_size(&self) -> usize { 0 + self.i.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) } fn write_message(&self, w: &mut Writer) -> Result<()> { if let Some(ref s) = self.i { w.write_with_tag(8, |w| w.write_bool(*s))?; } Ok(()) } } quick-protobuf-0.8.1/examples/pb_rs/a/mod.rs000064400000000000000000000000551046102023000170740ustar 00000000000000// Automatically generated mod.rs pub mod b; quick-protobuf-0.8.1/examples/pb_rs/data_types.proto000064400000000000000000000032231046102023000207510ustar 00000000000000import "data_types_import.proto"; enum FooEnum { FIRST_VALUE = 1; SECOND_VALUE = 2; } message BarMessage { required int32 b_required_int32 = 1; } message FooMessage { optional int32 f_int32 = 1; optional int64 f_int64 = 2; optional uint32 f_uint32 = 3; optional uint64 f_uint64 = 4; optional sint32 f_sint32 = 5; optional sint64 f_sint64 = 6 [default=4]; optional bool f_bool = 7 [default=true]; optional FooEnum f_FooEnum = 8; optional fixed64 f_fixed64 = 9; optional sfixed64 f_sfixed64 = 10; optional fixed32 f_fixed32 = 11 [default=0]; optional sfixed32 f_sfixed32 = 12; optional double f_double = 13; optional float f_float = 14; optional bytes f_bytes = 15; optional string f_string = 16; optional FooMessage f_self_message = 17; optional BarMessage f_bar_message = 18; repeated int32 f_repeated_int32 = 19; repeated int32 f_repeated_packed_int32 = 20 [ packed = true ]; repeated float f_repeated_packed_float = 21 [ packed = true ]; optional a.b.ImportedMessage f_imported = 22; optional BazMessage f_baz = 23; optional BazMessage.Nested f_nested = 24; optional BazMessage.Nested.NestedEnum f_nested_enum = 25; map f_map = 26; oneof test_oneof { int32 f1 = 27; bool f2 = 28; string f3 = 29; } } message BazMessage { message Nested { message NestedMessage { required int32 f_nested = 1; } enum NestedEnum { Foo = 0; Bar = 1; Baz = 2; } required NestedMessage f_nested = 1; } optional Nested nested = 1; } quick-protobuf-0.8.1/examples/pb_rs/data_types.rs000064400000000000000000000367701046102023000202470ustar 00000000000000// Automatically generated rust module for 'data_types.proto' file #![allow(non_snake_case)] #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(unused_imports)] #![allow(unknown_lints)] #![allow(clippy::all)] #![cfg_attr(rustfmt, rustfmt_skip)] use std::borrow::Cow; use std::collections::HashMap; type KVMap = HashMap; use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result}; use quick_protobuf::sizeofs::*; use super::*; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum FooEnum { FIRST_VALUE = 1, SECOND_VALUE = 2, } impl Default for FooEnum { fn default() -> Self { FooEnum::FIRST_VALUE } } impl From for FooEnum { fn from(i: i32) -> Self { match i { 1 => FooEnum::FIRST_VALUE, 2 => FooEnum::SECOND_VALUE, _ => Self::default(), } } } impl<'a> From<&'a str> for FooEnum { fn from(s: &'a str) -> Self { match s { "FIRST_VALUE" => FooEnum::FIRST_VALUE, "SECOND_VALUE" => FooEnum::SECOND_VALUE, _ => Self::default(), } } } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] pub struct BarMessage { pub b_required_int32: i32, } impl<'a> MessageRead<'a> for BarMessage { fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { Ok(8) => msg.b_required_int32 = r.read_int32(bytes)?, Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } } Ok(msg) } } impl MessageWrite for BarMessage { fn get_size(&self) -> usize { 0 + 1 + sizeof_varint(*(&self.b_required_int32) as u64) } fn write_message(&self, w: &mut Writer) -> Result<()> { w.write_with_tag(8, |w| w.write_int32(*&self.b_required_int32))?; Ok(()) } } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] pub struct FooMessage<'a> { pub f_int32: Option, pub f_int64: Option, pub f_uint32: Option, pub f_uint64: Option, pub f_sint32: Option, pub f_sint64: i64, pub f_bool: bool, pub f_FooEnum: Option, pub f_fixed64: Option, pub f_sfixed64: Option, pub f_fixed32: u32, pub f_sfixed32: Option, pub f_double: Option, pub f_float: Option, pub f_bytes: Option>, pub f_string: Option>, pub f_self_message: Option>>, pub f_bar_message: Option, pub f_repeated_int32: Vec, pub f_repeated_packed_int32: Vec, pub f_repeated_packed_float: Cow<'a, [f32]>, pub f_imported: Option, pub f_baz: Option, pub f_nested: Option, pub f_nested_enum: Option, pub f_map: KVMap, i32>, pub test_oneof: mod_FooMessage::OneOftest_oneof<'a>, } impl<'a> MessageRead<'a> for FooMessage<'a> { fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { let mut msg = FooMessage { f_sint64: 4i64, f_bool: true, ..Self::default() }; while !r.is_eof() { match r.next_tag(bytes) { Ok(8) => msg.f_int32 = Some(r.read_int32(bytes)?), Ok(16) => msg.f_int64 = Some(r.read_int64(bytes)?), Ok(24) => msg.f_uint32 = Some(r.read_uint32(bytes)?), Ok(32) => msg.f_uint64 = Some(r.read_uint64(bytes)?), Ok(40) => msg.f_sint32 = Some(r.read_sint32(bytes)?), Ok(48) => msg.f_sint64 = r.read_sint64(bytes)?, Ok(56) => msg.f_bool = r.read_bool(bytes)?, Ok(64) => msg.f_FooEnum = Some(r.read_enum(bytes)?), Ok(73) => msg.f_fixed64 = Some(r.read_fixed64(bytes)?), Ok(81) => msg.f_sfixed64 = Some(r.read_sfixed64(bytes)?), Ok(93) => msg.f_fixed32 = r.read_fixed32(bytes)?, Ok(101) => msg.f_sfixed32 = Some(r.read_sfixed32(bytes)?), Ok(105) => msg.f_double = Some(r.read_double(bytes)?), Ok(117) => msg.f_float = Some(r.read_float(bytes)?), Ok(122) => msg.f_bytes = Some(r.read_bytes(bytes).map(Cow::Borrowed)?), Ok(130) => msg.f_string = Some(r.read_string(bytes).map(Cow::Borrowed)?), Ok(138) => msg.f_self_message = Some(Box::new(r.read_message::(bytes)?)), Ok(146) => msg.f_bar_message = Some(r.read_message::(bytes)?), Ok(152) => msg.f_repeated_int32.push(r.read_int32(bytes)?), Ok(162) => msg.f_repeated_packed_int32 = r.read_packed(bytes, |r, bytes| Ok(r.read_int32(bytes)?))?, Ok(170) => msg.f_repeated_packed_float = r.read_packed_fixed(bytes)?.into(), Ok(178) => msg.f_imported = Some(r.read_message::(bytes)?), Ok(186) => msg.f_baz = Some(r.read_message::(bytes)?), Ok(194) => msg.f_nested = Some(r.read_message::(bytes)?), Ok(200) => msg.f_nested_enum = Some(r.read_enum(bytes)?), Ok(210) => { let (key, value) = r.read_map(bytes, |r, bytes| Ok(r.read_string(bytes).map(Cow::Borrowed)?), |r, bytes| Ok(r.read_int32(bytes)?))?; msg.f_map.insert(key, value); } Ok(216) => msg.test_oneof = mod_FooMessage::OneOftest_oneof::f1(r.read_int32(bytes)?), Ok(224) => msg.test_oneof = mod_FooMessage::OneOftest_oneof::f2(r.read_bool(bytes)?), Ok(234) => msg.test_oneof = mod_FooMessage::OneOftest_oneof::f3(r.read_string(bytes).map(Cow::Borrowed)?), Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } } Ok(msg) } } impl<'a> MessageWrite for FooMessage<'a> { fn get_size(&self) -> usize { 0 + self.f_int32.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + self.f_int64.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + self.f_uint32.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + self.f_uint64.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + self.f_sint32.as_ref().map_or(0, |m| 1 + sizeof_sint32(*(m))) + if self.f_sint64 == 4i64 { 0 } else { 1 + sizeof_sint64(*(&self.f_sint64)) } + if self.f_bool == true { 0 } else { 1 + sizeof_varint(*(&self.f_bool) as u64) } + self.f_FooEnum.as_ref().map_or(0, |m| 1 + sizeof_varint(*(m) as u64)) + self.f_fixed64.as_ref().map_or(0, |_| 1 + 8) + self.f_sfixed64.as_ref().map_or(0, |_| 1 + 8) + if self.f_fixed32 == 0u32 { 0 } else { 1 + 4 } + self.f_sfixed32.as_ref().map_or(0, |_| 1 + 4) + self.f_double.as_ref().map_or(0, |_| 1 + 8) + self.f_float.as_ref().map_or(0, |_| 1 + 4) + self.f_bytes.as_ref().map_or(0, |m| 1 + sizeof_len((m).len())) + self.f_string.as_ref().map_or(0, |m| 2 + sizeof_len((m).len())) + self.f_self_message.as_ref().map_or(0, |m| 2 + sizeof_len((m).get_size())) + self.f_bar_message.as_ref().map_or(0, |m| 2 + sizeof_len((m).get_size())) + self.f_repeated_int32.iter().map(|s| 2 + sizeof_varint(*(s) as u64)).sum::() + if self.f_repeated_packed_int32.is_empty() { 0 } else { 2 + sizeof_len(self.f_repeated_packed_int32.iter().map(|s| sizeof_varint(*(s) as u64)).sum::()) } + if self.f_repeated_packed_float.is_empty() { 0 } else { 2 + sizeof_len(self.f_repeated_packed_float.len() * 4) } + self.f_imported.as_ref().map_or(0, |m| 2 + sizeof_len((m).get_size())) + self.f_baz.as_ref().map_or(0, |m| 2 + sizeof_len((m).get_size())) + self.f_nested.as_ref().map_or(0, |m| 2 + sizeof_len((m).get_size())) + self.f_nested_enum.as_ref().map_or(0, |m| 2 + sizeof_varint(*(m) as u64)) + self.f_map.iter().map(|(k, v)| 2 + sizeof_len(2 + sizeof_len((k).len()) + sizeof_varint(*(v) as u64))).sum::() + match self.test_oneof { mod_FooMessage::OneOftest_oneof::f1(ref m) => 2 + sizeof_varint(*(m) as u64), mod_FooMessage::OneOftest_oneof::f2(ref m) => 2 + sizeof_varint(*(m) as u64), mod_FooMessage::OneOftest_oneof::f3(ref m) => 2 + sizeof_len((m).len()), mod_FooMessage::OneOftest_oneof::None => 0, } } fn write_message(&self, w: &mut Writer) -> Result<()> { if let Some(ref s) = self.f_int32 { w.write_with_tag(8, |w| w.write_int32(*s))?; } if let Some(ref s) = self.f_int64 { w.write_with_tag(16, |w| w.write_int64(*s))?; } if let Some(ref s) = self.f_uint32 { w.write_with_tag(24, |w| w.write_uint32(*s))?; } if let Some(ref s) = self.f_uint64 { w.write_with_tag(32, |w| w.write_uint64(*s))?; } if let Some(ref s) = self.f_sint32 { w.write_with_tag(40, |w| w.write_sint32(*s))?; } if self.f_sint64 != 4i64 { w.write_with_tag(48, |w| w.write_sint64(*&self.f_sint64))?; } if self.f_bool != true { w.write_with_tag(56, |w| w.write_bool(*&self.f_bool))?; } if let Some(ref s) = self.f_FooEnum { w.write_with_tag(64, |w| w.write_enum(*s as i32))?; } if let Some(ref s) = self.f_fixed64 { w.write_with_tag(73, |w| w.write_fixed64(*s))?; } if let Some(ref s) = self.f_sfixed64 { w.write_with_tag(81, |w| w.write_sfixed64(*s))?; } if self.f_fixed32 != 0u32 { w.write_with_tag(93, |w| w.write_fixed32(*&self.f_fixed32))?; } if let Some(ref s) = self.f_sfixed32 { w.write_with_tag(101, |w| w.write_sfixed32(*s))?; } if let Some(ref s) = self.f_double { w.write_with_tag(105, |w| w.write_double(*s))?; } if let Some(ref s) = self.f_float { w.write_with_tag(117, |w| w.write_float(*s))?; } if let Some(ref s) = self.f_bytes { w.write_with_tag(122, |w| w.write_bytes(&**s))?; } if let Some(ref s) = self.f_string { w.write_with_tag(130, |w| w.write_string(&**s))?; } if let Some(ref s) = self.f_self_message { w.write_with_tag(138, |w| w.write_message(&**s))?; } if let Some(ref s) = self.f_bar_message { w.write_with_tag(146, |w| w.write_message(s))?; } for s in &self.f_repeated_int32 { w.write_with_tag(152, |w| w.write_int32(*s))?; } w.write_packed_with_tag(162, &self.f_repeated_packed_int32, |w, m| w.write_int32(*m), &|m| sizeof_varint(*(m) as u64))?; w.write_packed_fixed_with_tag(170, &self.f_repeated_packed_float)?; if let Some(ref s) = self.f_imported { w.write_with_tag(178, |w| w.write_message(s))?; } if let Some(ref s) = self.f_baz { w.write_with_tag(186, |w| w.write_message(s))?; } if let Some(ref s) = self.f_nested { w.write_with_tag(194, |w| w.write_message(s))?; } if let Some(ref s) = self.f_nested_enum { w.write_with_tag(200, |w| w.write_enum(*s as i32))?; } for (k, v) in self.f_map.iter() { w.write_with_tag(210, |w| w.write_map(2 + sizeof_len((k).len()) + sizeof_varint(*(v) as u64), 10, |w| w.write_string(&**k), 16, |w| w.write_int32(*v)))?; } match self.test_oneof { mod_FooMessage::OneOftest_oneof::f1(ref m) => { w.write_with_tag(216, |w| w.write_int32(*m))? }, mod_FooMessage::OneOftest_oneof::f2(ref m) => { w.write_with_tag(224, |w| w.write_bool(*m))? }, mod_FooMessage::OneOftest_oneof::f3(ref m) => { w.write_with_tag(234, |w| w.write_string(&**m))? }, mod_FooMessage::OneOftest_oneof::None => {}, } Ok(()) } } pub mod mod_FooMessage { use super::*; #[derive(Debug, PartialEq, Clone)] pub enum OneOftest_oneof<'a> { f1(i32), f2(bool), f3(Cow<'a, str>), None, } impl<'a> Default for OneOftest_oneof<'a> { fn default() -> Self { OneOftest_oneof::None } } } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] pub struct BazMessage { pub nested: Option, } impl<'a> MessageRead<'a> for BazMessage { fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { Ok(10) => msg.nested = Some(r.read_message::(bytes)?), Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } } Ok(msg) } } impl MessageWrite for BazMessage { fn get_size(&self) -> usize { 0 + self.nested.as_ref().map_or(0, |m| 1 + sizeof_len((m).get_size())) } fn write_message(&self, w: &mut Writer) -> Result<()> { if let Some(ref s) = self.nested { w.write_with_tag(10, |w| w.write_message(s))?; } Ok(()) } } pub mod mod_BazMessage { use super::*; #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] pub struct Nested { pub f_nested: mod_BazMessage::mod_Nested::NestedMessage, } impl<'a> MessageRead<'a> for Nested { fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { Ok(10) => msg.f_nested = r.read_message::(bytes)?, Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } } Ok(msg) } } impl MessageWrite for Nested { fn get_size(&self) -> usize { 0 + 1 + sizeof_len((&self.f_nested).get_size()) } fn write_message(&self, w: &mut Writer) -> Result<()> { w.write_with_tag(10, |w| w.write_message(&self.f_nested))?; Ok(()) } } pub mod mod_Nested { use super::*; #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] pub struct NestedMessage { pub f_nested: i32, } impl<'a> MessageRead<'a> for NestedMessage { fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { Ok(8) => msg.f_nested = r.read_int32(bytes)?, Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } } Ok(msg) } } impl MessageWrite for NestedMessage { fn get_size(&self) -> usize { 0 + 1 + sizeof_varint(*(&self.f_nested) as u64) } fn write_message(&self, w: &mut Writer) -> Result<()> { w.write_with_tag(8, |w| w.write_int32(*&self.f_nested))?; Ok(()) } } #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum NestedEnum { Foo = 0, Bar = 1, Baz = 2, } impl Default for NestedEnum { fn default() -> Self { NestedEnum::Foo } } impl From for NestedEnum { fn from(i: i32) -> Self { match i { 0 => NestedEnum::Foo, 1 => NestedEnum::Bar, 2 => NestedEnum::Baz, _ => Self::default(), } } } impl<'a> From<&'a str> for NestedEnum { fn from(s: &'a str) -> Self { match s { "Foo" => NestedEnum::Foo, "Bar" => NestedEnum::Bar, "Baz" => NestedEnum::Baz, _ => Self::default(), } } } } } quick-protobuf-0.8.1/examples/pb_rs/data_types_import.proto000064400000000000000000000001031046102023000223350ustar 00000000000000package a.b; message ImportedMessage { optional bool i = 1; } quick-protobuf-0.8.1/examples/pb_rs/data_types_unit.proto000064400000000000000000000000641046102023000220100ustar 00000000000000message unit_message { } enum test { a = 10; } quick-protobuf-0.8.1/examples/pb_rs/data_types_unit.rs000064400000000000000000000022361046102023000212740ustar 00000000000000// Automatically generated rust module for 'data_types_unit.proto' file #![allow(non_snake_case)] #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(unused_imports)] #![allow(unknown_lints)] #![allow(clippy::all)] #![cfg_attr(rustfmt, rustfmt_skip)] use quick_protobuf::{BytesReader, Result, MessageInfo, MessageRead, MessageWrite}; use super::*; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum test { a = 10, } impl Default for test { fn default() -> Self { test::a } } impl From for test { fn from(i: i32) -> Self { match i { 10 => test::a, _ => Self::default(), } } } impl<'a> From<&'a str> for test { fn from(s: &'a str) -> Self { match s { "a" => test::a, _ => Self::default(), } } } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] pub struct unit_message { } impl<'a> MessageRead<'a> for unit_message { fn from_reader(r: &mut BytesReader, _: &[u8]) -> Result { r.read_to_end(); Ok(Self::default()) } } impl MessageWrite for unit_message { } quick-protobuf-0.8.1/examples/pb_rs/mod.rs000064400000000000000000000001321046102023000166500ustar 00000000000000// Automatically generated mod.rs pub mod data_types; pub mod a; pub mod data_types_unit; quick-protobuf-0.8.1/examples/pb_rs_example.rs000064400000000000000000000060101046102023000176050ustar 00000000000000extern crate quick_protobuf; mod pb_rs; use std::borrow::Cow; use crate::pb_rs::data_types::mod_FooMessage::OneOftest_oneof; use crate::pb_rs::data_types::{self, FooMessage}; // Imported fields contain package a.b, which is translated into // mod_a::mod_b rust module use crate::pb_rs::a::b::ImportedMessage; use quick_protobuf::{deserialize_from_slice, serialize_into_vec, BytesReader, Writer}; fn main() { // Generate a message, somehow // // For the example we will leverage the `Default` derive of all messages let message = FooMessage { // Regular field work as expected, optional leverages on rust Option<> f_int32: Some(54), // strings are borrowed (Cow) f_string: Some(Cow::Borrowed("Hello world from example!")), // bytes too f_bytes: Some(Cow::Borrowed(b"I see you!")), // imported fields work as expected f_imported: Some(ImportedMessage { i: Some(true) }), // nested messages are encapsulated into a rust module mod_Message f_nested: Some(data_types::mod_BazMessage::Nested { f_nested: data_types::mod_BazMessage::mod_Nested::NestedMessage { f_nested: 2 }, }), // nested enums too f_nested_enum: Some(data_types::mod_BazMessage::mod_Nested::NestedEnum::Baz), // a map! f_map: vec![(Cow::Borrowed("foo"), 1), (Cow::Borrowed("bar"), 2)] .into_iter() .collect(), // a oneof value test_oneof: OneOftest_oneof::f1(2), // Each message implements Default ... which makes it much easier ..FooMessage::default() }; // Write the message into our buffer, it could be a file, a ZipFile etc ... let mut out = Vec::new(); { let mut writer = Writer::new(&mut out); writer .write_message(&message) .expect("Cannot write message!"); } println!("Message written successfully!"); // alternatively, a one-liner if we want to work with vec directly let out_vec = serialize_into_vec(&message).expect("Cannot write message!"); assert_eq!(out, out_vec); // Try to read it back and confirm that we still have the exact same message // // In general, if we had written the data to say, a file, or if someone else // have written that data, it would be more convenient to use a `Reader` which // will feed an internal, owned, buffer. Here, on the contrary, we already hold // the `out` buffer. Thus it is more efficient to directly use a `BytesWriter`. let read_message = { let mut reader = BytesReader::from_bytes(&out); reader .read_message::(&out) .expect("Cannot read message") }; assert_eq!(message, read_message); // alternatively, a one-liner if we want to work with slices directly let read_vec = deserialize_from_slice(&out).expect("Cannot write message!"); assert_eq!(message, read_vec); println!("Message read back and everything matches!"); println!("{:#?}", read_message); } quick-protobuf-0.8.1/examples/pb_rs_example_nostd.rs000064400000000000000000000010571046102023000210220ustar 00000000000000#![no_std] extern crate std as ext_std; extern crate alloc; extern crate quick_protobuf; #[global_allocator] static A: ext_std::alloc::System = ext_std::alloc::System; mod pb_rs_nostd; use crate::pb_rs_nostd::protos::no_std::NoStdMessage; use quick_protobuf::{deserialize_from_slice, serialize_into_slice}; fn main() { let message = NoStdMessage::default(); let mut buf = [0u8; 1024]; serialize_into_slice(&message, &mut buf).unwrap(); let read_message = deserialize_from_slice(&buf).unwrap(); assert_eq!(message, read_message); } quick-protobuf-0.8.1/examples/pb_rs_example_v3.rs000064400000000000000000000117121046102023000202220ustar 00000000000000extern crate quick_protobuf; mod pb_rs_v3; use std::borrow::Cow; use crate::pb_rs_v3::data_types::mod_FooMessage::OneOftest_oneof; use crate::pb_rs_v3::data_types::{self, BarMessage, BazMessage, FooMessage, RepeatedMessage}; // Imported fields contain package a.b, which is translated into // mod_a::mod_b rust module use crate::pb_rs_v3::a::b::ImportedMessage; use quick_protobuf::{deserialize_from_slice, serialize_into_vec, BytesReader, Writer}; fn main() { // Generate a message, somehow // // For the example we will leverage the `Default` derive of all messages let message = FooMessage { // Regular field work as expected, optional leverages on rust Option<> f_int32: 54, // strings are borrowed (Cow) f_string: Cow::Borrowed("Hello world from example!"), // bytes too f_bytes: Cow::Borrowed(b"I see you!"), // imported fields work as expected f_imported: Some(ImportedMessage { i: true }), // nested messages are encapsulated into a rust module mod_Message f_nested: Some(data_types::mod_BazMessage::Nested { f_nested: Some(data_types::mod_BazMessage::mod_Nested::NestedMessage { f_nested: 2 }), }), // nested enums too f_nested_enum: data_types::mod_BazMessage::mod_Nested::NestedEnum::Baz, // a map! f_map: vec![(Cow::Borrowed("foo"), 1), (Cow::Borrowed("bar"), 2)] .into_iter() .collect(), // a oneof value test_oneof: OneOftest_oneof::f1(2), f_repeated_string: vec![Cow::Borrowed("goat"), Cow::Borrowed("running")], f_repeated_baz_message: vec![BazMessage { nested: Some(data_types::mod_BazMessage::Nested { f_nested: Some(data_types::mod_BazMessage::mod_Nested::NestedMessage { f_nested: 2, }), }), b_int64: 10, b_string: Cow::Borrowed("boom\n"), }], // Each message implements Default ... which makes it much easier ..FooMessage::default() }; // Write the message into our buffer, it could be a file, a ZipFile etc ... let mut out = Vec::new(); { let mut writer = Writer::new(&mut out); writer .write_message(&message) .expect("Cannot write message!"); } println!("Message written successfully! bytes={:?}", &out); // alternatively, a one-liner if we want to work with vec directly let out_vec = serialize_into_vec(&message).expect("Cannot write message!"); assert_eq!(out, out_vec); // Try to read it back and confirm that we still have the exact same message // // In general, if we had written the data to say, a file, or if someone else // have written that data, it would be more convenient to use a `Reader` which // will feed an internal, owned, buffer. Here, on the contrary, we already hold // the `out` buffer. Thus it is more efficient to directly use a `BytesWriter`. let read_message = { let mut reader = BytesReader::from_bytes(&out); reader .read_message::(&out) .expect("Cannot read message") }; assert_eq!(message, read_message); // alternatively, a one-liner if we want to work with slices directly let read_vec = deserialize_from_slice(&out).expect("Cannot write message!"); assert_eq!(message, read_vec); println!("Message read back and everything matches!"); println!("{:#?}", read_message); let message = RepeatedMessage { bar_message: vec![BarMessage { b_int32: 0 }, BarMessage { b_int32: 9 }], }; let mut out = Vec::new(); { let mut writer = Writer::new(&mut out); writer .write_message(&message) .expect("Cannot write message!"); } println!("Message written successfully! bytes={:?}", &out); // alternatively, a one-liner if we want to work with vec directly let out_vec = serialize_into_vec(&message).expect("Cannot write message!"); assert_eq!(out, out_vec); // Try to read it back and confirm that we still have the exact same message // // In general, if we had written the data to say, a file, or if someone else // have written that data, it would be more convenient to use a `Reader` which // will feed an internal, owned, buffer. Here, on the contrary, we already hold // the `out` buffer. Thus it is more efficient to directly use a `BytesWriter`. let read_message = { let mut reader = BytesReader::from_bytes(&out); reader .read_message::(&out) .expect("Cannot read message") }; assert_eq!(message, read_message); // alternatively, a one-liner if we want to work with slices directly let read_vec = deserialize_from_slice(&out).expect("Cannot write message!"); assert_eq!(message, read_vec); println!("Message read back and everything matches!"); println!("{:#?}", read_message); } quick-protobuf-0.8.1/examples/pb_rs_example_v3_owned.rs000064400000000000000000000061711046102023000214210ustar 00000000000000extern crate quick_protobuf; mod owned { include!("pb_rs_v3/owned/mod.rs"); } use std::borrow::Cow; use std::convert::TryFrom; use crate::owned::data_types::mod_FooMessage::OneOftest_oneof; use crate::owned::data_types::{self, BazMessage, FooMessage, FooMessageOwned}; // Imported fields contain package a.b, which is translated into // mod_a::mod_b rust module use crate::owned::a::b::ImportedMessage; use quick_protobuf::{MessageWrite, Writer}; fn main() { // Generate a message, somehow // // For the example we will leverage the `Default` derive of all messages let message = FooMessage { // Regular field work as expected, optional leverages on rust Option<> f_int32: 54, // strings are borrowed (Cow) f_string: Cow::Borrowed("Hello world from example!"), // bytes too f_bytes: Cow::Borrowed(b"I see you!"), // imported fields work as expected f_imported: Some(ImportedMessage { i: true }), // nested messages are encapsulated into a rust module mod_Message f_nested: Some(data_types::mod_BazMessage::Nested { f_nested: Some(data_types::mod_BazMessage::mod_Nested::NestedMessage { f_nested: 2 }), }), // nested enums too f_nested_enum: data_types::mod_BazMessage::mod_Nested::NestedEnum::Baz, // a map! f_map: vec![(Cow::Borrowed("foo"), 1), (Cow::Borrowed("bar"), 2)] .into_iter() .collect(), // a oneof value test_oneof: OneOftest_oneof::f1(2), f_repeated_string: vec![Cow::Borrowed("goat"), Cow::Borrowed("running")], f_repeated_baz_message: vec![BazMessage { nested: Some(data_types::mod_BazMessage::Nested { f_nested: Some(data_types::mod_BazMessage::mod_Nested::NestedMessage { f_nested: 2, }), }), b_int64: 10, b_string: Cow::Borrowed("boom\n"), }], // Each message implements Default ... which makes it much easier ..FooMessage::default() }; // Write the message into our buffer, it could be a file, a ZipFile etc ... let mut out = Vec::new(); { let mut writer = Writer::new(&mut out); message .write_message(&mut writer) .expect("Cannot write message!"); } println!("Message written successfully! bytes={:?}", &out); let read_message_owned = FooMessageOwned::try_from(out.clone()).unwrap(); let read_message_owned = read_message_owned.proto(); println!("{:?}", read_message_owned); assert_eq!(&message, read_message_owned); println!("Message read back and everything matches!"); // Test mutability works too let mut read_message_owned_mut = FooMessageOwned::try_from(out).unwrap(); let mut read_message_owned_mut = read_message_owned_mut.proto_mut(); read_message_owned_mut.f_int32 += 1; assert_eq!(message.f_int32 + 1, read_message_owned_mut.f_int32); read_message_owned_mut.f_string = "I see you, too!".into(); assert_eq!( read_message_owned_mut.f_string, Cow::Borrowed("I see you, too!") ); } quick-protobuf-0.8.1/examples/pb_rs_nostd/mod.rs000064400000000000000000000000621046102023000200610ustar 00000000000000// Automatically generated mod.rs pub mod protos; quick-protobuf-0.8.1/examples/pb_rs_nostd/no_std.proto000064400000000000000000000004441046102023000213130ustar 00000000000000syntax = "proto3"; package protos.no_std; enum MyEnum { Val0 = 0; Val1 = 1; } message EmbeddedMessage { int32 val = 1; MyEnum e = 2; } message NoStdMessage { fixed32 num = 1; repeated fixed32 nums = 2; EmbeddedMessage message = 3; repeated EmbeddedMessage messages = 4; } quick-protobuf-0.8.1/examples/pb_rs_nostd/protos/mod.rs000064400000000000000000000000621046102023000214070ustar 00000000000000// Automatically generated mod.rs pub mod no_std; quick-protobuf-0.8.1/examples/pb_rs_nostd/protos/no_std.rs000064400000000000000000000100271046102023000221200ustar 00000000000000// Automatically generated rust module for 'no_std.proto' file #![allow(non_snake_case)] #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(unused_imports)] #![allow(unknown_lints)] #![allow(clippy::all)] #![cfg_attr(rustfmt, rustfmt_skip)] use alloc::vec::Vec; use alloc::borrow::Cow; use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result}; use quick_protobuf::sizeofs::*; use super::super::*; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum MyEnum { Val0 = 0, Val1 = 1, } impl Default for MyEnum { fn default() -> Self { MyEnum::Val0 } } impl From for MyEnum { fn from(i: i32) -> Self { match i { 0 => MyEnum::Val0, 1 => MyEnum::Val1, _ => Self::default(), } } } impl<'a> From<&'a str> for MyEnum { fn from(s: &'a str) -> Self { match s { "Val0" => MyEnum::Val0, "Val1" => MyEnum::Val1, _ => Self::default(), } } } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] pub struct EmbeddedMessage { pub val: i32, pub e: protos::no_std::MyEnum, } impl<'a> MessageRead<'a> for EmbeddedMessage { fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { Ok(8) => msg.val = r.read_int32(bytes)?, Ok(16) => msg.e = r.read_enum(bytes)?, Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } } Ok(msg) } } impl MessageWrite for EmbeddedMessage { fn get_size(&self) -> usize { 0 + if self.val == 0i32 { 0 } else { 1 + sizeof_varint(*(&self.val) as u64) } + if self.e == protos::no_std::MyEnum::Val0 { 0 } else { 1 + sizeof_varint(*(&self.e) as u64) } } fn write_message(&self, w: &mut Writer) -> Result<()> { if self.val != 0i32 { w.write_with_tag(8, |w| w.write_int32(*&self.val))?; } if self.e != protos::no_std::MyEnum::Val0 { w.write_with_tag(16, |w| w.write_enum(*&self.e as i32))?; } Ok(()) } } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] pub struct NoStdMessage<'a> { pub num: u32, pub nums: Cow<'a, [u32]>, pub message: Option, pub messages: Vec, } impl<'a> MessageRead<'a> for NoStdMessage<'a> { fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { Ok(13) => msg.num = r.read_fixed32(bytes)?, Ok(18) => msg.nums = r.read_packed_fixed(bytes)?.into(), Ok(26) => msg.message = Some(r.read_message::(bytes)?), Ok(34) => msg.messages.push(r.read_message::(bytes)?), Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } } Ok(msg) } } impl<'a> MessageWrite for NoStdMessage<'a> { fn get_size(&self) -> usize { 0 + if self.num == 0u32 { 0 } else { 1 + 4 } + if self.nums.is_empty() { 0 } else { 1 + sizeof_len(self.nums.len() * 4) } + self.message.as_ref().map_or(0, |m| 1 + sizeof_len((m).get_size())) + self.messages.iter().map(|s| 1 + sizeof_len((s).get_size())).sum::() } fn write_message(&self, w: &mut Writer) -> Result<()> { if self.num != 0u32 { w.write_with_tag(13, |w| w.write_fixed32(*&self.num))?; } w.write_packed_fixed_with_tag(18, &self.nums)?; if let Some(ref s) = self.message { w.write_with_tag(26, |w| w.write_message(s))?; } for s in &self.messages { w.write_with_tag(34, |w| w.write_message(s))?; } Ok(()) } } quick-protobuf-0.8.1/examples/pb_rs_v3/a/b.rs000064400000000000000000000025241046102023000171510ustar 00000000000000// Automatically generated rust module for 'data_types_import.proto' file #![allow(non_snake_case)] #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(unused_imports)] #![allow(unknown_lints)] #![allow(clippy::all)] #![cfg_attr(rustfmt, rustfmt_skip)] use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result}; use quick_protobuf::sizeofs::*; use super::super::*; #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] pub struct ImportedMessage { pub i: bool, } impl<'a> MessageRead<'a> for ImportedMessage { fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { Ok(8) => msg.i = r.read_bool(bytes)?, Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } } Ok(msg) } } impl MessageWrite for ImportedMessage { fn get_size(&self) -> usize { 0 + if self.i == false { 0 } else { 1 + sizeof_varint(*(&self.i) as u64) } } fn write_message(&self, w: &mut Writer) -> Result<()> { if self.i != false { w.write_with_tag(8, |w| w.write_bool(*&self.i))?; } Ok(()) } } quick-protobuf-0.8.1/examples/pb_rs_v3/a/mod.rs000064400000000000000000000000551046102023000175040ustar 00000000000000// Automatically generated mod.rs pub mod b; quick-protobuf-0.8.1/examples/pb_rs_v3/data_types.proto000064400000000000000000000032101046102023000213550ustar 00000000000000syntax = "proto3"; import "data_types_import.proto"; enum FooEnum { FIRST_VALUE = 1; SECOND_VALUE = 2; } message BarMessage { int32 b_int32 = 1; } message FooMessage { int32 f_int32 = 1; int64 f_int64 = 2; uint32 f_uint32 = 3; uint64 f_uint64 = 4; sint32 f_sint32 = 5; sint64 f_sint64 = 6 [default=4]; bool f_bool = 7 [default=true]; FooEnum f_FooEnum = 8; fixed64 f_fixed64 = 9; sfixed64 f_sfixed64 = 10; fixed32 f_fixed32 = 11 [default=0]; sfixed32 f_sfixed32 = 12; double f_double = 13; float f_float = 14; bytes f_bytes = 15; string f_string = 16; FooMessage f_self_message = 17; BarMessage f_bar_message = 18; repeated int32 f_repeated_int32 = 19; repeated int32 f_repeated_packed_int32 = 20 [ packed = true ]; repeated float f_repeated_packed_float = 21 [ packed = true ]; a.b.ImportedMessage f_imported = 22; BazMessage f_baz = 23; BazMessage.Nested f_nested = 24; BazMessage.Nested.NestedEnum f_nested_enum = 25; map f_map = 26; oneof test_oneof { int32 f1 = 27; bool f2 = 28; string f3 = 29; } repeated string f_repeated_string = 30; repeated BazMessage f_repeated_baz_message = 31; } message BazMessage { message Nested { message NestedMessage { int32 f_nested = 1; } enum NestedEnum { Foo = 0; Bar = 1; Baz = 2; } NestedMessage f_nested = 1; } Nested nested = 1; int64 b_int64 = 2; string b_string = 3; } message RepeatedMessage { repeated BarMessage bar_message = 1; } quick-protobuf-0.8.1/examples/pb_rs_v3/data_types.rs000064400000000000000000000442451046102023000206530ustar 00000000000000// Automatically generated rust module for 'data_types.proto' file #![allow(non_snake_case)] #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(unused_imports)] #![allow(unknown_lints)] #![allow(clippy::all)] #![cfg_attr(rustfmt, rustfmt_skip)] use std::borrow::Cow; use std::collections::HashMap; type KVMap = HashMap; use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result}; use quick_protobuf::sizeofs::*; use super::*; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum FooEnum { FIRST_VALUE = 1, SECOND_VALUE = 2, } impl Default for FooEnum { fn default() -> Self { FooEnum::FIRST_VALUE } } impl From for FooEnum { fn from(i: i32) -> Self { match i { 1 => FooEnum::FIRST_VALUE, 2 => FooEnum::SECOND_VALUE, _ => Self::default(), } } } impl<'a> From<&'a str> for FooEnum { fn from(s: &'a str) -> Self { match s { "FIRST_VALUE" => FooEnum::FIRST_VALUE, "SECOND_VALUE" => FooEnum::SECOND_VALUE, _ => Self::default(), } } } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] pub struct BarMessage { pub b_int32: i32, } impl<'a> MessageRead<'a> for BarMessage { fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { Ok(8) => msg.b_int32 = r.read_int32(bytes)?, Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } } Ok(msg) } } impl MessageWrite for BarMessage { fn get_size(&self) -> usize { 0 + if self.b_int32 == 0i32 { 0 } else { 1 + sizeof_varint(*(&self.b_int32) as u64) } } fn write_message(&self, w: &mut Writer) -> Result<()> { if self.b_int32 != 0i32 { w.write_with_tag(8, |w| w.write_int32(*&self.b_int32))?; } Ok(()) } } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] pub struct FooMessage<'a> { pub f_int32: i32, pub f_int64: i64, pub f_uint32: u32, pub f_uint64: u64, pub f_sint32: i32, pub f_sint64: i64, pub f_bool: bool, pub f_FooEnum: FooEnum, pub f_fixed64: u64, pub f_sfixed64: i64, pub f_fixed32: u32, pub f_sfixed32: i32, pub f_double: f64, pub f_float: f32, pub f_bytes: Cow<'a, [u8]>, pub f_string: Cow<'a, str>, pub f_self_message: Option>>, pub f_bar_message: Option, pub f_repeated_int32: Vec, pub f_repeated_packed_int32: Vec, pub f_repeated_packed_float: Cow<'a, [f32]>, pub f_imported: Option, pub f_baz: Option>, pub f_nested: Option, pub f_nested_enum: mod_BazMessage::mod_Nested::NestedEnum, pub f_map: KVMap, i32>, pub f_repeated_string: Vec>, pub f_repeated_baz_message: Vec>, pub test_oneof: mod_FooMessage::OneOftest_oneof<'a>, } impl<'a> MessageRead<'a> for FooMessage<'a> { fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { let mut msg = FooMessage { f_sint64: 4i64, f_bool: true, ..Self::default() }; while !r.is_eof() { match r.next_tag(bytes) { Ok(8) => msg.f_int32 = r.read_int32(bytes)?, Ok(16) => msg.f_int64 = r.read_int64(bytes)?, Ok(24) => msg.f_uint32 = r.read_uint32(bytes)?, Ok(32) => msg.f_uint64 = r.read_uint64(bytes)?, Ok(40) => msg.f_sint32 = r.read_sint32(bytes)?, Ok(48) => msg.f_sint64 = r.read_sint64(bytes)?, Ok(56) => msg.f_bool = r.read_bool(bytes)?, Ok(64) => msg.f_FooEnum = r.read_enum(bytes)?, Ok(73) => msg.f_fixed64 = r.read_fixed64(bytes)?, Ok(81) => msg.f_sfixed64 = r.read_sfixed64(bytes)?, Ok(93) => msg.f_fixed32 = r.read_fixed32(bytes)?, Ok(101) => msg.f_sfixed32 = r.read_sfixed32(bytes)?, Ok(105) => msg.f_double = r.read_double(bytes)?, Ok(117) => msg.f_float = r.read_float(bytes)?, Ok(122) => msg.f_bytes = r.read_bytes(bytes).map(Cow::Borrowed)?, Ok(130) => msg.f_string = r.read_string(bytes).map(Cow::Borrowed)?, Ok(138) => msg.f_self_message = Some(Box::new(r.read_message::(bytes)?)), Ok(146) => msg.f_bar_message = Some(r.read_message::(bytes)?), Ok(154) => msg.f_repeated_int32 = r.read_packed(bytes, |r, bytes| Ok(r.read_int32(bytes)?))?, Ok(162) => msg.f_repeated_packed_int32 = r.read_packed(bytes, |r, bytes| Ok(r.read_int32(bytes)?))?, Ok(170) => msg.f_repeated_packed_float = r.read_packed_fixed(bytes)?.into(), Ok(178) => msg.f_imported = Some(r.read_message::(bytes)?), Ok(186) => msg.f_baz = Some(r.read_message::(bytes)?), Ok(194) => msg.f_nested = Some(r.read_message::(bytes)?), Ok(200) => msg.f_nested_enum = r.read_enum(bytes)?, Ok(210) => { let (key, value) = r.read_map(bytes, |r, bytes| Ok(r.read_string(bytes).map(Cow::Borrowed)?), |r, bytes| Ok(r.read_int32(bytes)?))?; msg.f_map.insert(key, value); } Ok(242) => msg.f_repeated_string.push(r.read_string(bytes).map(Cow::Borrowed)?), Ok(250) => msg.f_repeated_baz_message.push(r.read_message::(bytes)?), Ok(216) => msg.test_oneof = mod_FooMessage::OneOftest_oneof::f1(r.read_int32(bytes)?), Ok(224) => msg.test_oneof = mod_FooMessage::OneOftest_oneof::f2(r.read_bool(bytes)?), Ok(234) => msg.test_oneof = mod_FooMessage::OneOftest_oneof::f3(r.read_string(bytes).map(Cow::Borrowed)?), Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } } Ok(msg) } } impl<'a> MessageWrite for FooMessage<'a> { fn get_size(&self) -> usize { 0 + if self.f_int32 == 0i32 { 0 } else { 1 + sizeof_varint(*(&self.f_int32) as u64) } + if self.f_int64 == 0i64 { 0 } else { 1 + sizeof_varint(*(&self.f_int64) as u64) } + if self.f_uint32 == 0u32 { 0 } else { 1 + sizeof_varint(*(&self.f_uint32) as u64) } + if self.f_uint64 == 0u64 { 0 } else { 1 + sizeof_varint(*(&self.f_uint64) as u64) } + if self.f_sint32 == 0i32 { 0 } else { 1 + sizeof_sint32(*(&self.f_sint32)) } + if self.f_sint64 == 4i64 { 0 } else { 1 + sizeof_sint64(*(&self.f_sint64)) } + if self.f_bool == true { 0 } else { 1 + sizeof_varint(*(&self.f_bool) as u64) } + if self.f_FooEnum == data_types::FooEnum::FIRST_VALUE { 0 } else { 1 + sizeof_varint(*(&self.f_FooEnum) as u64) } + if self.f_fixed64 == 0u64 { 0 } else { 1 + 8 } + if self.f_sfixed64 == 0i64 { 0 } else { 1 + 8 } + if self.f_fixed32 == 0u32 { 0 } else { 1 + 4 } + if self.f_sfixed32 == 0i32 { 0 } else { 1 + 4 } + if self.f_double == 0f64 { 0 } else { 1 + 8 } + if self.f_float == 0f32 { 0 } else { 1 + 4 } + if self.f_bytes == Cow::Borrowed(b"") { 0 } else { 1 + sizeof_len((&self.f_bytes).len()) } + if self.f_string == "" { 0 } else { 2 + sizeof_len((&self.f_string).len()) } + self.f_self_message.as_ref().map_or(0, |m| 2 + sizeof_len((m).get_size())) + self.f_bar_message.as_ref().map_or(0, |m| 2 + sizeof_len((m).get_size())) + if self.f_repeated_int32.is_empty() { 0 } else { 2 + sizeof_len(self.f_repeated_int32.iter().map(|s| sizeof_varint(*(s) as u64)).sum::()) } + if self.f_repeated_packed_int32.is_empty() { 0 } else { 2 + sizeof_len(self.f_repeated_packed_int32.iter().map(|s| sizeof_varint(*(s) as u64)).sum::()) } + if self.f_repeated_packed_float.is_empty() { 0 } else { 2 + sizeof_len(self.f_repeated_packed_float.len() * 4) } + self.f_imported.as_ref().map_or(0, |m| 2 + sizeof_len((m).get_size())) + self.f_baz.as_ref().map_or(0, |m| 2 + sizeof_len((m).get_size())) + self.f_nested.as_ref().map_or(0, |m| 2 + sizeof_len((m).get_size())) + if self.f_nested_enum == data_types::mod_BazMessage::mod_Nested::NestedEnum::Foo { 0 } else { 2 + sizeof_varint(*(&self.f_nested_enum) as u64) } + self.f_map.iter().map(|(k, v)| 2 + sizeof_len(2 + sizeof_len((k).len()) + sizeof_varint(*(v) as u64))).sum::() + self.f_repeated_string.iter().map(|s| 2 + sizeof_len((s).len())).sum::() + self.f_repeated_baz_message.iter().map(|s| 2 + sizeof_len((s).get_size())).sum::() + match self.test_oneof { mod_FooMessage::OneOftest_oneof::f1(ref m) => 2 + sizeof_varint(*(m) as u64), mod_FooMessage::OneOftest_oneof::f2(ref m) => 2 + sizeof_varint(*(m) as u64), mod_FooMessage::OneOftest_oneof::f3(ref m) => 2 + sizeof_len((m).len()), mod_FooMessage::OneOftest_oneof::None => 0, } } fn write_message(&self, w: &mut Writer) -> Result<()> { if self.f_int32 != 0i32 { w.write_with_tag(8, |w| w.write_int32(*&self.f_int32))?; } if self.f_int64 != 0i64 { w.write_with_tag(16, |w| w.write_int64(*&self.f_int64))?; } if self.f_uint32 != 0u32 { w.write_with_tag(24, |w| w.write_uint32(*&self.f_uint32))?; } if self.f_uint64 != 0u64 { w.write_with_tag(32, |w| w.write_uint64(*&self.f_uint64))?; } if self.f_sint32 != 0i32 { w.write_with_tag(40, |w| w.write_sint32(*&self.f_sint32))?; } if self.f_sint64 != 4i64 { w.write_with_tag(48, |w| w.write_sint64(*&self.f_sint64))?; } if self.f_bool != true { w.write_with_tag(56, |w| w.write_bool(*&self.f_bool))?; } if self.f_FooEnum != data_types::FooEnum::FIRST_VALUE { w.write_with_tag(64, |w| w.write_enum(*&self.f_FooEnum as i32))?; } if self.f_fixed64 != 0u64 { w.write_with_tag(73, |w| w.write_fixed64(*&self.f_fixed64))?; } if self.f_sfixed64 != 0i64 { w.write_with_tag(81, |w| w.write_sfixed64(*&self.f_sfixed64))?; } if self.f_fixed32 != 0u32 { w.write_with_tag(93, |w| w.write_fixed32(*&self.f_fixed32))?; } if self.f_sfixed32 != 0i32 { w.write_with_tag(101, |w| w.write_sfixed32(*&self.f_sfixed32))?; } if self.f_double != 0f64 { w.write_with_tag(105, |w| w.write_double(*&self.f_double))?; } if self.f_float != 0f32 { w.write_with_tag(117, |w| w.write_float(*&self.f_float))?; } if self.f_bytes != Cow::Borrowed(b"") { w.write_with_tag(122, |w| w.write_bytes(&**&self.f_bytes))?; } if self.f_string != "" { w.write_with_tag(130, |w| w.write_string(&**&self.f_string))?; } if let Some(ref s) = self.f_self_message { w.write_with_tag(138, |w| w.write_message(&**s))?; } if let Some(ref s) = self.f_bar_message { w.write_with_tag(146, |w| w.write_message(s))?; } w.write_packed_with_tag(154, &self.f_repeated_int32, |w, m| w.write_int32(*m), &|m| sizeof_varint(*(m) as u64))?; w.write_packed_with_tag(162, &self.f_repeated_packed_int32, |w, m| w.write_int32(*m), &|m| sizeof_varint(*(m) as u64))?; w.write_packed_fixed_with_tag(170, &self.f_repeated_packed_float)?; if let Some(ref s) = self.f_imported { w.write_with_tag(178, |w| w.write_message(s))?; } if let Some(ref s) = self.f_baz { w.write_with_tag(186, |w| w.write_message(s))?; } if let Some(ref s) = self.f_nested { w.write_with_tag(194, |w| w.write_message(s))?; } if self.f_nested_enum != data_types::mod_BazMessage::mod_Nested::NestedEnum::Foo { w.write_with_tag(200, |w| w.write_enum(*&self.f_nested_enum as i32))?; } for (k, v) in self.f_map.iter() { w.write_with_tag(210, |w| w.write_map(2 + sizeof_len((k).len()) + sizeof_varint(*(v) as u64), 10, |w| w.write_string(&**k), 16, |w| w.write_int32(*v)))?; } for s in &self.f_repeated_string { w.write_with_tag(242, |w| w.write_string(&**s))?; } for s in &self.f_repeated_baz_message { w.write_with_tag(250, |w| w.write_message(s))?; } match self.test_oneof { mod_FooMessage::OneOftest_oneof::f1(ref m) => { w.write_with_tag(216, |w| w.write_int32(*m))? }, mod_FooMessage::OneOftest_oneof::f2(ref m) => { w.write_with_tag(224, |w| w.write_bool(*m))? }, mod_FooMessage::OneOftest_oneof::f3(ref m) => { w.write_with_tag(234, |w| w.write_string(&**m))? }, mod_FooMessage::OneOftest_oneof::None => {}, } Ok(()) } } pub mod mod_FooMessage { use super::*; #[derive(Debug, PartialEq, Clone)] pub enum OneOftest_oneof<'a> { f1(i32), f2(bool), f3(Cow<'a, str>), None, } impl<'a> Default for OneOftest_oneof<'a> { fn default() -> Self { OneOftest_oneof::None } } } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] pub struct BazMessage<'a> { pub nested: Option, pub b_int64: i64, pub b_string: Cow<'a, str>, } impl<'a> MessageRead<'a> for BazMessage<'a> { fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { Ok(10) => msg.nested = Some(r.read_message::(bytes)?), Ok(16) => msg.b_int64 = r.read_int64(bytes)?, Ok(26) => msg.b_string = r.read_string(bytes).map(Cow::Borrowed)?, Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } } Ok(msg) } } impl<'a> MessageWrite for BazMessage<'a> { fn get_size(&self) -> usize { 0 + self.nested.as_ref().map_or(0, |m| 1 + sizeof_len((m).get_size())) + if self.b_int64 == 0i64 { 0 } else { 1 + sizeof_varint(*(&self.b_int64) as u64) } + if self.b_string == "" { 0 } else { 1 + sizeof_len((&self.b_string).len()) } } fn write_message(&self, w: &mut Writer) -> Result<()> { if let Some(ref s) = self.nested { w.write_with_tag(10, |w| w.write_message(s))?; } if self.b_int64 != 0i64 { w.write_with_tag(16, |w| w.write_int64(*&self.b_int64))?; } if self.b_string != "" { w.write_with_tag(26, |w| w.write_string(&**&self.b_string))?; } Ok(()) } } pub mod mod_BazMessage { use super::*; #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] pub struct Nested { pub f_nested: Option, } impl<'a> MessageRead<'a> for Nested { fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { Ok(10) => msg.f_nested = Some(r.read_message::(bytes)?), Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } } Ok(msg) } } impl MessageWrite for Nested { fn get_size(&self) -> usize { 0 + self.f_nested.as_ref().map_or(0, |m| 1 + sizeof_len((m).get_size())) } fn write_message(&self, w: &mut Writer) -> Result<()> { if let Some(ref s) = self.f_nested { w.write_with_tag(10, |w| w.write_message(s))?; } Ok(()) } } pub mod mod_Nested { use super::*; #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] pub struct NestedMessage { pub f_nested: i32, } impl<'a> MessageRead<'a> for NestedMessage { fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { Ok(8) => msg.f_nested = r.read_int32(bytes)?, Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } } Ok(msg) } } impl MessageWrite for NestedMessage { fn get_size(&self) -> usize { 0 + if self.f_nested == 0i32 { 0 } else { 1 + sizeof_varint(*(&self.f_nested) as u64) } } fn write_message(&self, w: &mut Writer) -> Result<()> { if self.f_nested != 0i32 { w.write_with_tag(8, |w| w.write_int32(*&self.f_nested))?; } Ok(()) } } #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum NestedEnum { Foo = 0, Bar = 1, Baz = 2, } impl Default for NestedEnum { fn default() -> Self { NestedEnum::Foo } } impl From for NestedEnum { fn from(i: i32) -> Self { match i { 0 => NestedEnum::Foo, 1 => NestedEnum::Bar, 2 => NestedEnum::Baz, _ => Self::default(), } } } impl<'a> From<&'a str> for NestedEnum { fn from(s: &'a str) -> Self { match s { "Foo" => NestedEnum::Foo, "Bar" => NestedEnum::Bar, "Baz" => NestedEnum::Baz, _ => Self::default(), } } } } } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] pub struct RepeatedMessage { pub bar_message: Vec, } impl<'a> MessageRead<'a> for RepeatedMessage { fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result { let mut msg = Self::default(); while !r.is_eof() { match r.next_tag(bytes) { Ok(10) => msg.bar_message.push(r.read_message::(bytes)?), Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } } Ok(msg) } } impl MessageWrite for RepeatedMessage { fn get_size(&self) -> usize { 0 + self.bar_message.iter().map(|s| 1 + sizeof_len((s).get_size())).sum::() } fn write_message(&self, w: &mut Writer) -> Result<()> { for s in &self.bar_message { w.write_with_tag(10, |w| w.write_message(s))?; } Ok(()) } } quick-protobuf-0.8.1/examples/pb_rs_v3/data_types_import.proto000064400000000000000000000001161046102023000227510ustar 00000000000000syntax = "proto3"; package a.b; message ImportedMessage { bool i = 1; } quick-protobuf-0.8.1/examples/pb_rs_v3/data_types_unit.proto000064400000000000000000000000641046102023000224200ustar 00000000000000message unit_message { } enum test { a = 10; } quick-protobuf-0.8.1/examples/pb_rs_v3/data_types_unit.rs000064400000000000000000000022361046102023000217040ustar 00000000000000// Automatically generated rust module for 'data_types_unit.proto' file #![allow(non_snake_case)] #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(unused_imports)] #![allow(unknown_lints)] #![allow(clippy::all)] #![cfg_attr(rustfmt, rustfmt_skip)] use quick_protobuf::{BytesReader, Result, MessageInfo, MessageRead, MessageWrite}; use super::*; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum test { a = 10, } impl Default for test { fn default() -> Self { test::a } } impl From for test { fn from(i: i32) -> Self { match i { 10 => test::a, _ => Self::default(), } } } impl<'a> From<&'a str> for test { fn from(s: &'a str) -> Self { match s { "a" => test::a, _ => Self::default(), } } } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Debug, Default, PartialEq, Clone)] pub struct unit_message { } impl<'a> MessageRead<'a> for unit_message { fn from_reader(r: &mut BytesReader, _: &[u8]) -> Result { r.read_to_end(); Ok(Self::default()) } } impl MessageWrite for unit_message { } quick-protobuf-0.8.1/examples/pb_rs_v3/mod.rs000064400000000000000000000001321046102023000172600ustar 00000000000000// Automatically generated mod.rs pub mod data_types; pub mod a; pub mod data_types_unit; quick-protobuf-0.8.1/src/errors.rs000064400000000000000000000053441046102023000152630ustar 00000000000000//! A module to handle all errors /// An error enum #[derive(Debug)] pub enum Error { /// Io error #[cfg(feature = "std")] Io(std::io::Error), /// Io error #[cfg(not(feature = "std"))] Io, /// Utf8 Error Utf8(::core::str::Utf8Error), /// Deprecated feature (in protocol buffer specification) Deprecated(&'static str), /// Unknown wire type UnknownWireType(u8), /// Varint decoding error Varint, /// Error while parsing protocol buffer message #[cfg(feature = "std")] Message(String), /// Unexpected map tag Map(u8), /// Out of data when reading from or writing to a byte buffer UnexpectedEndOfBuffer, /// The supplied output buffer is not large enough to serialize the message OutputBufferTooSmall, } /// A wrapper for `Result` pub type Result = ::core::result::Result; #[cfg(feature = "std")] impl Into for Error { fn into(self) -> ::std::io::Error { match self { Error::Io(x) => x, Error::Utf8(x) => std::io::Error::new(std::io::ErrorKind::InvalidData, x), x => std::io::Error::new(std::io::ErrorKind::Other, x), } } } #[cfg(feature = "std")] impl From for Error { fn from(e: std::io::Error) -> Error { Error::Io(e) } } impl From<::core::str::Utf8Error> for Error { fn from(e: ::core::str::Utf8Error) -> Error { Error::Utf8(e) } } #[cfg(feature = "std")] impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { Error::Io(e) => Some(e), Error::Utf8(e) => Some(e), _ => None, } } } impl core::fmt::Display for Error { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { match self { #[cfg(feature = "std")] Error::Io(e) => write!(f, "{}", e), #[cfg(not(feature = "std"))] Error::Io => write!(f, "IO error"), Error::Utf8(e) => write!(f, "{}", e), Error::Deprecated(feature) => write!(f, "Feature '{}' has been deprecated", feature), Error::UnknownWireType(e) => { write!(f, "Unknown wire type '{}', must be less than 6", e) } Error::Varint => write!(f, "Cannot decode varint"), #[cfg(feature = "std")] Error::Message(msg) => write!(f, "Error while parsing message: {}", msg), Error::Map(tag) => write!(f, "Unexpected map tag: '{}', expecting 1 or 2", tag), Error::UnexpectedEndOfBuffer => write!(f, "Unexpected end of buffer"), Error::OutputBufferTooSmall => write!(f, "Output buffer too small"), } } } quick-protobuf-0.8.1/src/lib.rs000064400000000000000000000012121046102023000145030ustar 00000000000000//! A library to read binary protobuf files //! //! This reader is developed similarly to a pull reader #![deny(missing_docs)] #![allow(dead_code)] #![cfg_attr(not(feature = "std"), no_std)] pub mod errors; pub mod message; pub mod reader; pub mod sizeofs; pub mod writer; pub use crate::errors::{Error, Result}; pub use crate::message::{MessageInfo, MessageRead, MessageWrite}; pub use crate::reader::{deserialize_from_slice, BytesReader}; pub use crate::writer::{serialize_into_slice, BytesWriter, Writer, WriterBackend}; #[cfg(feature = "std")] pub use crate::reader::Reader; #[cfg(feature = "std")] pub use crate::writer::serialize_into_vec; quick-protobuf-0.8.1/src/message.rs000064400000000000000000000030651046102023000153710ustar 00000000000000//! A module to deserialize a `Message` as defined in a .proto file //! //! Creates the struct and implements a reader #[cfg(feature = "std")] use std::fs::File; #[cfg(feature = "std")] use std::io::BufWriter; #[cfg(feature = "std")] use std::path::Path; use crate::errors::Result; use crate::reader::BytesReader; use crate::writer::{Writer, WriterBackend}; /// A trait to handle deserialization based on parsed `Field`s pub trait MessageWrite: Sized { /// Writes `Self` into W writer fn write_message(&self, _: &mut Writer) -> Result<()> { Ok(()) } /// Computes necessary binary size of self once serialized in protobuf fn get_size(&self) -> usize { 0 } /// Writes self into a file #[cfg(feature = "std")] fn write_file>(&self, p: P) -> Result<()> { let file = BufWriter::new(File::create(p)?); let mut writer = Writer::new(file); self.write_message(&mut writer) } } /// A trait to handle deserialization from protocol buffers. pub trait MessageRead<'a>: Sized { /// Constructs an instance of `Self` by reading from the given bytes /// via the given reader. /// /// It does NOT read message length first. If you want to read a variable /// length message, use `BytesReader::read_message` directly fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result; } /// A trait to provide basic information about a given message pub trait MessageInfo { /// Full message path, in form of Module.Message const PATH: &'static str; } quick-protobuf-0.8.1/src/reader.rs000064400000000000000000000565261046102023000152210ustar 00000000000000//! A module to manage protobuf deserialization //! //! There are actually two main *readers* //! - a `BytesReader` which parses data from a `&[u8]` //! - a `Reader` which is a wrapper on `BytesReader` which has its own buffer. It provides //! convenient functions to the user suche as `from_file` //! //! It is advised, for convenience to directly work with a `Reader`. #[cfg(feature = "std")] use std::fs::File; #[cfg(feature = "std")] use std::io::Read; #[cfg(feature = "std")] use std::path::Path; use core::convert::TryFrom; #[cfg(not(feature = "std"))] extern crate alloc; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use crate::errors::{Error, Result}; use crate::message::MessageRead; use byteorder::ByteOrder; use byteorder::LittleEndian as LE; const WIRE_TYPE_VARINT: u8 = 0; const WIRE_TYPE_FIXED64: u8 = 1; const WIRE_TYPE_LENGTH_DELIMITED: u8 = 2; const WIRE_TYPE_START_GROUP: u8 = 3; const WIRE_TYPE_END_GROUP: u8 = 4; const WIRE_TYPE_FIXED32: u8 = 5; /// A struct to read protocol binary files /// /// # Examples /// /// ```rust /// # mod foo_bar { /// # use quick_protobuf::{MessageRead, BytesReader, Result}; /// # pub struct Foo {} /// # pub struct Bar {} /// # pub struct FooBar { pub foos: Vec, pub bars: Vec, } /// # impl<'a> MessageRead<'a> for FooBar { /// # fn from_reader(_: &mut BytesReader, _: &[u8]) -> Result { /// # Ok(FooBar { foos: vec![], bars: vec![] }) /// # } /// # } /// # } /// /// // FooBar is a message generated from a proto file /// // in parcicular it contains a `from_reader` function /// use foo_bar::FooBar; /// use quick_protobuf::{MessageRead, BytesReader}; /// /// fn main() { /// // bytes is a buffer on the data we want to deserialize /// // typically bytes is read from a `Read`: /// // r.read_to_end(&mut bytes).expect("cannot read bytes"); /// let mut bytes: Vec; /// # bytes = vec![]; /// /// // we can build a bytes reader directly out of the bytes /// let mut reader = BytesReader::from_bytes(&bytes); /// /// // now using the generated module decoding is as easy as: /// let foobar = FooBar::from_reader(&mut reader, &bytes).expect("Cannot read FooBar"); /// /// // if instead the buffer contains a length delimited stream of message we could use: /// // while !r.is_eof() { /// // let foobar: FooBar = r.read_message(&bytes).expect(...); /// // ... /// // } /// println!("Found {} foos and {} bars", foobar.foos.len(), foobar.bars.len()); /// } /// ``` #[derive(Debug, Clone, PartialEq, Eq)] pub struct BytesReader { start: usize, end: usize, } impl BytesReader { /// Creates a new reader from chunks of data pub fn from_bytes(bytes: &[u8]) -> BytesReader { BytesReader { start: 0, end: bytes.len(), } } /// Reads next tag, `None` if all bytes have been read #[cfg_attr(std, inline(always))] pub fn next_tag(&mut self, bytes: &[u8]) -> Result { self.read_varint32(bytes) } /// Reads the next byte #[cfg_attr(std, inline(always))] pub fn read_u8(&mut self, bytes: &[u8]) -> Result { let b = bytes.get(self.start).ok_or(Error::UnexpectedEndOfBuffer)?; self.start += 1; Ok(*b) } /// Reads the next varint encoded u64 #[cfg_attr(std, inline(always))] pub fn read_varint32(&mut self, bytes: &[u8]) -> Result { let mut b = self.read_u8(bytes)?; // byte0 if b & 0x80 == 0 { return Ok(b as u32); } let mut r = (b & 0x7f) as u32; b = self.read_u8(bytes)?; // byte1 r |= ((b & 0x7f) as u32) << 7; if b & 0x80 == 0 { return Ok(r); } b = self.read_u8(bytes)?; // byte2 r |= ((b & 0x7f) as u32) << 14; if b & 0x80 == 0 { return Ok(r); } b = self.read_u8(bytes)?; // byte3 r |= ((b & 0x7f) as u32) << 21; if b & 0x80 == 0 { return Ok(r); } b = self.read_u8(bytes)?; // byte4 r |= ((b & 0xf) as u32) << 28; // silently prevent overflow; only mask 0xF if b & 0x80 == 0 { // WARNING ABOUT TRUNCATION // // In this case, byte4 takes the form 0ZZZ_YYYY where: // Y: part of the resulting 32-bit number // Z: beyond 32 bits (excess bits,not used) // // If the Z bits were set, it might indicate that the number being // decoded was intended to be bigger than 32 bits, suggesting an // error somewhere else. // // However, for the sake of consistency with Google's own protobuf // implementation, and also to allow for any efficient use of those // extra bits by users if they wish (this crate is meant for speed // optimization anyway) we shall not check for this here. // // Therefore, THIS FUNCTION SIMPLY IGNORES THE EXTRA BITS, WHICH IS // ESSENTIALLY A SILENT TRUNCATION! return Ok(r); } // ANOTHER WARNING ABOUT TRUNCATION // // Again, we do not check whether the byte representation fits within 32 // bits, and simply ignore extra bytes, CONSTITUTING A SILENT // TRUNCATION! // // Therefore, if the user wants this function to avoid ignoring any // bits/bytes, they need to ensure that the input is a varint // representing a value within EITHER u32 OR i32 range. Since at this // point we are beyond 5 bits, the only possible case is a negative i32 // (since negative numbers are always 10 bytes in protobuf). We must // have exactly 5 bytes more to go. // // Since we know it must be a negative number, and this function is // meant to read 32-bit ints (there is a different function for reading // 64-bit ints), the user might want to take care to ensure that this // negative number is within valid i32 range, i.e. at least // -2,147,483,648. Otherwise, this function simply ignores the extra // bits, essentially constituting a silent truncation! // // What this means in the end is that the user should ensure that the // resulting number, once decoded from the varint format, takes such a // form: // // 11111111_11111111_11111111_11111111_1XXXXXXX_XXXXXXXX_XXXXXXXX_XXXXXXXX // ^(MSB bit 63) ^(bit 31 is set) ^(LSB bit 0) // discards extra bytes for _ in 0..5 { if self.read_u8(bytes)? & 0x80 == 0 { return Ok(r); } } // cannot read more than 10 bytes Err(Error::Varint) } /// Reads the next varint encoded u64 #[cfg_attr(std, inline(always))] pub fn read_varint64(&mut self, bytes: &[u8]) -> Result { // part0 let mut b = self.read_u8(bytes)?; if b & 0x80 == 0 { return Ok(b as u64); } let mut r0 = (b & 0x7f) as u32; b = self.read_u8(bytes)?; r0 |= ((b & 0x7f) as u32) << 7; if b & 0x80 == 0 { return Ok(r0 as u64); } b = self.read_u8(bytes)?; r0 |= ((b & 0x7f) as u32) << 14; if b & 0x80 == 0 { return Ok(r0 as u64); } b = self.read_u8(bytes)?; r0 |= ((b & 0x7f) as u32) << 21; if b & 0x80 == 0 { return Ok(r0 as u64); } // part1 b = self.read_u8(bytes)?; let mut r1 = (b & 0x7f) as u32; if b & 0x80 == 0 { return Ok(r0 as u64 | (r1 as u64) << 28); } b = self.read_u8(bytes)?; r1 |= ((b & 0x7f) as u32) << 7; if b & 0x80 == 0 { return Ok(r0 as u64 | (r1 as u64) << 28); } b = self.read_u8(bytes)?; r1 |= ((b & 0x7f) as u32) << 14; if b & 0x80 == 0 { return Ok(r0 as u64 | (r1 as u64) << 28); } b = self.read_u8(bytes)?; r1 |= ((b & 0x7f) as u32) << 21; if b & 0x80 == 0 { return Ok(r0 as u64 | (r1 as u64) << 28); } // part2 b = self.read_u8(bytes)?; let mut r2 = (b & 0x7f) as u32; if b & 0x80 == 0 { return Ok((r0 as u64 | (r1 as u64) << 28) | (r2 as u64) << 56); } // WARNING ABOUT TRUNCATION: // // For the number to be within valid 64 bit range, some conditions about // this last byte must be met: // 1. This must be the last byte (MSB not set) // 2. No 64-bit overflow (middle 6 bits are beyond 64 bits for the // entire varint, so they cannot be set either) // // However, for the sake of consistency with Google's own protobuf // implementation, and also to allow for any efficient use of those // extra bits by users if they wish (this crate is meant for speed // optimization anyway) we shall not check for this here. // // Therefore, THIS FUNCTION SIMPLY IGNORES THE EXTRA BITS, WHICH IS // ESSENTIALLY A SILENT TRUNCATION! b = self.read_u8(bytes)?; r2 |= (b as u32) << 7; if b & 0x80 == 0 { return Ok((r0 as u64 | (r1 as u64) << 28) | (r2 as u64) << 56); } // cannot read more than 10 bytes Err(Error::Varint) } /// Reads int32 (varint) #[cfg_attr(std, inline)] pub fn read_int32(&mut self, bytes: &[u8]) -> Result { self.read_varint32(bytes).map(|i| i as i32) } /// Reads int64 (varint) #[cfg_attr(std, inline)] pub fn read_int64(&mut self, bytes: &[u8]) -> Result { self.read_varint64(bytes).map(|i| i as i64) } /// Reads uint32 (varint) #[cfg_attr(std, inline)] pub fn read_uint32(&mut self, bytes: &[u8]) -> Result { self.read_varint32(bytes) } /// Reads uint64 (varint) #[cfg_attr(std, inline)] pub fn read_uint64(&mut self, bytes: &[u8]) -> Result { self.read_varint64(bytes) } /// Reads sint32 (varint) #[cfg_attr(std, inline)] pub fn read_sint32(&mut self, bytes: &[u8]) -> Result { // zigzag let n = self.read_varint32(bytes)?; Ok(((n >> 1) as i32) ^ (-((n & 1) as i32))) } /// Reads sint64 (varint) #[cfg_attr(std, inline)] pub fn read_sint64(&mut self, bytes: &[u8]) -> Result { // zigzag let n = self.read_varint64(bytes)?; Ok(((n >> 1) as i64) ^ (-((n & 1) as i64))) } /// Reads fixed64 (little endian u64) #[cfg_attr(std, inline)] fn read_fixed M>(&mut self, bytes: &[u8], len: usize, read: F) -> Result { let v = read( &bytes .get(self.start..self.start + len) .ok_or_else(|| Error::UnexpectedEndOfBuffer)?, ); self.start += len; Ok(v) } /// Reads fixed64 (little endian u64) #[cfg_attr(std, inline)] pub fn read_fixed64(&mut self, bytes: &[u8]) -> Result { self.read_fixed(bytes, 8, LE::read_u64) } /// Reads fixed32 (little endian u32) #[cfg_attr(std, inline)] pub fn read_fixed32(&mut self, bytes: &[u8]) -> Result { self.read_fixed(bytes, 4, LE::read_u32) } /// Reads sfixed64 (little endian i64) #[cfg_attr(std, inline)] pub fn read_sfixed64(&mut self, bytes: &[u8]) -> Result { self.read_fixed(bytes, 8, LE::read_i64) } /// Reads sfixed32 (little endian i32) #[cfg_attr(std, inline)] pub fn read_sfixed32(&mut self, bytes: &[u8]) -> Result { self.read_fixed(bytes, 4, LE::read_i32) } /// Reads float (little endian f32) #[cfg_attr(std, inline)] pub fn read_float(&mut self, bytes: &[u8]) -> Result { self.read_fixed(bytes, 4, LE::read_f32) } /// Reads double (little endian f64) #[cfg_attr(std, inline)] pub fn read_double(&mut self, bytes: &[u8]) -> Result { self.read_fixed(bytes, 8, LE::read_f64) } /// Reads bool (varint, check if == 0) #[cfg_attr(std, inline)] pub fn read_bool(&mut self, bytes: &[u8]) -> Result { self.read_varint32(bytes).map(|i| i != 0) } /// Reads enum, encoded as i32 #[cfg_attr(std, inline)] pub fn read_enum>(&mut self, bytes: &[u8]) -> Result { self.read_int32(bytes).map(|e| e.into()) } /// First reads a varint and use it as size to read a generic object #[cfg_attr(std, inline(always))] fn read_len_varint<'a, M, F>(&mut self, bytes: &'a [u8], read: F) -> Result where F: FnMut(&mut BytesReader, &'a [u8]) -> Result, { let len = self.read_varint32(bytes)? as usize; self.read_len(bytes, read, len) } /// Reads a certain number of bytes specified by len #[cfg_attr(std, inline(always))] fn read_len<'a, M, F>(&mut self, bytes: &'a [u8], mut read: F, len: usize) -> Result where F: FnMut(&mut BytesReader, &'a [u8]) -> Result, { let cur_end = self.end; self.end = self.start + len; let v = read(self, bytes)?; self.start = self.end; self.end = cur_end; Ok(v) } /// Reads bytes (Vec) #[cfg_attr(std, inline)] pub fn read_bytes<'a>(&mut self, bytes: &'a [u8]) -> Result<&'a [u8]> { self.read_len_varint(bytes, |r, b| { b.get(r.start..r.end) .ok_or_else(|| Error::UnexpectedEndOfBuffer) }) } /// Reads string (String) #[cfg_attr(std, inline)] pub fn read_string<'a>(&mut self, bytes: &'a [u8]) -> Result<&'a str> { self.read_len_varint(bytes, |r, b| { b.get(r.start..r.end) .ok_or_else(|| Error::UnexpectedEndOfBuffer) .and_then(|x| ::core::str::from_utf8(x).map_err(|e| e.into())) }) } /// Reads packed repeated field (Vec) /// /// Note: packed field are stored as a variable length chunk of data, while regular repeated /// fields behaves like an iterator, yielding their tag everytime #[cfg_attr(std, inline)] pub fn read_packed<'a, M, F>(&mut self, bytes: &'a [u8], mut read: F) -> Result> where F: FnMut(&mut BytesReader, &'a [u8]) -> Result, { self.read_len_varint(bytes, |r, b| { let mut v = Vec::new(); while !r.is_eof() { v.push(read(r, b)?); } Ok(v) }) } /// Reads packed repeated field where M can directly be transmutted from raw bytes /// /// Note: packed field are stored as a variable length chunk of data, while regular repeated /// fields behaves like an iterator, yielding their tag everytime #[cfg_attr(std, inline)] pub fn read_packed_fixed<'a, M>(&mut self, bytes: &'a [u8]) -> Result<&'a [M]> { let len = self.read_varint32(bytes)? as usize; if self.len() < len { return Err(Error::UnexpectedEndOfBuffer); } let n = len / ::core::mem::size_of::(); let slice = unsafe { ::core::slice::from_raw_parts( bytes.get_unchecked(self.start) as *const u8 as *const M, n, ) }; self.start += len; Ok(slice) } /// Reads a nested message /// /// First reads a varint and interprets it as the length of the message #[cfg_attr(std, inline)] pub fn read_message<'a, M>(&mut self, bytes: &'a [u8]) -> Result where M: MessageRead<'a>, { self.read_len_varint(bytes, M::from_reader) } /// Reads a nested message /// /// Reads just the message and does not try to read it's size first. /// * 'len' - The length of the message to be read. #[cfg_attr(std, inline)] pub fn read_message_by_len<'a, M>(&mut self, bytes: &'a [u8], len: usize) -> Result where M: MessageRead<'a>, { self.read_len(bytes, M::from_reader, len) } /// Reads a map item: (key, value) #[cfg_attr(std, inline)] pub fn read_map<'a, K, V, F, G>( &mut self, bytes: &'a [u8], mut read_key: F, mut read_val: G, ) -> Result<(K, V)> where F: FnMut(&mut BytesReader, &'a [u8]) -> Result, G: FnMut(&mut BytesReader, &'a [u8]) -> Result, K: ::core::fmt::Debug + Default, V: ::core::fmt::Debug + Default, { self.read_len_varint(bytes, |r, bytes| { let mut k = K::default(); let mut v = V::default(); while !r.is_eof() { let t = r.read_u8(bytes)?; match t >> 3 { 1 => k = read_key(r, bytes)?, 2 => v = read_val(r, bytes)?, t => return Err(Error::Map(t)), } } Ok((k, v)) }) } /// Reads unknown data, based on its tag value (which itself gives us the wire_type value) #[cfg_attr(std, inline)] pub fn read_unknown(&mut self, bytes: &[u8], tag_value: u32) -> Result<()> { // Since `read.varint64()` calls `read_u8()`, which increments // `self.start`, we don't need to manually increment `self.start` in // control flows that either call `read_varint64()` or error out. let offset = match (tag_value & 0x7) as u8 { WIRE_TYPE_VARINT => { self.read_varint64(bytes)?; return Ok(()); } WIRE_TYPE_FIXED64 => 8, WIRE_TYPE_FIXED32 => 4, WIRE_TYPE_LENGTH_DELIMITED => { usize::try_from(self.read_varint64(bytes)?).map_err(|_| Error::Varint)? } WIRE_TYPE_START_GROUP | WIRE_TYPE_END_GROUP => { return Err(Error::Deprecated("group")); } t => { return Err(Error::UnknownWireType(t)); } }; // Meant to prevent overflowing. Comparison used is *strictly* lesser // since `self.end` is given by `len()`; i.e. `self.end` is 1 more than // highest index if self.end - self.start < offset { Err(Error::Varint) } else { self.start += offset; Ok(()) } } /// Gets the remaining length of bytes not read yet #[cfg_attr(std, inline(always))] pub fn len(&self) -> usize { self.end - self.start } /// Checks if `self.len == 0` #[cfg_attr(std, inline(always))] pub fn is_eof(&self) -> bool { self.start == self.end } /// Advance inner cursor to the end pub fn read_to_end(&mut self) { self.start = self.end; } } /// A struct to read protobuf data /// /// Contrary to `BytesReader`, this struct will own a buffer /// /// # Examples /// /// ```rust,should_panic /// # mod foo_bar { /// # use quick_protobuf::{MessageRead, BytesReader, Result}; /// # pub struct Foo {} /// # pub struct Bar {} /// # pub struct FooBar { pub foos: Vec, pub bars: Vec, } /// # impl<'a> MessageRead<'a> for FooBar { /// # fn from_reader(_: &mut BytesReader, _: &[u8]) -> Result { /// # Ok(FooBar { foos: vec![], bars: vec![] }) /// # } /// # } /// # } /// /// // FooBar is a message generated from a proto file /// // In particular it implements the `MessageRead` trait, containing a `from_reader` function. /// use foo_bar::FooBar; /// use quick_protobuf::Reader; /// /// fn main() { /// // create a reader, which will parse the protobuf binary file and pop events /// // this reader will read the entire file into an internal buffer /// let mut reader = Reader::from_file("/path/to/binary/protobuf.bin") /// .expect("Cannot read input file"); /// /// // Use the generated module fns with the reader to convert your data into rust structs. /// // /// // Depending on your input file, the message can or not be prefixed with the encoded length /// // for instance, a *stream* which contains several messages generally split them using this /// // technique (see https://developers.google.com/protocol-buffers/docs/techniques#streaming) /// // /// // To read a message without a length prefix you can directly call `FooBar::from_reader`: /// // let foobar = reader.read(FooBar::from_reader).expect("Cannot read FooBar message"); /// // /// // Else to read a length then a message, you can use: /// let foobar: FooBar = reader.read(|r, b| r.read_message(b)) /// .expect("Cannot read FooBar message"); /// // Reader::read_message uses `FooBar::from_reader` internally through the `MessageRead` /// // trait. /// /// println!("Found {} foos and {} bars!", foobar.foos.len(), foobar.bars.len()); /// } /// ``` pub struct Reader { buffer: Vec, inner: BytesReader, } impl Reader { /// Creates a new `Reader` #[cfg(feature = "std")] pub fn from_reader(mut r: R, capacity: usize) -> Result { let mut buf = Vec::with_capacity(capacity); unsafe { buf.set_len(capacity); } buf.shrink_to_fit(); r.read_exact(&mut buf)?; Ok(Reader::from_bytes(buf)) } /// Creates a new `Reader` out of a file path #[cfg(feature = "std")] pub fn from_file>(src: P) -> Result { let len = src.as_ref().metadata().unwrap().len() as usize; let f = File::open(src)?; Reader::from_reader(f, len) } /// Creates a new reader consuming the bytes pub fn from_bytes(bytes: Vec) -> Reader { let reader = BytesReader { start: 0, end: bytes.len(), }; Reader { buffer: bytes, inner: reader, } } /// Run a `BytesReader` dependent function #[cfg_attr(std, inline)] pub fn read<'a, M, F>(&'a mut self, mut read: F) -> Result where F: FnMut(&mut BytesReader, &'a [u8]) -> Result, { read(&mut self.inner, &self.buffer) } /// Gets the inner `BytesReader` pub fn inner(&mut self) -> &mut BytesReader { &mut self.inner } /// Gets the buffer used internally pub fn buffer(&self) -> &[u8] { &self.buffer } } /// Deserialize a `MessageRead from a `&[u8]` pub fn deserialize_from_slice<'a, M: MessageRead<'a>>(bytes: &'a [u8]) -> Result { let mut reader = BytesReader::from_bytes(&bytes); reader.read_message::(&bytes) } #[test] fn test_varint() { let data = [0x96, 0x01]; let mut r = BytesReader::from_bytes(&data[..]); assert_eq!(150, r.read_varint32(&data[..]).unwrap()); assert!(r.is_eof()); } #[test] fn read_size_overflowing_unknown() { let bytes = &[ 200, 250, 35, // varint tag with WIRE_TYPE_VARINT -- 589128 // // 47, // varint itself // // 250, 36, // varint tag with WIRE_TYPE_LENGTH_DELIMITED -- 4730 // // 255, 255, 255, 255, 255, 255, 255, 255, 255, 3, // huge 10-byte length // // 255, 255, 227, // unused extra bytes ]; let mut r = BytesReader::from_bytes(bytes); assert!(!r.is_eof()); assert_eq!(r.next_tag(bytes).unwrap(), 589128); r.read_unknown(bytes, 589128).unwrap(); assert!(!r.is_eof()); assert_eq!(r.next_tag(bytes).unwrap(), 4730); let e = r.read_unknown(bytes, 4730).unwrap_err(); assert!(matches!(e, Error::Varint), "{:?}", e); } quick-protobuf-0.8.1/src/sizeofs.rs000064400000000000000000000037721046102023000154340ustar 00000000000000//! A module to compute the binary size of data once encoded //! //! This module is used primilarly when implementing the `MessageWrite::get_size` /// Computes the binary size of the varint encoded u64 /// /// https://developers.google.com/protocol-buffers/docs/encoding pub fn sizeof_varint(v: u64) -> usize { match v { 0x0..=0x7F => 1, 0x80..=0x3FFF => 2, 0x4000..=0x1FFFFF => 3, 0x200000..=0xFFFFFFF => 4, 0x10000000..=0x7FFFFFFFF => 5, 0x0800000000..=0x3FFFFFFFFFF => 6, 0x040000000000..=0x1FFFFFFFFFFFF => 7, 0x02000000000000..=0xFFFFFFFFFFFFFF => 8, 0x0100000000000000..=0x7FFFFFFFFFFFFFFF => 9, _ => 10, } } /// Computes the binary size of a variable length chunk of data (wire type 2) /// /// The total size is the varint encoded length size plus the length itself /// https://developers.google.com/protocol-buffers/docs/encoding pub fn sizeof_len(len: usize) -> usize { sizeof_varint(len as u64) + len } /// Computes the binary size of the varint encoded i32 pub fn sizeof_int32(v: i32) -> usize { sizeof_varint(v as u64) } /// Computes the binary size of the varint encoded i64 pub fn sizeof_int64(v: i64) -> usize { sizeof_varint(v as u64) } /// Computes the binary size of the varint encoded uint32 pub fn sizeof_uint32(v: u32) -> usize { sizeof_varint(v as u64) } /// Computes the binary size of the varint encoded uint64 pub fn sizeof_uint64(v: u64) -> usize { sizeof_varint(v) } /// Computes the binary size of the varint encoded sint32 pub fn sizeof_sint32(v: i32) -> usize { sizeof_varint(((v << 1) ^ (v >> 31)) as u64) } /// Computes the binary size of the varint encoded sint64 pub fn sizeof_sint64(v: i64) -> usize { sizeof_varint(((v << 1) ^ (v >> 63)) as u64) } /// Computes the binary size of the varint encoded bool (always = 1) pub fn sizeof_bool(_: bool) -> usize { 1 } /// Computes the binary size of the varint encoded enum pub fn sizeof_enum(v: i32) -> usize { sizeof_int32(v) } quick-protobuf-0.8.1/src/writer.rs000064400000000000000000000421611046102023000152610ustar 00000000000000//! A module to manage protobuf serialization use crate::errors::{Error, Result}; use crate::message::MessageWrite; use byteorder::{ByteOrder, LittleEndian as LE}; #[cfg(feature = "std")] use byteorder::WriteBytesExt; /// A struct to write protobuf messages /// /// # Examples /// /// ```rust /// // an automatically generated module which is in a separate file in general /// mod foo_bar { /// # use quick_protobuf::{MessageWrite, Writer, WriterBackend, Result}; /// # use std::borrow::Cow; /// pub struct Foo<'a> { pub name: Option>, } /// pub struct Bar { pub id: Option } /// pub struct FooBar<'a> { pub foos: Vec>, pub bars: Vec, } /// impl<'a> MessageWrite for FooBar<'a> { /// // implements /// // fn get_size(&self) -> usize { ... } /// // fn write_message(&self, r: &mut Writer) -> Result<()> { ... } /// # fn get_size(&self) -> usize { 0 } /// # fn write_message(&self, _: &mut Writer) -> Result<()> { Ok(()) } /// } /// } /// /// // FooBar is a message generated from a proto file /// // in parcicular it contains a `write_message` function /// use foo_bar::{FooBar, Foo, Bar}; /// use std::borrow::Cow; /// use quick_protobuf::Writer; /// /// fn main() { /// // let mut r = File::create("...").expect("Cannot create file"); /// // for the sake of example, we'll use a simpler struct which impl `Write` /// let mut r = Vec::new(); /// let mut writer = Writer::new(&mut r); /// /// // manually generates a FooBar for the example /// let foobar = FooBar { /// foos: vec![Foo { name: Some(Cow::Borrowed("test!")) }, Foo { name: None }], /// bars: vec![Bar { id: Some(43) }, Bar { id: None }], /// }; /// /// // now using the generated module /// writer.write_message(&foobar).expect("Cannot write FooBar"); /// } /// ``` pub struct Writer { inner: W, } impl Writer { /// Creates a new `ProtobufWriter` pub fn new(w: W) -> Writer { Writer { inner: w } } /// Writes a byte which is NOT internally coded as a `varint` pub fn write_u8(&mut self, byte: u8) -> Result<()> { self.inner.pb_write_u8(byte) } /// Writes a `varint` (compacted `u64`) pub fn write_varint(&mut self, mut v: u64) -> Result<()> { while v > 0x7F { self.inner.pb_write_u8(((v as u8) & 0x7F) | 0x80)?; v >>= 7; } self.inner.pb_write_u8(v as u8) } /// Writes a tag, which represents both the field number and the wire type #[cfg_attr(std, inline(always))] pub fn write_tag(&mut self, tag: u32) -> Result<()> { self.write_varint(tag as u64) } /// Writes a `int32` which is internally coded as a `varint` #[cfg_attr(std, inline(always))] pub fn write_int32(&mut self, v: i32) -> Result<()> { self.write_varint(v as u64) } /// Writes a `int64` which is internally coded as a `varint` #[cfg_attr(std, inline(always))] pub fn write_int64(&mut self, v: i64) -> Result<()> { self.write_varint(v as u64) } /// Writes a `uint32` which is internally coded as a `varint` #[cfg_attr(std, inline(always))] pub fn write_uint32(&mut self, v: u32) -> Result<()> { self.write_varint(v as u64) } /// Writes a `uint64` which is internally coded as a `varint` #[cfg_attr(std, inline(always))] pub fn write_uint64(&mut self, v: u64) -> Result<()> { self.write_varint(v) } /// Writes a `sint32` which is internally coded as a `varint` #[cfg_attr(std, inline(always))] pub fn write_sint32(&mut self, v: i32) -> Result<()> { self.write_varint(((v << 1) ^ (v >> 31)) as u64) } /// Writes a `sint64` which is internally coded as a `varint` #[cfg_attr(std, inline(always))] pub fn write_sint64(&mut self, v: i64) -> Result<()> { self.write_varint(((v << 1) ^ (v >> 63)) as u64) } /// Writes a `fixed64` which is little endian coded `u64` #[cfg_attr(std, inline(always))] pub fn write_fixed64(&mut self, v: u64) -> Result<()> { self.inner.pb_write_u64(v) } /// Writes a `fixed32` which is little endian coded `u32` #[cfg_attr(std, inline(always))] pub fn write_fixed32(&mut self, v: u32) -> Result<()> { self.inner.pb_write_u32(v) } /// Writes a `sfixed64` which is little endian coded `i64` #[cfg_attr(std, inline(always))] pub fn write_sfixed64(&mut self, v: i64) -> Result<()> { self.inner.pb_write_i64(v) } /// Writes a `sfixed32` which is little endian coded `i32` #[cfg_attr(std, inline(always))] pub fn write_sfixed32(&mut self, v: i32) -> Result<()> { self.inner.pb_write_i32(v) } /// Writes a `float` #[cfg_attr(std, inline(always))] pub fn write_float(&mut self, v: f32) -> Result<()> { self.inner.pb_write_f32(v) } /// Writes a `double` #[cfg_attr(std, inline(always))] pub fn write_double(&mut self, v: f64) -> Result<()> { self.inner.pb_write_f64(v) } /// Writes a `bool` 1 = true, 0 = false #[cfg_attr(std, inline(always))] pub fn write_bool(&mut self, v: bool) -> Result<()> { self.inner.pb_write_u8(if v { 1 } else { 0 }) } /// Writes an `enum` converting it to a `i32` first #[cfg_attr(std, inline(always))] pub fn write_enum(&mut self, v: i32) -> Result<()> { self.write_int32(v) } /// Writes `bytes`: length first then the chunk of data #[cfg_attr(std, inline(always))] pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<()> { self.write_varint(bytes.len() as u64)?; self.inner.pb_write_all(bytes) } /// Writes `string`: length first then the chunk of data #[cfg_attr(std, inline(always))] pub fn write_string(&mut self, s: &str) -> Result<()> { self.write_bytes(s.as_bytes()) } /// Writes packed repeated field: length first then the chunk of data pub fn write_packed(&mut self, v: &[M], mut write: F, size: &S) -> Result<()> where F: FnMut(&mut Self, &M) -> Result<()>, S: Fn(&M) -> usize, { if v.is_empty() { return Ok(()); } let len: usize = v.iter().map(|m| size(m)).sum(); self.write_varint(len as u64)?; for m in v { write(self, m)?; } Ok(()) } /// Writes packed repeated field when we know the size of items /// /// `item_size` is internally used to compute the total length /// As the length is fixed (and the same as rust internal representation, we can directly dump /// all data at once #[cfg_attr(std, inline)] pub fn write_packed_fixed(&mut self, v: &[M]) -> Result<()> { let len = v.len() * ::core::mem::size_of::(); let bytes = unsafe { ::core::slice::from_raw_parts(v.as_ptr() as *const u8, len) }; self.write_bytes(bytes) } /// Writes a message which implements `MessageWrite` #[cfg_attr(std, inline)] pub fn write_message(&mut self, m: &M) -> Result<()> { let len = m.get_size(); self.write_varint(len as u64)?; m.write_message(self) } /// Writes another item prefixed with tag #[cfg_attr(std, inline)] pub fn write_with_tag(&mut self, tag: u32, mut write: F) -> Result<()> where F: FnMut(&mut Self) -> Result<()>, { self.write_tag(tag)?; write(self) } /// Writes tag then repeated field /// /// If array is empty, then do nothing (do not even write the tag) pub fn write_packed_with_tag( &mut self, tag: u32, v: &[M], mut write: F, size: &S, ) -> Result<()> where F: FnMut(&mut Self, &M) -> Result<()>, S: Fn(&M) -> usize, { if v.is_empty() { return Ok(()); } self.write_tag(tag)?; let len: usize = v.iter().map(|m| size(m)).sum(); self.write_varint(len as u64)?; for m in v { write(self, m)?; } Ok(()) } /// Writes tag then repeated field /// /// If array is empty, then do nothing (do not even write the tag) pub fn write_packed_fixed_with_tag(&mut self, tag: u32, v: &[M]) -> Result<()> { if v.is_empty() { return Ok(()); } self.write_tag(tag)?; let len = ::core::mem::size_of::() * v.len(); let bytes = unsafe { ::core::slice::from_raw_parts(v.as_ptr() as *const u8, len) }; self.write_bytes(bytes) } /// Writes tag then repeated field with fixed length item size /// /// If array is empty, then do nothing (do not even write the tag) pub fn write_packed_fixed_size_with_tag( &mut self, tag: u32, v: &[M], item_size: usize, ) -> Result<()> { if v.is_empty() { return Ok(()); } self.write_tag(tag)?; let len = v.len() * item_size; let bytes = unsafe { ::core::slice::from_raw_parts(v as *const [M] as *const M as *const u8, len) }; self.write_bytes(bytes) } /// Write entire map pub fn write_map( &mut self, size: usize, tag_key: u32, mut write_key: FK, tag_val: u32, mut write_val: FV, ) -> Result<()> where FK: FnMut(&mut Self) -> Result<()>, FV: FnMut(&mut Self) -> Result<()>, { self.write_varint(size as u64)?; self.write_tag(tag_key)?; write_key(self)?; self.write_tag(tag_val)?; write_val(self) } } /// Serialize a `MessageWrite` into a `Vec` #[cfg(feature = "std")] pub fn serialize_into_vec(message: &M) -> Result> { let len = message.get_size(); let mut v = Vec::with_capacity(crate::sizeofs::sizeof_len(len)); { let mut writer = Writer::new(&mut v); writer.write_message(message)?; } Ok(v) } /// Serialize a `MessageWrite` into a byte slice pub fn serialize_into_slice(message: &M, out: &mut [u8]) -> Result<()> { let len = message.get_size(); if out.len() < crate::sizeofs::sizeof_len(len) { return Err(Error::OutputBufferTooSmall); } { let mut writer = Writer::new(BytesWriter::new(out)); writer.write_message(message)?; } Ok(()) } /// Writer backend abstraction pub trait WriterBackend { /// Write a u8 fn pb_write_u8(&mut self, x: u8) -> Result<()>; /// Write a u32 fn pb_write_u32(&mut self, x: u32) -> Result<()>; /// Write a i32 fn pb_write_i32(&mut self, x: i32) -> Result<()>; /// Write a f32 fn pb_write_f32(&mut self, x: f32) -> Result<()>; /// Write a u64 fn pb_write_u64(&mut self, x: u64) -> Result<()>; /// Write a i64 fn pb_write_i64(&mut self, x: i64) -> Result<()>; /// Write a f64 fn pb_write_f64(&mut self, x: f64) -> Result<()>; /// Write all bytes in buf fn pb_write_all(&mut self, buf: &[u8]) -> Result<()>; } /// A writer backend for byte buffers pub struct BytesWriter<'a> { buf: &'a mut [u8], cursor: usize, } impl<'a> BytesWriter<'a> { /// Create a new BytesWriter to write into `buf` pub fn new(buf: &'a mut [u8]) -> BytesWriter<'a> { BytesWriter { buf, cursor: 0 } } } impl<'a> WriterBackend for BytesWriter<'a> { #[cfg_attr(std, inline(always))] fn pb_write_u8(&mut self, x: u8) -> Result<()> { if self.buf.len() - self.cursor < 1 { Err(Error::UnexpectedEndOfBuffer) } else { self.buf[self.cursor] = x; self.cursor += 1; Ok(()) } } #[cfg_attr(std, inline(always))] fn pb_write_u32(&mut self, x: u32) -> Result<()> { if self.buf.len() - self.cursor < 4 { Err(Error::UnexpectedEndOfBuffer) } else { LE::write_u32(&mut self.buf[self.cursor..], x); self.cursor += 4; Ok(()) } } #[cfg_attr(std, inline(always))] fn pb_write_i32(&mut self, x: i32) -> Result<()> { if self.buf.len() - self.cursor < 4 { Err(Error::UnexpectedEndOfBuffer) } else { LE::write_i32(&mut self.buf[self.cursor..], x); self.cursor += 4; Ok(()) } } #[cfg_attr(std, inline(always))] fn pb_write_f32(&mut self, x: f32) -> Result<()> { if self.buf.len() - self.cursor < 4 { Err(Error::UnexpectedEndOfBuffer) } else { LE::write_f32(&mut self.buf[self.cursor..], x); self.cursor += 4; Ok(()) } } #[cfg_attr(std, inline(always))] fn pb_write_u64(&mut self, x: u64) -> Result<()> { if self.buf.len() - self.cursor < 8 { Err(Error::UnexpectedEndOfBuffer) } else { LE::write_u64(&mut self.buf[self.cursor..], x); self.cursor += 8; Ok(()) } } #[cfg_attr(std, inline(always))] fn pb_write_i64(&mut self, x: i64) -> Result<()> { if self.buf.len() - self.cursor < 8 { Err(Error::UnexpectedEndOfBuffer) } else { LE::write_i64(&mut self.buf[self.cursor..], x); self.cursor += 8; Ok(()) } } #[cfg_attr(std, inline(always))] fn pb_write_f64(&mut self, x: f64) -> Result<()> { if self.buf.len() - self.cursor < 8 { Err(Error::UnexpectedEndOfBuffer) } else { LE::write_f64(&mut self.buf[self.cursor..], x); self.cursor += 8; Ok(()) } } #[cfg_attr(std, inline(always))] fn pb_write_all(&mut self, buf: &[u8]) -> Result<()> { if self.buf.len() - self.cursor < buf.len() { Err(Error::UnexpectedEndOfBuffer) } else { self.buf[self.cursor..(self.cursor + buf.len())].copy_from_slice(buf); self.cursor += buf.len(); Ok(()) } } } #[cfg(feature = "std")] impl WriterBackend for W { #[inline(always)] fn pb_write_u8(&mut self, x: u8) -> Result<()> { self.write_u8(x).map_err(|e| e.into()) } #[inline(always)] fn pb_write_u32(&mut self, x: u32) -> Result<()> { self.write_u32::(x).map_err(|e| e.into()) } #[inline(always)] fn pb_write_i32(&mut self, x: i32) -> Result<()> { self.write_i32::(x).map_err(|e| e.into()) } #[inline(always)] fn pb_write_f32(&mut self, x: f32) -> Result<()> { self.write_f32::(x).map_err(|e| e.into()) } #[inline(always)] fn pb_write_u64(&mut self, x: u64) -> Result<()> { self.write_u64::(x).map_err(|e| e.into()) } #[inline(always)] fn pb_write_i64(&mut self, x: i64) -> Result<()> { self.write_i64::(x).map_err(|e| e.into()) } #[inline(always)] fn pb_write_f64(&mut self, x: f64) -> Result<()> { self.write_f64::(x).map_err(|e| e.into()) } #[inline(always)] fn pb_write_all(&mut self, buf: &[u8]) -> Result<()> { self.write_all(buf).map_err(|e| e.into()) } } #[test] fn test_issue_222() { // remember that `serialize_into_vec()` and `serialize_into_slice()` add a // length prefix in addition to writing the message itself; important for // when you look at the buffer sizes and errors thrown in this test struct TestMsg {} impl MessageWrite for TestMsg { fn write_message(&self, w: &mut Writer) -> Result<()> { let bytes = [0x08u8, 0x96u8, 0x01u8]; for b in bytes { // use `write_u8()` in loop because some other functions have // hidden writes (length prefixes etc.) inside them. w.write_u8(b)?; } Ok(()) } fn get_size(&self) -> usize { 3 // corresponding to `bytes` above } } let msg = TestMsg {}; let v = serialize_into_vec(&msg).unwrap(); // We would really like to assert that the vector `v` WITHIN // `serialize_into_vec()` does not get its capacity modified after // initializion with `with_capacity()`, but the only way to do that would be // to put an assert within `serialize_into_vec()` itself, which isn't a // pattern seen in this project. // // Instead, we do this. If this check fails, it definitely means that the // capacity setting in `serialize_into_vec()` is suboptimal, but passing // doesn't guarantee that it is optimal. assert_eq!(v.len(), v.capacity()); let mut buf_len_2 = vec![0x00u8, 0x00u8]; let mut buf_len_3 = vec![0x00u8, 0x00u8, 0x00u8]; let mut buf_len_4 = vec![0x00u8, 0x00u8, 0x00u8, 0x00u8]; assert!(matches!( serialize_into_slice(&msg, buf_len_2.as_mut_slice()), Err(Error::OutputBufferTooSmall) )); assert!(matches!( // the salient case in issue 222; before bugfix this would have been // Err(Error::UnexpectedEndOfBuffer) serialize_into_slice(&msg, buf_len_3.as_mut_slice()), Err(Error::OutputBufferTooSmall) )); assert!(matches!( serialize_into_slice(&msg, buf_len_4.as_mut_slice()), Ok(()) )); } quick-protobuf-0.8.1/tests/packed_primitives/person.proto000064400000000000000000000004011046102023000220360ustar 00000000000000enum City { LONDON = 0; PARIS = 1; } message Address { City city = 1; } message Person { Address address = 2; repeated string names = 3; } message PersonPacked { Address address = 2; repeated string names = 3 [packed=true]; } quick-protobuf-0.8.1/tests/rust_proto.rs000064400000000000000000000001101046102023000165240ustar 00000000000000extern crate quick_protobuf; mod packed_primitives; mod rust_protobuf; quick-protobuf-0.8.1/tests/rust_protobuf/common/mesos.proto000064400000000000000000003660421046102023000224010ustar 00000000000000// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. syntax = "proto2"; package mesos.v1; option java_package = "org.apache.mesos.v1"; option java_outer_classname = "Protos"; /** * Status is used to indicate the state of the scheduler and executor * driver after function calls. */ enum Status { DRIVER_NOT_STARTED = 1; DRIVER_RUNNING = 2; DRIVER_ABORTED = 3; DRIVER_STOPPED = 4; } /** * A unique ID assigned to a framework. A framework can reuse this ID * in order to do failover (see MesosSchedulerDriver). */ message FrameworkID { required string value = 1; } /** * A unique ID assigned to an offer. */ message OfferID { required string value = 1; } /** * A unique ID assigned to an agent. Currently, an agent gets a new ID * whenever it (re)registers with Mesos. Framework writers shouldn't * assume any binding between an agent ID and and a hostname. */ message AgentID { required string value = 1; } /** * A framework-generated ID to distinguish a task. The ID must remain * unique while the task is active. A framework can reuse an ID _only_ * if the previous task with the same ID has reached a terminal state * (e.g., TASK_FINISHED, TASK_KILLED, etc.). However, reusing task IDs * is strongly discouraged (MESOS-2198). */ message TaskID { required string value = 1; } /** * A framework-generated ID to distinguish an executor. Only one * executor with the same ID can be active on the same agent at a * time. However, reusing executor IDs is discouraged. */ message ExecutorID { required string value = 1; } /** * ID used to uniquely identify a container. If the `parent` is not * specified, the ID is a UUID generated by the agent to uniquely * identify the container of an executor run. If the `parent` field is * specified, it represents a nested container. */ message ContainerID { required string value = 1; optional ContainerID parent = 2; } /** * A unique ID assigned to a resource provider. Currently, a resource * provider gets a new ID whenever it (re)registers with Mesos. */ message ResourceProviderID { required string value = 1; } /** * A framework-generated ID to distinguish an operation. The ID * must be unique within the framework. */ message OperationID { required string value = 1; } /** * Represents time since the epoch, in nanoseconds. */ message TimeInfo { required int64 nanoseconds = 1; } /** * Represents duration in nanoseconds. */ message DurationInfo { required int64 nanoseconds = 1; } /** * A network address. * * TODO(bmahler): Use this more widely. */ message Address { // May contain a hostname, IP address, or both. optional string hostname = 1; optional string ip = 2; required int32 port = 3; } /** * Represents a URL. */ message URL { required string scheme = 1; required Address address = 2; optional string path = 3; repeated Parameter query = 4; optional string fragment = 5; } /** * Represents an interval, from a given start time over a given duration. * This interval pertains to an unavailability event, such as maintenance, * and is not a generic interval. */ message Unavailability { required TimeInfo start = 1; // When added to `start`, this represents the end of the interval. // If unspecified, the duration is assumed to be infinite. optional DurationInfo duration = 2; // TODO(josephw): Add additional fields for expressing the purpose and // urgency of the unavailability event. } /** * Represents a single machine, which may hold one or more agents. * * NOTE: In order to match an agent to a machine, both the `hostname` and * `ip` must match the values advertised by the agent to the master. * Hostname is not case-sensitive. */ message MachineID { optional string hostname = 1; optional string ip = 2; } /** * Holds information about a single machine, its `mode`, and any other * relevant information which may affect the behavior of the machine. */ message MachineInfo { // Describes the several states that a machine can be in. A `Mode` // applies to a machine and to all associated agents on the machine. enum Mode { // In this mode, a machine is behaving normally; // offering resources, executing tasks, etc. UP = 1; // In this mode, all agents on the machine are expected to cooperate with // frameworks to drain resources. In general, draining is done ahead of // a pending `unavailability`. The resources should be drained so as to // maximize utilization prior to the maintenance but without knowingly // violating the frameworks' requirements. DRAINING = 2; // In this mode, a machine is not running any tasks and will not offer // any of its resources. Agents on the machine will not be allowed to // register with the master. DOWN = 3; } required MachineID id = 1; optional Mode mode = 2; // Signifies that the machine may be unavailable during the given interval. // See comments in `Unavailability` and for the `unavailability` fields // in `Offer` and `InverseOffer` for more information. optional Unavailability unavailability = 3; } /** * Describes a framework. */ message FrameworkInfo { // Used to determine the Unix user that an executor or task should be // launched as. // // When using the MesosSchedulerDriver, if the field is set to an // empty string, it will automagically set it to the current user. // // When using the HTTP Scheduler API, the user has to be set // explicitly. required string user = 1; // Name of the framework that shows up in the Mesos Web UI. required string name = 2; // Note that 'id' is only available after a framework has // registered, however, it is included here in order to facilitate // scheduler failover (i.e., if it is set then the // MesosSchedulerDriver expects the scheduler is performing // failover). optional FrameworkID id = 3; // The amount of time (in seconds) that the master will wait for the // scheduler to failover before it tears down the framework by // killing all its tasks/executors. This should be non-zero if a // framework expects to reconnect after a failure and not lose its // tasks/executors. // // NOTE: To avoid accidental destruction of tasks, production // frameworks typically set this to a large value (e.g., 1 week). optional double failover_timeout = 4 [default = 0.0]; // If set, agents running tasks started by this framework will write // the framework pid, executor pids and status updates to disk. If // the agent exits (e.g., due to a crash or as part of upgrading // Mesos), this checkpointed data allows the restarted agent to // reconnect to executors that were started by the old instance of // the agent. Enabling checkpointing improves fault tolerance, at // the cost of a (usually small) increase in disk I/O. optional bool checkpoint = 5 [default = false]; // Roles are the entities to which allocations are made. // The framework must have at least one role in order to // be offered resources. Note that `role` is deprecated // in favor of `roles` and only one of these fields must // be used. Since we cannot distinguish between empty // `roles` and the default unset `role`, we require that // frameworks set the `MULTI_ROLE` capability if // setting the `roles` field. optional string role = 6 [default = "*", deprecated=true]; repeated string roles = 12; // Used to indicate the current host from which the scheduler is // registered in the Mesos Web UI. If set to an empty string Mesos // will automagically set it to the current hostname if one is // available. optional string hostname = 7; // This field should match the credential's principal the framework // uses for authentication. This field is used for framework API // rate limiting and dynamic reservations. It should be set even // if authentication is not enabled if these features are desired. optional string principal = 8; // This field allows a framework to advertise its web UI, so that // the Mesos web UI can link to it. It is expected to be a full URL, // for example http://my-scheduler.example.com:8080/. optional string webui_url = 9; message Capability { enum Type { // This must be the first enum value in this list, to // ensure that if 'type' is not set, the default value // is UNKNOWN. This enables enum values to be added // in a backwards-compatible way. See: MESOS-4997. UNKNOWN = 0; // Receive offers with revocable resources. See 'Resource' // message for details. REVOCABLE_RESOURCES = 1; // Receive the TASK_KILLING TaskState when a task is being // killed by an executor. The executor will examine this // capability to determine whether it can send TASK_KILLING. TASK_KILLING_STATE = 2; // Indicates whether the framework is aware of GPU resources. // Frameworks that are aware of GPU resources are expected to // avoid placing non-GPU workloads on GPU agents, in order // to avoid occupying a GPU agent and preventing GPU workloads // from running! Currently, if a framework is unaware of GPU // resources, it will not be offered *any* of the resources on // an agent with GPUs. This restriction is in place because we // do not have a revocation mechanism that ensures GPU workloads // can evict GPU agent occupants if necessary. // // TODO(bmahler): As we add revocation we can relax the // restriction here. See MESOS-5634 for more information. GPU_RESOURCES = 3; // Receive offers with resources that are shared. SHARED_RESOURCES = 4; // Indicates that (1) the framework is prepared to handle the // following TaskStates: TASK_UNREACHABLE, TASK_DROPPED, // TASK_GONE, TASK_GONE_BY_OPERATOR, and TASK_UNKNOWN, and (2) // the framework will assume responsibility for managing // partitioned tasks that reregister with the master. // // Frameworks that enable this capability can define how they // would like to handle partitioned tasks. Frameworks will // receive TASK_UNREACHABLE for tasks on agents that are // partitioned from the master. // // Without this capability, frameworks will receive TASK_LOST // for tasks on partitioned agents. // NOTE: Prior to Mesos 1.5, such tasks will be killed by Mesos // when the agent reregisters (unless the master has failed over). // However due to the lack of benefit in maintaining different // behaviors depending on whether the master has failed over // (see MESOS-7215), as of 1.5, Mesos will not kill these // tasks in either case. PARTITION_AWARE = 5; // This expresses the ability for the framework to be // "multi-tenant" via using the newly introduced `roles` // field, and examining `Offer.allocation_info` to determine // which role the offers are being made to. We also // expect that "single-tenant" schedulers eventually // provide this and move away from the deprecated // `role` field. MULTI_ROLE = 6; // This capability has two effects for a framework. // // (1) The framework is offered resources in a new format. // // The offered resources have the `Resource.reservations` field set // rather than `Resource.role` and `Resource.reservation`. In short, // an empty `reservations` field denotes unreserved resources, and // each `ReservationInfo` in the `reservations` field denotes a // reservation that refines the previous one. // // See the 'Resource Format' section for more details. // // (2) The framework can create refined reservations. // // A framework can refine an existing reservation via the // `Resource.reservations` field. For example, a reservation for role // `eng` can be refined to `eng/front_end`. // // See `ReservationInfo.reservations` for more details. // // NOTE: Without this capability, a framework is not offered resources // that have refined reservations. A resource is said to have refined // reservations if it uses the `Resource.reservations` field, and // `Resource.reservations_size() > 1`. RESERVATION_REFINEMENT = 7; // EXPERIMENTAL. // Indicates that the framework is prepared to receive offers // for agents whose region is different from the master's // region. Network links between hosts in different regions // typically have higher latency and lower bandwidth than // network links within a region, so frameworks should be // careful to only place suitable workloads in remote regions. // Frameworks that are not region-aware will never receive // offers for remote agents; region-aware frameworks are assumed // to implement their own logic to decide which workloads (if // any) are suitable for placement on remote agents. REGION_AWARE = 8; } // Enum fields should be optional, see: MESOS-4997. optional Type type = 1; } // This field allows a framework to advertise its set of // capabilities (e.g., ability to receive offers for revocable // resources). repeated Capability capabilities = 10; // Labels are free-form key value pairs supplied by the framework // scheduler (e.g., to describe additional functionality offered by // the framework). These labels are not interpreted by Mesos itself. // Labels should not contain duplicate key-value pairs. optional Labels labels = 11; } /** * Describes a general non-interpreting non-killing check for a task or * executor (or any arbitrary process/command). A type is picked by * specifying one of the optional fields. Specifying more than one type * is an error. * * NOTE: This API is subject to change and the related feature is experimental. */ message CheckInfo { enum Type { UNKNOWN = 0; COMMAND = 1; HTTP = 2; TCP = 3; // TODO(alexr): Consider supporting custom user checks. They should // probably be paired with a `data` field and complemented by a // `data` response in `CheckStatusInfo`. } // Describes a command check. If applicable, enters mount and/or network // namespaces of the task. message Command { required CommandInfo command = 1; } // Describes an HTTP check. Sends a GET request to // http://:port/path. Note that is not configurable and is // resolved automatically to 127.0.0.1. message Http { // Port to send the HTTP request. required uint32 port = 1; // HTTP request path. optional string path = 2; // TODO(alexr): Add support for HTTP method. While adding POST // and PUT is simple, supporting payload is more involved. // TODO(alexr): Add support for custom HTTP headers. // TODO(alexr): Consider adding an optional message to describe TLS // options and thus enabling https. Such message might contain certificate // validation, TLS version. } // Describes a TCP check, i.e. based on establishing a TCP connection to // the specified port. Note that is not configurable and is resolved // automatically to 127.0.0.1. message Tcp { required uint32 port = 1; } // The type of the check. optional Type type = 1; // Command check. optional Command command = 2; // HTTP check. optional Http http = 3; // TCP check. optional Tcp tcp = 7; // Amount of time to wait to start checking the task after it // transitions to `TASK_RUNNING` or `TASK_STARTING` if the latter // is used by the executor. optional double delay_seconds = 4 [default = 15.0]; // Interval between check attempts, i.e., amount of time to wait after // the previous check finished or timed out to start the next check. optional double interval_seconds = 5 [default = 10.0]; // Amount of time to wait for the check to complete. Zero means infinite // timeout. // // After this timeout, the check attempt is aborted and no result is // reported. Note that this may be considered a state change and hence // may trigger a check status change delivery to the corresponding // scheduler. See `CheckStatusInfo` for more details. optional double timeout_seconds = 6 [default = 20.0]; } /** * Describes a health check for a task or executor (or any arbitrary * process/command). A type is picked by specifying one of the * optional fields. Specifying more than one type is an error. */ message HealthCheck { enum Type { UNKNOWN = 0; COMMAND = 1; HTTP = 2; TCP = 3; } // Describes an HTTP health check. Sends a GET request to // scheme://:port/path. Note that is not configurable and is // resolved automatically, in most cases to 127.0.0.1. Default executors // treat return codes between 200 and 399 as success; custom executors // may employ a different strategy, e.g. leveraging the `statuses` field. message HTTPCheckInfo { optional NetworkInfo.Protocol protocol = 5 [default = IPv4]; // Currently "http" and "https" are supported. optional string scheme = 3; // Port to send the HTTP request. required uint32 port = 1; // HTTP request path. optional string path = 2; // TODO(alexr): Add support for HTTP method. While adding POST // and PUT is simple, supporting payload is more involved. // TODO(alexr): Add support for custom HTTP headers. // TODO(alexr): Add support for success and possibly failure // statuses. // NOTE: It is up to the custom executor to interpret and act on this // field. Setting this field has no effect on the default executors. // // TODO(haosdent): Deprecate this field when we add better support for // success and possibly failure statuses, e.g. ranges of success and // failure statuses. repeated uint32 statuses = 4; // TODO(haosdent): Consider adding a flag to enable task's certificate // validation for HTTPS health checks, see MESOS-5997. // TODO(benh): Include an 'optional bytes data' field for checking // for specific data in the response. } // Describes a TCP health check, i.e. based on establishing // a TCP connection to the specified port. message TCPCheckInfo { optional NetworkInfo.Protocol protocol = 2 [default = IPv4]; // Port expected to be open. required uint32 port = 1; } // TODO(benh): Consider adding a URL health check strategy which // allows doing something similar to the HTTP strategy but // encapsulates all the details in a single string field. // Amount of time to wait to start health checking the task after it // transitions to `TASK_RUNNING` or `TASK_STATING` if the latter is // used by the executor. optional double delay_seconds = 2 [default = 15.0]; // Interval between health checks, i.e., amount of time to wait after // the previous health check finished or timed out to start the next // health check. optional double interval_seconds = 3 [default = 10.0]; // Amount of time to wait for the health check to complete. After this // timeout, the health check is aborted and treated as a failure. Zero // means infinite timeout. optional double timeout_seconds = 4 [default = 20.0]; // Number of consecutive failures until the task is killed by the executor. optional uint32 consecutive_failures = 5 [default = 3]; // Amount of time after the task is launched during which health check // failures are ignored. Once a check succeeds for the first time, // the grace period does not apply anymore. Note that it includes // `delay_seconds`, i.e., setting `grace_period_seconds` < `delay_seconds` // has no effect. optional double grace_period_seconds = 6 [default = 10.0]; // TODO(alexr): Add an optional `KillPolicy` that should be used // if the task is killed because of a health check failure. // The type of health check. optional Type type = 8; // Command health check. optional CommandInfo command = 7; // HTTP health check. optional HTTPCheckInfo http = 1; // TCP health check. optional TCPCheckInfo tcp = 9; } /** * Describes a kill policy for a task. Currently does not express * different policies (e.g. hitting HTTP endpoints), only controls * how long to wait between graceful and forcible task kill: * * graceful kill --------------> forcible kill * grace_period * * Kill policies are best-effort, because machine failures / forcible * terminations may occur. * * NOTE: For executor-less command-based tasks, the kill is performed * via sending a signal to the task process: SIGTERM for the graceful * kill and SIGKILL for the forcible kill. For the docker executor-less * tasks the grace period is passed to 'docker stop --time'. */ message KillPolicy { // The grace period specifies how long to wait before forcibly // killing the task. It is recommended to attempt to gracefully // kill the task (and send TASK_KILLING) to indicate that the // graceful kill is in progress. Once the grace period elapses, // if the task has not terminated, a forcible kill should occur. // The task should not assume that it will always be allotted // the full grace period. For example, the executor may be // shutdown more quickly by the agent, or failures / forcible // terminations may occur. optional DurationInfo grace_period = 1; } /** * Describes a command, executed via: '/bin/sh -c value'. Any URIs specified * are fetched before executing the command. If the executable field for an * uri is set, executable file permission is set on the downloaded file. * Otherwise, if the downloaded file has a recognized archive extension * (currently [compressed] tar and zip) it is extracted into the executor's * working directory. This extraction can be disabled by setting `extract` to * false. In addition, any environment variables are set before executing * the command (so they can be used to "parameterize" your command). */ message CommandInfo { message URI { required string value = 1; optional bool executable = 2; // In case the fetched file is recognized as an archive, extract // its contents into the sandbox. Note that a cached archive is // not copied from the cache to the sandbox in case extraction // originates from an archive in the cache. optional bool extract = 3 [default = true]; // If this field is "true", the fetcher cache will be used. If not, // fetching bypasses the cache and downloads directly into the // sandbox directory, no matter whether a suitable cache file is // available or not. The former directs the fetcher to download to // the file cache, then copy from there to the sandbox. Subsequent // fetch attempts with the same URI will omit downloading and copy // from the cache as long as the file is resident there. Cache files // may get evicted at any time, which then leads to renewed // downloading. See also "docs/fetcher.md" and // "docs/fetcher-cache-internals.md". optional bool cache = 4; // The fetcher's default behavior is to use the URI string's basename to // name the local copy. If this field is provided, the local copy will be // named with its value instead. If there is a directory component (which // must be a relative path), the local copy will be stored in that // subdirectory inside the sandbox. optional string output_file = 5; } repeated URI uris = 1; optional Environment environment = 2; // There are two ways to specify the command: // 1) If 'shell == true', the command will be launched via shell // (i.e., /bin/sh -c 'value'). The 'value' specified will be // treated as the shell command. The 'arguments' will be ignored. // 2) If 'shell == false', the command will be launched by passing // arguments to an executable. The 'value' specified will be // treated as the filename of the executable. The 'arguments' // will be treated as the arguments to the executable. This is // similar to how POSIX exec families launch processes (i.e., // execlp(value, arguments(0), arguments(1), ...)). // NOTE: The field 'value' is changed from 'required' to 'optional' // in 0.20.0. It will only cause issues if a new framework is // connecting to an old master. optional bool shell = 6 [default = true]; optional string value = 3; repeated string arguments = 7; // Enables executor and tasks to run as a specific user. If the user // field is present both in FrameworkInfo and here, the CommandInfo // user value takes precedence. optional string user = 5; } /** * Describes information about an executor. */ message ExecutorInfo { enum Type { UNKNOWN = 0; // Mesos provides a simple built-in default executor that frameworks can // leverage to run shell commands and containers. // // NOTES: // // 1) `command` must not be set when using a default executor. // // 2) Default executor only accepts a *single* `LAUNCH` or `LAUNCH_GROUP` // operation. // // 3) If `container` is set, `container.type` must be `MESOS` // and `container.mesos.image` must not be set. DEFAULT = 1; // For frameworks that need custom functionality to run tasks, a `CUSTOM` // executor can be used. Note that `command` must be set when using a // `CUSTOM` executor. CUSTOM = 2; } // For backwards compatibility, if this field is not set when using `LAUNCH` // operation, Mesos will infer the type by checking if `command` is set // (`CUSTOM`) or unset (`DEFAULT`). `type` must be set when using // `LAUNCH_GROUP` operation. // // TODO(vinod): Add support for explicitly setting `type` to `DEFAULT` in // `LAUNCH` operation. optional Type type = 15; required ExecutorID executor_id = 1; optional FrameworkID framework_id = 8; // TODO(benh): Make this required. optional CommandInfo command = 7; // Executor provided with a container will launch the container // with the executor's CommandInfo and we expect the container to // act as a Mesos executor. optional ContainerInfo container = 11; repeated Resource resources = 5; optional string name = 9; // 'source' is an identifier style string used by frameworks to // track the source of an executor. This is useful when it's // possible for different executor ids to be related semantically. // // NOTE: 'source' is exposed alongside the resource usage of the // executor via JSON on the agent. This allows users to import usage // information into a time series database for monitoring. // // This field is deprecated since 1.0. Please use labels for // free-form metadata instead. optional string source = 10 [deprecated = true]; // Since 1.0. // This field can be used to pass arbitrary bytes to an executor. optional bytes data = 4; // Service discovery information for the executor. It is not // interpreted or acted upon by Mesos. It is up to a service // discovery system to use this information as needed and to handle // executors without service discovery information. optional DiscoveryInfo discovery = 12; // When shutting down an executor the agent will wait in a // best-effort manner for the grace period specified here // before forcibly destroying the container. The executor // must not assume that it will always be allotted the full // grace period, as the agent may decide to allot a shorter // period and failures / forcible terminations may occur. optional DurationInfo shutdown_grace_period = 13; // Labels are free-form key value pairs which are exposed through // master and agent endpoints. Labels will not be interpreted or // acted upon by Mesos itself. As opposed to the data field, labels // will be kept in memory on master and agent processes. Therefore, // labels should be used to tag executors with lightweight metadata. // Labels should not contain duplicate key-value pairs. optional Labels labels = 14; } /** * Describes a domain. A domain is a collection of hosts that have * similar characteristics. Mesos currently only supports "fault * domains", which identify groups of hosts with similar failure * characteristics. * * Frameworks can generally assume that network links between hosts in * the same fault domain have lower latency, higher bandwidth, and better * availability than network links between hosts in different domains. * Schedulers may prefer to place network-intensive workloads in the * same domain, as this may improve performance. Conversely, a single * failure that affects a host in a domain may be more likely to * affect other hosts in the same domain; hence, schedulers may prefer * to place workloads that require high availability in multiple * domains. (For example, all the hosts in a single rack might lose * power or network connectivity simultaneously.) * * There are two kinds of fault domains: regions and zones. Regions * offer the highest degree of fault isolation, but network latency * between regions is typically high (typically >50 ms). Zones offer a * modest degree of fault isolation along with reasonably low network * latency (typically <10 ms). * * The mapping from fault domains to physical infrastructure is up to * the operator to configure. In cloud environments, regions and zones * can be mapped to the "region" and "availability zone" concepts * exposed by most cloud providers, respectively. In on-premise * deployments, regions and zones can be mapped to data centers and * racks, respectively. * * Both masters and agents can be configured with domains. Frameworks * can compare the domains of two hosts to determine if the hosts are * in the same zone, in different zones in the same region, or in * different regions. Note that all masters in a given Mesos cluster * must be in the same region. */ message DomainInfo { message FaultDomain { message RegionInfo { required string name = 1; } message ZoneInfo { required string name = 1; } required RegionInfo region = 1; required ZoneInfo zone = 2; } optional FaultDomain fault_domain = 1; } /** * Describes a master. This will probably have more fields in the * future which might be used, for example, to link a framework webui * to a master webui. */ message MasterInfo { required string id = 1; // The IP address (only IPv4) as a packed 4-bytes integer, // stored in network order. Deprecated, use `address.ip` instead. required uint32 ip = 2; // The TCP port the Master is listening on for incoming // HTTP requests; deprecated, use `address.port` instead. required uint32 port = 3 [default = 5050]; // In the default implementation, this will contain information // about both the IP address, port and Master name; it should really // not be relied upon by external tooling/frameworks and be // considered an "internal" implementation field. optional string pid = 4; // The server's hostname, if available; it may be unreliable // in environments where the DNS configuration does not resolve // internal hostnames (eg, some public cloud providers). // Deprecated, use `address.hostname` instead. optional string hostname = 5; // The running Master version, as a string; taken from the // generated "master/version.hpp". optional string version = 6; // The full IP address (supports both IPv4 and IPv6 formats) // and supersedes the use of `ip`, `port` and `hostname`. // Since Mesos 0.24. optional Address address = 7; // The domain that this master belongs to. All masters in a Mesos // cluster should belong to the same region. optional DomainInfo domain = 8; message Capability { enum Type { UNKNOWN = 0; // The master can handle slaves whose state // changes after reregistering. AGENT_UPDATE = 1; } optional Type type = 1; } repeated Capability capabilities = 9; } /** * Describes an agent. Note that the 'id' field is only available * after an agent is registered with the master, and is made available * here to facilitate re-registration. */ message AgentInfo { required string hostname = 1; optional int32 port = 8 [default = 5051]; // The configured resources at the agent. This does not include any // dynamic reservations or persistent volumes that may currently // exist at the agent. repeated Resource resources = 3; repeated Attribute attributes = 5; optional AgentID id = 6; // The domain that this agent belongs to. If the agent's region // differs from the master's region, it will not appear in resource // offers to frameworks that have not enabled the REGION_AWARE // capability. optional DomainInfo domain = 10; message Capability { enum Type { // This must be the first enum value in this list, to // ensure that if 'type' is not set, the default value // is UNKNOWN. This enables enum values to be added // in a backwards-compatible way. See: MESOS-4997. UNKNOWN = 0; // This expresses the ability for the agent to be able // to launch tasks of a 'multi-role' framework. MULTI_ROLE = 1; // This expresses the ability for the agent to be able to launch // tasks, reserve resources, and create volumes using resources // allocated to a 'hierarchical-role'. // NOTE: This capability is required specifically for creating // volumes because a hierchical role includes '/' (slashes) in them. // Agents with this capability know to transform the '/' (slashes) // into ' ' (spaces). HIERARCHICAL_ROLE = 2; // This capability has three effects for an agent. // // (1) The format of the checkpointed resources, and // the resources reported to master. // // These resources are reported in the "pre-reservation-refinement" // format if none of the resources have refined reservations. If any // of the resources have refined reservations, they are reported in // the "post-reservation-refinement" format. The purpose is to allow // downgrading of an agent as well as communication with a pre-1.4.0 // master until the reservation refinement feature is actually used. // // See the 'Resource Format' section for more details. // // (2) The format of the resources reported by the HTTP endpoints. // // For resources reported by agent endpoints, the // "pre-reservation-refinement" format is "injected" if possible. // That is, resources without refined reservations will have the // `Resource.role` and `Resource.reservation` set, whereas // resources with refined reservations will not. // // See the 'Resource Format' section for more details. // // (3) The ability for the agent to launch tasks, reserve resources, and // create volumes using resources that have refined reservations. // // See `ReservationInfo.reservations` section for more details. // // NOTE: Resources are said to have refined reservations if it uses the // `Resource.reservations` field, and `Resource.reservations_size() > 1`. RESERVATION_REFINEMENT = 3; // This expresses the ability for the agent to handle resource // provider related operations. This includes the following: // // (1) The ability to report resources that are provided by some // local resource providers through the resource provider API. // // (2) The ability to provide operation feedback. RESOURCE_PROVIDER = 4; // This expresses the capability for the agent to handle persistent volume // resize operations safely. This capability is turned on by default. RESIZE_VOLUME = 5; } // Enum fields should be optional, see: MESOS-4997. optional Type type = 1; } } /** * Describes the container configuration to run a CSI plugin component. */ message CSIPluginContainerInfo { enum Service { UNKNOWN = 0; CONTROLLER_SERVICE = 1; NODE_SERVICE = 2; } repeated Service services = 1; optional CommandInfo command = 2; repeated Resource resources = 3; optional ContainerInfo container = 4; } /** * Describes a CSI plugin. */ message CSIPluginInfo { // The type of the CSI service. This uniquely identifies a CSI // implementation. For instance: // org.apache.mesos.csi.test // // Please follow to Java package naming convention // (https://en.wikipedia.org/wiki/Java_package#Package_naming_conventions) // to avoid conflicts on type names. required string type = 1; // The name of the CSI service. There could be mutliple instances of a // type of CSI service. The name field is used to distinguish these // instances. It should be a legal Java identifier // (https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html) // to avoid conflicts on concatenation of type and name. required string name = 2; // A list of container configurations to run CSI plugin components. // The controller service will be served by the first configuration // that contains `CONTROLLER_SERVICE`, and the node service will be // served by the first configuration that contains `NODE_SERVICE`. repeated CSIPluginContainerInfo containers = 3; } /** * Describes a resource provider. Note that the 'id' field is only available * after a resource provider is registered with the master, and is made * available here to facilitate re-registration. */ message ResourceProviderInfo { optional ResourceProviderID id = 1; repeated Attribute attributes = 2; // The type of the resource provider. This uniquely identifies a // resource provider implementation. For instance: // org.apache.mesos.rp.local.storage // // Please follow to Java package naming convention // (https://en.wikipedia.org/wiki/Java_package#Package_naming_conventions) // to avoid conflicts on type names. required string type = 3; // The name of the resource provider. There could be multiple // instances of a type of resource provider. The name field is used // to distinguish these instances. It should be a legal Java identifier // (https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html) // to avoid conflicts on concatenation of type and name. required string name = 4; // The stack of default reservations. If this field is not empty, it // indicates that resources from this resource provider are reserved // by default, except for the resources that have been reserved or // unreserved through operations. The first `ReservationInfo` // may have type `STATIC` or `DYNAMIC`, but the rest must have // `DYNAMIC`. One can create a new reservation on top of an existing // one by pushing a new `ReservationInfo` to the back. The last // `ReservationInfo` in this stack is the "current" reservation. The // new reservation's role must be a child of the current one. repeated Resource.ReservationInfo default_reservations = 5; // EXPERIMENTAL. // Storage resource provider related information. message Storage { required CSIPluginInfo plugin = 1; } optional Storage storage = 6; // EXPERIMENTAL. } /** * Describes an Attribute or Resource "value". A value is described * using the standard protocol buffer "union" trick. */ message Value { enum Type { SCALAR = 0; RANGES = 1; SET = 2; TEXT = 3; } message Scalar { // Scalar values are represented using floating point. To reduce // the chance of unpredictable floating point behavior due to // roundoff error, Mesos only supports three decimal digits of // precision for scalar resource values. That is, floating point // values are converted to a fixed point format that supports // three decimal digits of precision, and then converted back to // floating point on output. Any additional precision in scalar // resource values is discarded (via rounding). required double value = 1; } message Range { required uint64 begin = 1; required uint64 end = 2; } message Ranges { repeated Range range = 1; } message Set { repeated string item = 1; } message Text { required string value = 1; } required Type type = 1; optional Scalar scalar = 2; optional Ranges ranges = 3; optional Set set = 4; optional Text text = 5; } /** * Describes an attribute that can be set on a machine. For now, * attributes and resources share the same "value" type, but this may * change in the future and attributes may only be string based. */ message Attribute { required string name = 1; required Value.Type type = 2; optional Value.Scalar scalar = 3; optional Value.Ranges ranges = 4; optional Value.Set set = 6; optional Value.Text text = 5; } /** * Describes a resource from a resource provider. The `name` field is * a string like "cpus" or "mem" that indicates which kind of resource * this is; the rest of the fields describe the properties of the * resource. A resource can take on one of three types: scalar * (double), a list of finite and discrete ranges (e.g., [1-10, * 20-30]), or a set of items. A resource is described using the * standard protocol buffer "union" trick. * * Note that "disk" and "mem" resources are scalar values expressed in * megabytes. Fractional "cpus" values are allowed (e.g., "0.5"), * which correspond to partial shares of a CPU. */ message Resource { optional ResourceProviderID provider_id = 12; required string name = 1; required Value.Type type = 2; optional Value.Scalar scalar = 3; optional Value.Ranges ranges = 4; optional Value.Set set = 5; // The role that this resource is reserved for. If "*", this indicates // that the resource is unreserved. Otherwise, the resource will only // be offered to frameworks that belong to this role. // // NOTE: Frameworks must not set this field if `reservations` is set. // See the 'Resource Format' section for more details. // // TODO(mpark): Deprecate once `reservations` is no longer experimental. optional string role = 6 [default = "*", deprecated=true]; // This was initially introduced to support MULTI_ROLE capable // frameworks. Frameworks that are not MULTI_ROLE capable can // continue to assume that the offered resources are allocated // to their role. message AllocationInfo { // If set, this resource is allocated to a role. Note that in the // future, this may be unset and the scheduler may be responsible // for allocating to one of its roles. optional string role = 1; // In the future, we may add additional fields here, e.g. priority // tier, type of allocation (quota / fair share). } optional AllocationInfo allocation_info = 11; // Resource Format: // // Frameworks receive resource offers in one of two formats, depending on // whether the RESERVATION_REFINEMENT capability is enabled. // // __WITHOUT__ the RESERVATION_REFINEMENT capability, the framework is offered // resources in the "pre-reservation-refinement" format. In this format, the // `Resource.role` and `Resource.reservation` fields are used in conjunction // to describe the reservation state of a `Resource` message. // // The following is an overview of the possible reservation states: // // +------------+------------------------------------------------------------+ // | unreserved | { | // | | role: "*", | // | | reservation: , | // | | reservations: | // | | } | // +------------+------------------------------------------------------------+ // | static | { | // | | role: "eng", | // | | reservation: , | // | | reservations: | // | | } | // +------------+------------------------------------------------------------+ // | dynamic | { | // | | role: "eng", | // | | reservation: { | // | | type: , | // | | role: , | // | | principal: , | // | | labels: | // | | }, | // | | reservations: | // | | } | // +------------+------------------------------------------------------------+ // // __WITH__ the RESERVATION_REFINEMENT capability, the framework is offered // resources in the "post-reservation-refinement" format. In this format, the // reservation state of a `Resource` message is expressed solely in // `Resource.reservations` field. // // The following is an overview of the possible reservation states: // // +------------+------------------------------------------------------------+ // | unreserved | { | // | | role: , | // | | reservation: , | // | | reservations: [] | // | | } | // +------------+------------------------------------------------------------+ // | static | { | // | | role: , | // | | reservation: , | // | | reservations: [ | // | | { | // | | type: STATIC, | // | | role: "eng", | // | | principal: , | // | | labels: | // | | } | // | | ] | // | | } | // +------------+------------------------------------------------------------+ // | dynamic | { | // | | role: , | // | | reservation: , | // | | reservations: [ | // | | { | // | | type: DYNAMIC, | // | | role: "eng", | // | | principal: , | // | | labels: | // | | } | // | | ] | // | | } | // +------------+------------------------------------------------------------+ // // We can also __refine__ reservations with this capability like so: // // +------------+------------------------------------------------------------+ // | refined | { | // | | role: , | // | | reservation: , | // | | reservations: [ | // | | { | // | | type: STATIC or DYNAMIC, | // | | role: "eng", | // | | principal: , | // | | labels: | // | | }, | // | | { | // | | type: DYNAMIC, | // | | role: "eng/front_end", | // | | principal: , | // | | labels: | // | | } | // | | ] | // | | } | // +------------+------------------------------------------------------------+ // // NOTE: Each `ReservationInfo` in the `reservations` field denotes // a reservation that refines the previous `ReservationInfo`. message ReservationInfo { // Describes a reservation. A static reservation is set by the operator on // the command-line and they are immutable without agent restart. A dynamic // reservation is made by an operator via the '/reserve' HTTP endpoint // or by a framework via the offer cycle by sending back an // 'Offer::Operation::Reserve' message. // // NOTE: We currently do not allow frameworks with role "*" to make dynamic // reservations. enum Type { UNKNOWN = 0; STATIC = 1; DYNAMIC = 2; } // The type of this reservation. // // NOTE: This field must not be set for `Resource.reservation`. // See the 'Resource Format' section for more details. optional Type type = 4; // The role to which this reservation is made for. // // NOTE: This field must not be set for `Resource.reservation`. // See the 'Resource Format' section for more details. optional string role = 3; // Indicates the principal, if any, of the framework or operator // that reserved this resource. If reserved by a framework, the // field should match the `FrameworkInfo.principal`. It is used in // conjunction with the `UnreserveResources` ACL to determine // whether the entity attempting to unreserve this resource is // permitted to do so. optional string principal = 1; // Labels are free-form key value pairs that can be used to // associate arbitrary metadata with a reserved resource. For // example, frameworks can use labels to identify the intended // purpose for a portion of the resources the framework has // reserved at a given agent. Labels should not contain duplicate // key-value pairs. optional Labels labels = 2; } // If this is set, this resource was dynamically reserved by an // operator or a framework. Otherwise, this resource is either unreserved // or statically reserved by an operator via the --resources flag. // // NOTE: Frameworks must not set this field if `reservations` is set. // See the 'Resource Format' section for more details. // // TODO(mpark): Deprecate once `reservations` is no longer experimental. optional ReservationInfo reservation = 8; // The stack of reservations. If this field is empty, it indicates that this // resource is unreserved. Otherwise, the resource is reserved. The first // `ReservationInfo` may have type `STATIC` or `DYNAMIC`, but the rest must // have `DYNAMIC`. One can create a new reservation on top of an existing // one by pushing a new `ReservationInfo` to the back. The last // `ReservationInfo` in this stack is the "current" reservation. The new // reservation's role must be a child of the current reservation's role. // // NOTE: Frameworks must not set this field if `reservation` is set. // See the 'Resource Format' section for more details. // // TODO(mpark): Deprecate `role` and `reservation` once this is stable. repeated ReservationInfo reservations = 13; // EXPERIMENTAL. message DiskInfo { // Describes a persistent disk volume. // // A persistent disk volume will not be automatically garbage // collected if the task/executor/agent terminates, but will be // re-offered to the framework(s) belonging to the 'role'. // // NOTE: Currently, we do not allow persistent disk volumes // without a reservation (i.e., 'role' cannot be '*'). message Persistence { // A unique ID for the persistent disk volume. This ID must be // unique per role on each agent. Although it is possible to use // the same ID on different agents in the cluster and to reuse // IDs after a volume with that ID has been destroyed, both // practices are discouraged. required string id = 1; // This field indicates the principal of the operator or // framework that created this volume. It is used in conjunction // with the "destroy" ACL to determine whether an entity // attempting to destroy the volume is permitted to do so. // // NOTE: This field should match the FrameworkInfo.principal of // the framework that created the volume. optional string principal = 2; } optional Persistence persistence = 1; // Describes how this disk resource will be mounted in the // container. If not set, the disk resource will be used as the // sandbox. Otherwise, it will be mounted according to the // 'container_path' inside 'volume'. The 'host_path' inside // 'volume' is ignored. // NOTE: If 'volume' is set but 'persistence' is not set, the // volume will be automatically garbage collected after // task/executor terminates. Currently, if 'persistence' is set, // 'volume' must be set. optional Volume volume = 2; // Describes where a disk originates from. message Source { enum Type { UNKNOWN = 0; PATH = 1; MOUNT = 2; BLOCK = 3; RAW = 4; } // A folder that can be located on a separate disk device. This // can be shared and carved up as necessary between frameworks. message Path { // Path to the folder (e.g., /mnt/raid/disk0). If the path is a // relative path, it is relative to the agent work directory. optional string root = 1; } // A mounted file-system set up by the Agent administrator. This // can only be used exclusively: a framework cannot accept a // partial amount of this disk. message Mount { // Path to mount point (e.g., /mnt/raid/disk0). If the path is a // relative path, it is relative to the agent work directory. optional string root = 1; } required Type type = 1; optional Path path = 2; optional Mount mount = 3; // An identifier for this source. This field maps onto CSI // volume IDs and is not expected to be set by frameworks. optional string id = 4; // EXPERIMENTAL. // Additional metadata for this source. This field maps onto CSI // volume metadata and is not expected to be set by frameworks. optional Labels metadata = 5; // EXPERIMENTAL. // This field serves as an indirection to a set of storage // vendor specific disk parameters which describe the properties // of the disk. The operator will setup mappings between a // profile name to a set of vendor specific disk parameters. And // the framework will do disk selection based on profile names, // instead of vendor specific disk parameters. // // Also see the DiskProfileAdaptor module. optional string profile = 6; // EXPERIMENTAL. } optional Source source = 3; } optional DiskInfo disk = 7; message RevocableInfo {} // If this is set, the resources are revocable, i.e., any tasks or // executors launched using these resources could get preempted or // throttled at any time. This could be used by frameworks to run // best effort tasks that do not need strict uptime or performance // guarantees. Note that if this is set, 'disk' or 'reservation' // cannot be set. optional RevocableInfo revocable = 9; // Allow the resource to be shared across tasks. message SharedInfo {} // If this is set, the resources are shared, i.e. multiple tasks // can be launched using this resource and all of them shall refer // to the same physical resource on the cluster. Note that only // persistent volumes can be shared currently. optional SharedInfo shared = 10; } /** * When the network bandwidth caps are enabled and the container * is over its limit, outbound packets may be either delayed or * dropped completely either because it exceeds the maximum bandwidth * allocation for a single container (the cap) or because the combined * network traffic of multiple containers on the host exceeds the * transmit capacity of the host (the share). We can report the * following statistics for each of these conditions exported directly * from the Linux Traffic Control Queueing Discipline. * * id : name of the limiter, e.g. 'tx_bw_cap' * backlog : number of packets currently delayed * bytes : total bytes seen * drops : number of packets dropped in total * overlimits : number of packets which exceeded allocation * packets : total packets seen * qlen : number of packets currently queued * rate_bps : throughput in bytes/sec * rate_pps : throughput in packets/sec * requeues : number of times a packet has been delayed due to * locking or device contention issues * * More information on the operation of Linux Traffic Control can be * found at http://www.lartc.org/lartc.html. */ message TrafficControlStatistics { required string id = 1; optional uint64 backlog = 2; optional uint64 bytes = 3; optional uint64 drops = 4; optional uint64 overlimits = 5; optional uint64 packets = 6; optional uint64 qlen = 7; optional uint64 ratebps = 8; optional uint64 ratepps = 9; optional uint64 requeues = 10; } message IpStatistics { optional int64 Forwarding = 1; optional int64 DefaultTTL = 2; optional int64 InReceives = 3; optional int64 InHdrErrors = 4; optional int64 InAddrErrors = 5; optional int64 ForwDatagrams = 6; optional int64 InUnknownProtos = 7; optional int64 InDiscards = 8; optional int64 InDelivers = 9; optional int64 OutRequests = 10; optional int64 OutDiscards = 11; optional int64 OutNoRoutes = 12; optional int64 ReasmTimeout = 13; optional int64 ReasmReqds = 14; optional int64 ReasmOKs = 15; optional int64 ReasmFails = 16; optional int64 FragOKs = 17; optional int64 FragFails = 18; optional int64 FragCreates = 19; } message IcmpStatistics { optional int64 InMsgs = 1; optional int64 InErrors = 2; optional int64 InCsumErrors = 3; optional int64 InDestUnreachs = 4; optional int64 InTimeExcds = 5; optional int64 InParmProbs = 6; optional int64 InSrcQuenchs = 7; optional int64 InRedirects = 8; optional int64 InEchos = 9; optional int64 InEchoReps = 10; optional int64 InTimestamps = 11; optional int64 InTimestampReps = 12; optional int64 InAddrMasks = 13; optional int64 InAddrMaskReps = 14; optional int64 OutMsgs = 15; optional int64 OutErrors = 16; optional int64 OutDestUnreachs = 17; optional int64 OutTimeExcds = 18; optional int64 OutParmProbs = 19; optional int64 OutSrcQuenchs = 20; optional int64 OutRedirects = 21; optional int64 OutEchos = 22; optional int64 OutEchoReps = 23; optional int64 OutTimestamps = 24; optional int64 OutTimestampReps = 25; optional int64 OutAddrMasks = 26; optional int64 OutAddrMaskReps = 27; } message TcpStatistics { optional int64 RtoAlgorithm = 1; optional int64 RtoMin = 2; optional int64 RtoMax = 3; optional int64 MaxConn = 4; optional int64 ActiveOpens = 5; optional int64 PassiveOpens = 6; optional int64 AttemptFails = 7; optional int64 EstabResets = 8; optional int64 CurrEstab = 9; optional int64 InSegs = 10; optional int64 OutSegs = 11; optional int64 RetransSegs = 12; optional int64 InErrs = 13; optional int64 OutRsts = 14; optional int64 InCsumErrors = 15; } message UdpStatistics { optional int64 InDatagrams = 1; optional int64 NoPorts = 2; optional int64 InErrors = 3; optional int64 OutDatagrams = 4; optional int64 RcvbufErrors = 5; optional int64 SndbufErrors = 6; optional int64 InCsumErrors = 7; optional int64 IgnoredMulti = 8; } message SNMPStatistics { optional IpStatistics ip_stats = 1; optional IcmpStatistics icmp_stats = 2; optional TcpStatistics tcp_stats = 3; optional UdpStatistics udp_stats = 4; } message DiskStatistics { optional Resource.DiskInfo.Source source = 1; optional Resource.DiskInfo.Persistence persistence = 2; optional uint64 limit_bytes = 3; optional uint64 used_bytes = 4; } /** * A snapshot of resource usage statistics. */ message ResourceStatistics { required double timestamp = 1; // Snapshot time, in seconds since the Epoch. optional uint32 processes = 30; optional uint32 threads = 31; // CPU Usage Information: // Total CPU time spent in user mode, and kernel mode. optional double cpus_user_time_secs = 2; optional double cpus_system_time_secs = 3; // Number of CPUs allocated. optional double cpus_limit = 4; // cpu.stat on process throttling (for contention issues). optional uint32 cpus_nr_periods = 7; optional uint32 cpus_nr_throttled = 8; optional double cpus_throttled_time_secs = 9; // Memory Usage Information: // mem_total_bytes was added in 0.23.0 to represent the total memory // of a process in RAM (as opposed to in Swap). This was previously // reported as mem_rss_bytes, which was also changed in 0.23.0 to // represent only the anonymous memory usage, to keep in sync with // Linux kernel's (arguably erroneous) use of terminology. optional uint64 mem_total_bytes = 36; // Total memory + swap usage. This is set if swap is enabled. optional uint64 mem_total_memsw_bytes = 37; // Hard memory limit for a container. optional uint64 mem_limit_bytes = 6; // Soft memory limit for a container. optional uint64 mem_soft_limit_bytes = 38; // Broken out memory usage information: pagecache, rss (anonymous), // mmaped files and swap. // TODO(chzhcn) mem_file_bytes and mem_anon_bytes are deprecated in // 0.23.0 and will be removed in 0.24.0. optional uint64 mem_file_bytes = 10; optional uint64 mem_anon_bytes = 11; // mem_cache_bytes is added in 0.23.0 to represent page cache usage. optional uint64 mem_cache_bytes = 39; // Since 0.23.0, mem_rss_bytes is changed to represent only // anonymous memory usage. Note that neither its requiredness, type, // name nor numeric tag has been changed. optional uint64 mem_rss_bytes = 5; optional uint64 mem_mapped_file_bytes = 12; // This is only set if swap is enabled. optional uint64 mem_swap_bytes = 40; optional uint64 mem_unevictable_bytes = 41; // Number of occurrences of different levels of memory pressure // events reported by memory cgroup. Pressure listening (re)starts // with these values set to 0 when agent (re)starts. See // https://www.kernel.org/doc/Documentation/cgroups/memory.txt for // more details. optional uint64 mem_low_pressure_counter = 32; optional uint64 mem_medium_pressure_counter = 33; optional uint64 mem_critical_pressure_counter = 34; // Disk Usage Information for executor working directory. optional uint64 disk_limit_bytes = 26; optional uint64 disk_used_bytes = 27; // Per disk (resource) statistics. repeated DiskStatistics disk_statistics = 43; // Cgroups blkio statistics. optional CgroupInfo.Blkio.Statistics blkio_statistics = 44; // Perf statistics. optional PerfStatistics perf = 13; // Network Usage Information: optional uint64 net_rx_packets = 14; optional uint64 net_rx_bytes = 15; optional uint64 net_rx_errors = 16; optional uint64 net_rx_dropped = 17; optional uint64 net_tx_packets = 18; optional uint64 net_tx_bytes = 19; optional uint64 net_tx_errors = 20; optional uint64 net_tx_dropped = 21; // The kernel keeps track of RTT (round-trip time) for its TCP // sockets. RTT is a way to tell the latency of a container. optional double net_tcp_rtt_microsecs_p50 = 22; optional double net_tcp_rtt_microsecs_p90 = 23; optional double net_tcp_rtt_microsecs_p95 = 24; optional double net_tcp_rtt_microsecs_p99 = 25; optional double net_tcp_active_connections = 28; optional double net_tcp_time_wait_connections = 29; // Network traffic flowing into or out of a container can be delayed // or dropped due to congestion or policy inside and outside the // container. repeated TrafficControlStatistics net_traffic_control_statistics = 35; // Network SNMP statistics for each container. optional SNMPStatistics net_snmp_statistics = 42; } /** * Describes a snapshot of the resource usage for executors. */ message ResourceUsage { message Executor { required ExecutorInfo executor_info = 1; // This includes resources used by the executor itself // as well as its active tasks. repeated Resource allocated = 2; // Current resource usage. If absent, the containerizer // cannot provide resource usage. optional ResourceStatistics statistics = 3; // The container id for the executor specified in the executor_info field. required ContainerID container_id = 4; message Task { required string name = 1; required TaskID id = 2; repeated Resource resources = 3; optional Labels labels = 4; } // Non-terminal tasks. repeated Task tasks = 5; } repeated Executor executors = 1; // Agent's total resources including checkpointed dynamic // reservations and persistent volumes. repeated Resource total = 2; } /** * Describes a sample of events from "perf stat". Only available on * Linux. * * NOTE: Each optional field matches the name of a perf event (see * "perf list") with the following changes: * 1. Names are downcased. * 2. Hyphens ('-') are replaced with underscores ('_'). * 3. Events with alternate names use the name "perf stat" returns, * e.g., for the event "cycles OR cpu-cycles" perf always returns * cycles. */ message PerfStatistics { required double timestamp = 1; // Start of sample interval, in seconds since the Epoch. required double duration = 2; // Duration of sample interval, in seconds. // Hardware event. optional uint64 cycles = 3; optional uint64 stalled_cycles_frontend = 4; optional uint64 stalled_cycles_backend = 5; optional uint64 instructions = 6; optional uint64 cache_references = 7; optional uint64 cache_misses = 8; optional uint64 branches = 9; optional uint64 branch_misses = 10; optional uint64 bus_cycles = 11; optional uint64 ref_cycles = 12; // Software event. optional double cpu_clock = 13; optional double task_clock = 14; optional uint64 page_faults = 15; optional uint64 minor_faults = 16; optional uint64 major_faults = 17; optional uint64 context_switches = 18; optional uint64 cpu_migrations = 19; optional uint64 alignment_faults = 20; optional uint64 emulation_faults = 21; // Hardware cache event. optional uint64 l1_dcache_loads = 22; optional uint64 l1_dcache_load_misses = 23; optional uint64 l1_dcache_stores = 24; optional uint64 l1_dcache_store_misses = 25; optional uint64 l1_dcache_prefetches = 26; optional uint64 l1_dcache_prefetch_misses = 27; optional uint64 l1_icache_loads = 28; optional uint64 l1_icache_load_misses = 29; optional uint64 l1_icache_prefetches = 30; optional uint64 l1_icache_prefetch_misses = 31; optional uint64 llc_loads = 32; optional uint64 llc_load_misses = 33; optional uint64 llc_stores = 34; optional uint64 llc_store_misses = 35; optional uint64 llc_prefetches = 36; optional uint64 llc_prefetch_misses = 37; optional uint64 dtlb_loads = 38; optional uint64 dtlb_load_misses = 39; optional uint64 dtlb_stores = 40; optional uint64 dtlb_store_misses = 41; optional uint64 dtlb_prefetches = 42; optional uint64 dtlb_prefetch_misses = 43; optional uint64 itlb_loads = 44; optional uint64 itlb_load_misses = 45; optional uint64 branch_loads = 46; optional uint64 branch_load_misses = 47; optional uint64 node_loads = 48; optional uint64 node_load_misses = 49; optional uint64 node_stores = 50; optional uint64 node_store_misses = 51; optional uint64 node_prefetches = 52; optional uint64 node_prefetch_misses = 53; } /** * Describes a request for resources that can be used by a framework * to proactively influence the allocator. If 'agent_id' is provided * then this request is assumed to only apply to resources on that * agent. */ message Request { optional AgentID agent_id = 1; repeated Resource resources = 2; } /** * Describes some resources available on an agent. An offer only * contains resources from a single agent. */ message Offer { required OfferID id = 1; required FrameworkID framework_id = 2; required AgentID agent_id = 3; required string hostname = 4; // URL for reaching the agent running on the host. optional URL url = 8; // The domain of the agent. optional DomainInfo domain = 11; repeated Resource resources = 5; repeated Attribute attributes = 7; // Executors of the same framework running on this agent. repeated ExecutorID executor_ids = 6; // Signifies that the resources in this Offer may be unavailable during // the given interval. Any tasks launched using these resources may be // killed when the interval arrives. For example, these resources may be // part of a planned maintenance schedule. // // This field only provides information about a planned unavailability. // The unavailability interval may not necessarily start at exactly this // interval, nor last for exactly the duration of this interval. // The unavailability may also be forever! See comments in // `Unavailability` for more details. optional Unavailability unavailability = 9; // An offer represents resources allocated to *one* of the // roles managed by the scheduler. (Therefore, each // `Offer.resources[i].allocation_info` will match the // top level `Offer.allocation_info`). optional Resource.AllocationInfo allocation_info = 10; // Defines an operation that can be performed against offers. message Operation { enum Type { UNKNOWN = 0; LAUNCH = 1; LAUNCH_GROUP = 6; RESERVE = 2; UNRESERVE = 3; CREATE = 4; DESTROY = 5; GROW_VOLUME = 11; // EXPERIMENTAL. SHRINK_VOLUME = 12; // EXPERIMENTAL. CREATE_DISK = 13; // EXPERIMENTAL. DESTROY_DISK = 14; // EXPERIMENTAL. } // TODO(vinod): Deprecate this in favor of `LaunchGroup` below. message Launch { repeated TaskInfo task_infos = 1; } // Unlike `Launch` above, all the tasks in a `task_group` are // atomically delivered to an executor. // // `NetworkInfo` set on executor will be shared by all tasks in // the task group. // // TODO(vinod): Any volumes set on executor could be used by a // task by explicitly setting `Volume.source` in its resources. message LaunchGroup { required ExecutorInfo executor = 1; required TaskGroupInfo task_group = 2; } message Reserve { repeated Resource resources = 1; } message Unreserve { repeated Resource resources = 1; } message Create { repeated Resource volumes = 1; } message Destroy { repeated Resource volumes = 1; } // Grow a volume by an additional disk resource. // NOTE: This is currently experimental and only for persistent volumes // created on ROOT/PATH disk. message GrowVolume { required Resource volume = 1; required Resource addition = 2; } // Shrink a volume by the size specified in the `subtract` field. // NOTE: This is currently experimental and only for persistent volumes // created on ROOT/PATH disk. message ShrinkVolume { required Resource volume = 1; // See comments in `Value.Scalar` for maximum precision supported. required Value.Scalar subtract = 2; } // Create a `MOUNT` or `BLOCK` disk resource from a `RAW` disk resource. // NOTE: For the time being, this API is subject to change and the related // feature is experimental. message CreateDisk { required Resource source = 1; // NOTE: Only `MOUNT` or `BLOCK` is allowed in the `target_type` field. required Resource.DiskInfo.Source.Type target_type = 2; } // Destroy a `MOUNT` or `BLOCK` disk resource. This will result in a `RAW` // disk resource. // NOTE: For the time being, this API is subject to change and the related // feature is experimental. message DestroyDisk { // NOTE: Only a `MOUNT` or `BLOCK` disk is allowed in the `source` field. required Resource source = 1; } optional Type type = 1; // NOTE: The `id` field will allow frameworks to indicate that they wish to // receive feedback about an operation. Since this feature is not yet // implemented, the `id` field should NOT be set at present. See MESOS-8054. optional OperationID id = 12; // EXPERIMENTAL. optional Launch launch = 2; optional LaunchGroup launch_group = 7; optional Reserve reserve = 3; optional Unreserve unreserve = 4; optional Create create = 5; optional Destroy destroy = 6; optional GrowVolume grow_volume = 13; // EXPERIMENTAL. optional ShrinkVolume shrink_volume = 14; // EXPERIMENTAL. optional CreateDisk create_disk = 15; // EXPERIMENTAL. optional DestroyDisk destroy_disk = 16; // EXPERIMENTAL. } } /** * A request to return some resources occupied by a framework. */ message InverseOffer { // This is the same OfferID as found in normal offers, which allows // re-use of some of the OfferID-only messages. required OfferID id = 1; // URL for reaching the agent running on the host. This enables some // optimizations as described in MESOS-3012, such as allowing the // scheduler driver to bypass the master and talk directly with an agent. optional URL url = 2; // The framework that should release its resources. // If no specifics are provided (i.e. which agent), all the framework's // resources are requested back. required FrameworkID framework_id = 3; // Specified if the resources need to be released from a particular agent. // All the framework's resources on this agent are requested back, // unless further qualified by the `resources` field. optional AgentID agent_id = 4; // This InverseOffer represents a planned unavailability event in the // specified interval. Any tasks running on the given framework or agent // may be killed when the interval arrives. Therefore, frameworks should // aim to gracefully terminate tasks prior to the arrival of the interval. // // For reserved resources, the resources are expected to be returned to the // framework after the unavailability interval. This is an expectation, // not a guarantee. For example, if the unavailability duration is not set, // the resources may be removed permanently. // // For other resources, there is no guarantee that requested resources will // be returned after the unavailability interval. The allocator has no // obligation to re-offer these resources to the prior framework after // the unavailability. required Unavailability unavailability = 5; // A list of resources being requested back from the framework, // on the agent identified by `agent_id`. If no resources are specified // then all resources are being requested back. For the purpose of // maintenance, this field is always empty (maintenance always requests // all resources back). repeated Resource resources = 6; // TODO(josephw): Add additional options for narrowing down the resources // being requested back. Such as specific executors, tasks, etc. } /** * Describes a task. Passed from the scheduler all the way to an * executor (see SchedulerDriver::launchTasks and * Executor::launchTask). Either ExecutorInfo or CommandInfo should be set. * A different executor can be used to launch this task, and subsequent tasks * meant for the same executor can reuse the same ExecutorInfo struct. */ message TaskInfo { required string name = 1; required TaskID task_id = 2; required AgentID agent_id = 3; repeated Resource resources = 4; optional ExecutorInfo executor = 5; optional CommandInfo command = 7; // Task provided with a container will launch the container as part // of this task paired with the task's CommandInfo. optional ContainerInfo container = 9; // A health check for the task. Implemented for executor-less // command-based tasks. For tasks that specify an executor, it is // the executor's responsibility to implement the health checking. optional HealthCheck health_check = 8; // A general check for the task. Implemented for all built-in executors. // For tasks that specify an executor, it is the executor's responsibility // to implement checking support. Executors should (all built-in executors // will) neither interpret nor act on the check's result. // // NOTE: Check support in built-in executors is experimental. // // TODO(alexr): Consider supporting multiple checks per task. optional CheckInfo check = 13; // A kill policy for the task. Implemented for executor-less // command-based and docker tasks. For tasks that specify an // executor, it is the executor's responsibility to implement // the kill policy. optional KillPolicy kill_policy = 12; optional bytes data = 6; // Labels are free-form key value pairs which are exposed through // master and agent endpoints. Labels will not be interpreted or // acted upon by Mesos itself. As opposed to the data field, labels // will be kept in memory on master and agent processes. Therefore, // labels should be used to tag tasks with light-weight meta-data. // Labels should not contain duplicate key-value pairs. optional Labels labels = 10; // Service discovery information for the task. It is not interpreted // or acted upon by Mesos. It is up to a service discovery system // to use this information as needed and to handle tasks without // service discovery information. optional DiscoveryInfo discovery = 11; // Maximum duration for task completion. If the task is non-terminal at the // end of this duration, it will fail with the reason // `REASON_MAX_COMPLETION_TIME_REACHED`. Mesos supports this field for // executor-less tasks, and tasks that use Docker or default executors. // It is the executor's responsibility to implement this, so it might not be // supported by all custom executors. optional DurationInfo max_completion_time = 14; } /** * Describes a group of tasks that belong to an executor. The * executor will receive the task group in a single message to * allow the group to be launched "atomically". * * NOTES: * 1) `NetworkInfo` must not be set inside task's `ContainerInfo`. * 2) `TaskInfo.executor` doesn't need to set. If set, it should match * `LaunchGroup.executor`. */ message TaskGroupInfo { repeated TaskInfo tasks = 1; } // TODO(bmahler): Add executor_uuid here, and send it to the master. This will // allow us to expose executor work directories for tasks in the webui when // looking from the master level. Currently only the agent knows which run the // task belongs to. /** * Describes a task, similar to `TaskInfo`. * * `Task` is used in some of the Mesos messages found below. * `Task` is used instead of `TaskInfo` if: * 1) we need additional IDs, such as a specific * framework, executor, or agent; or * 2) we do not need the additional data, such as the command run by the * task or the health checks. These additional fields may be large and * unnecessary for some Mesos messages. * * `Task` is generally constructed from a `TaskInfo`. See protobuf::createTask. */ message Task { required string name = 1; required TaskID task_id = 2; required FrameworkID framework_id = 3; optional ExecutorID executor_id = 4; required AgentID agent_id = 5; required TaskState state = 6; // Latest state of the task. repeated Resource resources = 7; repeated TaskStatus statuses = 8; // These fields correspond to the state and uuid of the latest // status update forwarded to the master. // NOTE: Either both the fields must be set or both must be unset. optional TaskState status_update_state = 9; optional bytes status_update_uuid = 10; optional Labels labels = 11; // Service discovery information for the task. It is not interpreted // or acted upon by Mesos. It is up to a service discovery system // to use this information as needed and to handle tasks without // service discovery information. optional DiscoveryInfo discovery = 12; // Container information for the task. optional ContainerInfo container = 13; // Specific user under which task is running. optional string user = 14; } /** * Describes possible task states. IMPORTANT: Mesos assumes tasks that * enter terminal states (see below) imply the task is no longer * running and thus clean up any thing associated with the task * (ultimately offering any resources being consumed by that task to * another task). */ enum TaskState { TASK_STAGING = 6; // Initial state. Framework status updates should not use. TASK_STARTING = 0; // The task is being launched by the executor. TASK_RUNNING = 1; // NOTE: This should only be sent when the framework has // the TASK_KILLING_STATE capability. TASK_KILLING = 8; // The task is being killed by the executor. // The task finished successfully on its own without external interference. TASK_FINISHED = 2; // TERMINAL. TASK_FAILED = 3; // TERMINAL: The task failed to finish successfully. TASK_KILLED = 4; // TERMINAL: The task was killed by the executor. TASK_ERROR = 7; // TERMINAL: The task description contains an error. // In Mesos 1.3, this will only be sent when the framework does NOT // opt-in to the PARTITION_AWARE capability. // // NOTE: This state is not always terminal. For example, tasks might // transition from TASK_LOST to TASK_RUNNING or other states when a // partitioned agent reregisters. TASK_LOST = 5; // The task failed but can be rescheduled. // The following task states are only sent when the framework // opts-in to the PARTITION_AWARE capability. // The task failed to launch because of a transient error. The // task's executor never started running. Unlike TASK_ERROR, the // task description is valid -- attempting to launch the task again // may be successful. TASK_DROPPED = 9; // TERMINAL. // The task was running on an agent that has lost contact with the // master, typically due to a network failure or partition. The task // may or may not still be running. TASK_UNREACHABLE = 10; // The task is no longer running. This can occur if the agent has // been terminated along with all of its tasks (e.g., the host that // was running the agent was rebooted). It might also occur if the // task was terminated due to an agent or containerizer error, or if // the task was preempted by the QoS controller in an // oversubscription scenario. TASK_GONE = 11; // TERMINAL. // The task was running on an agent that the master cannot contact; // the operator has asserted that the agent has been shutdown, but // this has not been directly confirmed by the master. If the // operator is correct, the task is not running and this is a // terminal state; if the operator is mistaken, the task may still // be running and might return to RUNNING in the future. TASK_GONE_BY_OPERATOR = 12; // The master has no knowledge of the task. This is typically // because either (a) the master never had knowledge of the task, or // (b) the master forgot about the task because it garbage collected // its metadata about the task. The task may or may not still be // running. TASK_UNKNOWN = 13; } /** * Describes a resource limitation that caused a task failure. */ message TaskResourceLimitation { // This field contains the resource whose limits were violated. // // NOTE: 'Resources' is used here because the resource may span // multiple roles (e.g. `"mem(*):1;mem(role):2"`). repeated Resource resources = 1; } /** * Describes a UUID. */ message UUID { required bytes value = 1; } /** * Describes an operation, similar to `Offer.Operation`, with * some additional information. */ message Operation { optional FrameworkID framework_id = 1; optional AgentID agent_id = 2; required Offer.Operation info = 3; required OperationStatus latest_status = 4; // All the statuses known to this operation. Some of the statuses in this // list might not have been acknowledged yet. The statuses are ordered. repeated OperationStatus statuses = 5; // This is the internal UUID for the operation, which is kept independently // from the framework-specified operation ID, which is optional. required UUID uuid = 6; } /** * Describes possible operation states. */ enum OperationState { // Default value if the enum is not set. See MESOS-4997. OPERATION_UNSUPPORTED = 0; // Initial state. OPERATION_PENDING = 1; // TERMINAL: The operation was successfully applied. OPERATION_FINISHED = 2; // TERMINAL: The operation failed to apply. OPERATION_FAILED = 3; // TERMINAL: The operation description contains an error. OPERATION_ERROR = 4; // TERMINAL: The operation was dropped due to a transient error. OPERATION_DROPPED = 5; // The operation affects an agent that has lost contact with the master, // typically due to a network failure or partition. The operation may or may // not still be pending. OPERATION_UNREACHABLE = 6; // The operation affected an agent that the master cannot contact; // the operator has asserted that the agent has been shutdown, but this has // not been directly confirmed by the master. // // If the operator is correct, the operation is not pending and this is a // terminal state; if the operator is mistaken, the operation may still be // pending and might return to a different state in the future. OPERATION_GONE_BY_OPERATOR = 7; // The operation affects an agent that the master recovered from its // state, but that agent has not yet re-registered. // // The operation can transition to `OPERATION_UNREACHABLE` if the // corresponding agent is marked as unreachable, and will transition to // another status if the agent re-registers. OPERATION_RECOVERING = 8; // The master has no knowledge of the operation. This is typically // because either (a) the master never had knowledge of the operation, or // (b) the master forgot about the operation because it garbage collected // its metadata about the operation. The operation may or may not still be // pending. OPERATION_UNKNOWN = 9; } /** * Describes the current status of an operation. */ message OperationStatus { // While frameworks will only receive status updates for operations on which // they have set an ID, this field is optional because this message is also // used internally by Mesos components when the operation's ID has not been // set. optional OperationID operation_id = 1; required OperationState state = 2; optional string message = 3; // Converted resources after applying the operation. This only // applies if the `state` is `OPERATION_FINISHED`. repeated Resource converted_resources = 4; // Statuses that are delivered reliably to the scheduler will // include a `uuid`. The status is considered delivered once // it is acknowledged by the scheduler. optional UUID uuid = 5; } /** * Describes the status of a check. Type and the corresponding field, i.e., * `command` or `http` must be set. If the result of the check is not available * (e.g., the check timed out), these fields must contain empty messages, i.e., * `exit_code` or `status_code` will be unset. * * NOTE: This API is subject to change and the related feature is experimental. */ message CheckStatusInfo { message Command { // Exit code of a command check. It is the result of calling // `WEXITSTATUS()` on `waitpid()` termination information on // Posix and calling `GetExitCodeProcess()` on Windows. optional int32 exit_code = 1; } message Http { // HTTP status code of an HTTP check. optional uint32 status_code = 1; } message Tcp { // Whether a TCP connection succeeded. optional bool succeeded = 1; } // TODO(alexr): Consider adding a `data` field, which can contain, e.g., // truncated stdout/stderr output for command checks or HTTP response body // for HTTP checks. Alternatively, it can be an even shorter `message` field // containing the last line of stdout or Reason-Phrase of the status line of // the HTTP response. // The type of the check this status corresponds to. optional CheckInfo.Type type = 1; // Status of a command check. optional Command command = 2; // Status of an HTTP check. optional Http http = 3; // Status of a TCP check. optional Tcp tcp = 4; // TODO(alexr): Consider introducing a "last changed at" timestamp, since // task status update's timestamp may not correspond to the last check's // state, e.g., for reconciliation. // TODO(alexr): Consider introducing a `reason` enum here to explicitly // distinguish between completed, delayed, and timed out checks. } /** * Describes the current status of a task. */ message TaskStatus { // Describes the source of the task status update. enum Source { SOURCE_MASTER = 0; SOURCE_AGENT = 1; SOURCE_EXECUTOR = 2; } // Detailed reason for the task status update. // Refer to docs/task-state-reasons.md for additional explanation. enum Reason { // TODO(jieyu): The default value when a caller doesn't check for // presence is 0 and so ideally the 0 reason is not a valid one. // Since this is not used anywhere, consider removing this reason. REASON_COMMAND_EXECUTOR_FAILED = 0; REASON_CONTAINER_LAUNCH_FAILED = 21; REASON_CONTAINER_LIMITATION = 19; REASON_CONTAINER_LIMITATION_DISK = 20; REASON_CONTAINER_LIMITATION_MEMORY = 8; REASON_CONTAINER_PREEMPTED = 17; REASON_CONTAINER_UPDATE_FAILED = 22; REASON_MAX_COMPLETION_TIME_REACHED = 33; REASON_EXECUTOR_REGISTRATION_TIMEOUT = 23; REASON_EXECUTOR_REREGISTRATION_TIMEOUT = 24; REASON_EXECUTOR_TERMINATED = 1; REASON_EXECUTOR_UNREGISTERED = 2; // No longer used. REASON_FRAMEWORK_REMOVED = 3; REASON_GC_ERROR = 4; REASON_INVALID_FRAMEWORKID = 5; REASON_INVALID_OFFERS = 6; REASON_IO_SWITCHBOARD_EXITED = 27; REASON_MASTER_DISCONNECTED = 7; REASON_RECONCILIATION = 9; REASON_RESOURCES_UNKNOWN = 18; REASON_AGENT_DISCONNECTED = 10; REASON_AGENT_REMOVED = 11; REASON_AGENT_REMOVED_BY_OPERATOR = 31; REASON_AGENT_REREGISTERED = 32; REASON_AGENT_RESTARTED = 12; REASON_AGENT_UNKNOWN = 13; REASON_TASK_KILLED_DURING_LAUNCH = 30; REASON_TASK_CHECK_STATUS_UPDATED = 28; REASON_TASK_HEALTH_CHECK_STATUS_UPDATED = 29; REASON_TASK_GROUP_INVALID = 25; REASON_TASK_GROUP_UNAUTHORIZED = 26; REASON_TASK_INVALID = 14; REASON_TASK_UNAUTHORIZED = 15; REASON_TASK_UNKNOWN = 16; } required TaskID task_id = 1; required TaskState state = 2; optional string message = 4; // Possible message explaining state. optional Source source = 9; optional Reason reason = 10; optional bytes data = 3; optional AgentID agent_id = 5; optional ExecutorID executor_id = 7; // TODO(benh): Use in master/agent. optional double timestamp = 6; // Statuses that are delivered reliably to the scheduler will // include a 'uuid'. The status is considered delivered once // it is acknowledged by the scheduler. Schedulers can choose // to either explicitly acknowledge statuses or let the scheduler // driver implicitly acknowledge (default). // // TODO(bmahler): This is currently overwritten in the scheduler // driver and executor driver, but executors will need to set this // to a valid RFC-4122 UUID if using the HTTP API. optional bytes uuid = 11; // Describes whether the task has been determined to be healthy (true) or // unhealthy (false) according to the `health_check` field in `TaskInfo`. optional bool healthy = 8; // Contains check status for the check specified in the corresponding // `TaskInfo`. If no check has been specified, this field must be // absent, otherwise it must be present even if the check status is // not available yet. If the status update is triggered for a different // reason than `REASON_TASK_CHECK_STATUS_UPDATED`, this field will contain // the last known value. // // NOTE: A check-related task status update is triggered if and only if // the value or presence of any field in `CheckStatusInfo` changes. // // NOTE: Check support in built-in executors is experimental. optional CheckStatusInfo check_status = 15; // Labels are free-form key value pairs which are exposed through // master and agent endpoints. Labels will not be interpreted or // acted upon by Mesos itself. As opposed to the data field, labels // will be kept in memory on master and agent processes. Therefore, // labels should be used to tag TaskStatus message with light-weight // meta-data. Labels should not contain duplicate key-value pairs. optional Labels labels = 12; // Container related information that is resolved dynamically such as // network address. optional ContainerStatus container_status = 13; // The time (according to the master's clock) when the agent where // this task was running became unreachable. This is only set on // status updates for tasks running on agents that are unreachable // (e.g., partitioned away from the master). optional TimeInfo unreachable_time = 14; // If the reason field indicates a container resource limitation, // this field optionally contains additional information. optional TaskResourceLimitation limitation = 16; } /** * Describes possible filters that can be applied to unused resources * (see SchedulerDriver::launchTasks) to influence the allocator. */ message Filters { // Time to consider unused resources refused. Note that all unused // resources will be considered refused and use the default value // (below) regardless of whether Filters was passed to // SchedulerDriver::launchTasks. You MUST pass Filters with this // field set to change this behavior (i.e., get another offer which // includes unused resources sooner or later than the default). // // If this field is set to a number of seconds greater than 31536000 // (365 days), then the resources will be considered refused for 365 // days. If it is set to a negative number, then the default value // will be used. optional double refuse_seconds = 1 [default = 5.0]; } /** * Describes a collection of environment variables. This is used with * CommandInfo in order to set environment variables before running a * command. The contents of each variable may be specified as a string * or a Secret; only one of `value` and `secret` must be set. */ message Environment { message Variable { required string name = 1; enum Type { UNKNOWN = 0; VALUE = 1; SECRET = 2; } // In Mesos 1.2, the `Environment.variables.value` message was made // optional. The default type for `Environment.variables.type` is now VALUE, // which requires `value` to be set, maintaining backward compatibility. // // TODO(greggomann): The default can be removed in Mesos 2.1 (MESOS-7134). optional Type type = 3 [default = VALUE]; // Only one of `value` and `secret` must be set. optional string value = 2; optional Secret secret = 4; } repeated Variable variables = 1; } /** * A generic (key, value) pair used in various places for parameters. */ message Parameter { required string key = 1; required string value = 2; } /** * Collection of Parameter. */ message Parameters { repeated Parameter parameter = 1; } /** * Credential used in various places for authentication and * authorization. * * NOTE: A 'principal' is different from 'FrameworkInfo.user'. The * former is used for authentication and authorization while the * latter is used to determine the default user under which the * framework's executors/tasks are run. */ message Credential { required string principal = 1; optional string secret = 2; } /** * Credentials used for framework authentication, HTTP authentication * (where the common 'username' and 'password' are captured as * 'principal' and 'secret' respectively), etc. */ message Credentials { repeated Credential credentials = 1; } /** * Secret used to pass privileged information. It is designed to provide * pass-by-value or pass-by-reference semantics, where the REFERENCE type can be * used by custom modules which interact with a secure back-end. */ message Secret { enum Type { UNKNOWN = 0; REFERENCE = 1; VALUE = 2; } // Can be used by modules to refer to a secret stored in a secure back-end. // The `key` field is provided to permit reference to a single value within a // secret containing arbitrary key-value pairs. // // For example, given a back-end secret store with a secret named // "my-secret" containing the following key-value pairs: // // { // "username": "my-user", // "password": "my-password // } // // the username could be referred to in a `Secret` by specifying // "my-secret" for the `name` and "username" for the `key`. message Reference { required string name = 1; optional string key = 2; } // Used to pass the value of a secret. message Value { required bytes data = 1; } optional Type type = 1; // Only one of `reference` and `value` must be set. optional Reference reference = 2; optional Value value = 3; } /** * Rate (queries per second, QPS) limit for messages from a framework to master. * Strictly speaking they are the combined rate from all frameworks of the same * principal. */ message RateLimit { // Leaving QPS unset gives it unlimited rate (i.e., not throttled), // which also implies unlimited capacity. optional double qps = 1; // Principal of framework(s) to be throttled. Should match // FrameworkInfo.principal and Credential.principal (if using authentication). required string principal = 2; // Max number of outstanding messages from frameworks of this principal // allowed by master before the next message is dropped and an error is sent // back to the sender. Messages received before the capacity is reached are // still going to be processed after the error is sent. // If unspecified, this principal is assigned unlimited capacity. // NOTE: This value is ignored if 'qps' is not set. optional uint64 capacity = 3; } /** * Collection of RateLimit. * Frameworks without rate limits defined here are not throttled unless * 'aggregate_default_qps' is specified. */ message RateLimits { // Items should have unique principals. repeated RateLimit limits = 1; // All the frameworks not specified in 'limits' get this default rate. // This rate is an aggregate rate for all of them, i.e., their combined // traffic is throttled together at this rate. optional double aggregate_default_qps = 2; // All the frameworks not specified in 'limits' get this default capacity. // This is an aggregate value similar to 'aggregate_default_qps'. optional uint64 aggregate_default_capacity = 3; } /** * Describe an image used by tasks or executors. Note that it's only * for tasks or executors launched by MesosContainerizer currently. */ message Image { enum Type { APPC = 1; DOCKER = 2; } // Protobuf for specifying an Appc container image. See: // https://github.com/appc/spec/blob/master/spec/aci.md message Appc { // The name of the image. required string name = 1; // An image ID is a string of the format "hash-value", where // "hash" is the hash algorithm used and "value" is the hex // encoded string of the digest. Currently the only permitted // hash algorithm is sha512. optional string id = 2; // Optional labels. Suggested labels: "version", "os", and "arch". optional Labels labels = 3; } message Docker { // The name of the image. Expected format: // [REGISTRY_HOST[:REGISTRY_PORT]/]REPOSITORY[:TAG|@TYPE:DIGEST] // // See: https://docs.docker.com/reference/commandline/pull/ required string name = 1; // Credential to authenticate with docker registry. // NOTE: This is not encrypted, therefore framework and operators // should enable SSL when passing this information. // // This field has never been used in Mesos before and is // deprecated since Mesos 1.3. Please use `config` below // (see MESOS-7088 for details). optional Credential credential = 2 [deprecated = true]; // Since 1.3. // Docker config containing credentials to authenticate with // docker registry. The secret is expected to be a docker // config file in JSON format with UTF-8 character encoding. optional Secret config = 3; } required Type type = 1; // Only one of the following image messages should be set to match // the type. optional Appc appc = 2; optional Docker docker = 3; // With this flag set to false, the mesos containerizer will pull // the docker/appc image from the registry even if the image is // already downloaded on the agent. optional bool cached = 4 [default = true]; } /** * Describes how the mount will be propagated for a volume. See the * following doc for more details about mount propagation: * https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt */ message MountPropagation { enum Mode { UNKNOWN = 0; // The volume in a container will receive new mounts from the host // or other containers, but filesystems mounted inside the // container won't be propagated to the host or other containers. // This is currently the default behavior for all volumes. HOST_TO_CONTAINER = 1; // The volume in a container will receive new mounts from the host // or other containers, and its own mounts will be propagated from // the container to the host or other containers. BIDIRECTIONAL = 2; } optional Mode mode = 1; } /** * Describes a volume mapping either from host to container or vice * versa. Both paths can either refer to a directory or a file. */ message Volume { enum Mode { RW = 1; // read-write. RO = 2; // read-only. } // TODO(gyliu513): Make this as `optional` after deprecation cycle of 1.0. required Mode mode = 3; // Path pointing to a directory or file in the container. If the // path is a relative path, it is relative to the container work // directory. If the path is an absolute path, that path must // already exist. required string container_path = 1; // The following specifies the source of this volume. At most one of // the following should be set. // Absolute path pointing to a directory or file on the host or a // path relative to the container work directory. optional string host_path = 2; // The source of the volume is an Image which describes a root // filesystem which will be provisioned by Mesos. optional Image image = 4; // Describes where a volume originates from. message Source { enum Type { // This must be the first enum value in this list, to // ensure that if 'type' is not set, the default value // is UNKNOWN. This enables enum values to be added // in a backwards-compatible way. See: MESOS-4997. UNKNOWN = 0; // TODO(gyliu513): Add IMAGE as volume source type. DOCKER_VOLUME = 1; HOST_PATH = 4; SANDBOX_PATH = 2; SECRET = 3; } message DockerVolume { // Driver of the volume, it can be flocker, convoy, raxrey etc. optional string driver = 1; // Name of the volume. required string name = 2; // Volume driver specific options. optional Parameters driver_options = 3; } // Absolute path pointing to a directory or file on the host. message HostPath { required string path = 1; optional MountPropagation mount_propagation = 2; } // Describe a path from a container's sandbox. The container can // be the current container (SELF), or its parent container // (PARENT). PARENT allows all child containers to share a volume // from their parent container's sandbox. It'll be an error if // the current container is a top level container. message SandboxPath { enum Type { UNKNOWN = 0; SELF = 1; PARENT = 2; } optional Type type = 1; // A path relative to the corresponding container's sandbox. // Note that upwards traversal (i.e. ../../abc) is not allowed. required string path = 2; } // Enum fields should be optional, see: MESOS-4997. optional Type type = 1; // The following specifies the source of this volume. At most one of // the following should be set. // The source of the volume created by docker volume driver. optional DockerVolume docker_volume = 2; optional HostPath host_path = 5; optional SandboxPath sandbox_path = 3; // The volume/secret isolator uses the secret-fetcher module (third-party or // internal) downloads the secret and makes it available at container_path. optional Secret secret = 4; } optional Source source = 5; } /** * Describes a network request from a framework as well as network resolution * provided by Mesos. * * A framework may request the network isolator on the Agent to isolate the * container in a network namespace and create a virtual network interface. * The `NetworkInfo` message describes the properties of that virtual * interface, including the IP addresses and network isolation policy * (network group membership). * * The NetworkInfo message is not interpreted by the Master or Agent and is * intended to be used by Agent and Master modules implementing network * isolation. If the modules are missing, the message is simply ignored. In * future, the task launch will fail if there is no module providing the * network isolation capabilities (MESOS-3390). * * An executor, Agent, or an Agent module may append NetworkInfos inside * TaskStatus::container_status to provide information such as the container IP * address and isolation groups. */ message NetworkInfo { enum Protocol { IPv4 = 1; IPv6 = 2; } // Specifies a request for an IP address, or reports the assigned container // IP address. // // Users can request an automatically assigned IP (for example, via an // IPAM service) or a specific IP by adding a NetworkInfo to the // ContainerInfo for a task. On a request, specifying neither `protocol` // nor `ip_address` means that any available address may be assigned. message IPAddress { // Specify IP address requirement. Set protocol to the desired value to // request the network isolator on the Agent to assign an IP address to the // container being launched. If a specific IP address is specified in // ip_address, this field should not be set. optional Protocol protocol = 1 [default = IPv4]; // Statically assigned IP provided by the Framework. This IP will be // assigned to the container by the network isolator module on the Agent. // This field should not be used with the protocol field above. // // If an explicit address is requested but is unavailable, the network // isolator should fail the task. optional string ip_address = 2; } // When included in a ContainerInfo, each of these represent a // request for an IP address. Each request can specify an explicit address // or the IP protocol to use. // // When included in a TaskStatus message, these inform the framework // scheduler about the IP addresses that are bound to the container // interface. When there are no custom network isolator modules installed, // this field is filled in automatically with the Agent IP address. repeated IPAddress ip_addresses = 5; // Name of the network which will be used by network isolator to determine // the network that the container joins. It's up to the network isolator // to decide how to interpret this field. optional string name = 6; // A group is the name given to a set of logically-related interfaces that // are allowed to communicate among themselves. Network traffic is allowed // between two container interfaces that share at least one network group. // For example, one might want to create separate groups for isolating dev, // testing, qa and prod deployment environments. repeated string groups = 3; // To tag certain metadata to be used by Isolator/IPAM, e.g., rack, etc. optional Labels labels = 4; // Specifies a port mapping request for the task on this network. message PortMapping { required uint32 host_port = 1; required uint32 container_port = 2; // Protocol to expose as (ie: tcp, udp). optional string protocol = 3; } repeated PortMapping port_mappings = 7; }; /** * Encapsulation of `Capabilities` supported by Linux. * Reference: http://linux.die.net/man/7/capabilities. */ message CapabilityInfo { // We start the actual values at an offset(1000) because Protobuf 2 // uses the first value as the default one. Separating the default // value from the real first value helps to disambiguate them. This // is especially valuable for backward compatibility. // See: MESOS-4997. enum Capability { UNKNOWN = 0; CHOWN = 1000; DAC_OVERRIDE = 1001; DAC_READ_SEARCH = 1002; FOWNER = 1003; FSETID = 1004; KILL = 1005; SETGID = 1006; SETUID = 1007; SETPCAP = 1008; LINUX_IMMUTABLE = 1009; NET_BIND_SERVICE = 1010; NET_BROADCAST = 1011; NET_ADMIN = 1012; NET_RAW = 1013; IPC_LOCK = 1014; IPC_OWNER = 1015; SYS_MODULE = 1016; SYS_RAWIO = 1017; SYS_CHROOT = 1018; SYS_PTRACE = 1019; SYS_PACCT = 1020; SYS_ADMIN = 1021; SYS_BOOT = 1022; SYS_NICE = 1023; SYS_RESOURCE = 1024; SYS_TIME = 1025; SYS_TTY_CONFIG = 1026; MKNOD = 1027; LEASE = 1028; AUDIT_WRITE = 1029; AUDIT_CONTROL = 1030; SETFCAP = 1031; MAC_OVERRIDE = 1032; MAC_ADMIN = 1033; SYSLOG = 1034; WAKE_ALARM = 1035; BLOCK_SUSPEND = 1036; AUDIT_READ = 1037; } repeated Capability capabilities = 1; } /** * Encapsulation for Linux specific configuration. * E.g, capabilities, limits etc. */ message LinuxInfo { // Since 1.4.0, deprecated in favor of `effective_capabilities`. optional CapabilityInfo capability_info = 1 [deprecated = true]; // The set of capabilities that are allowed but not initially // granted to tasks. optional CapabilityInfo bounding_capabilities = 2; // Represents the set of capabilities that the task will // be executed with. optional CapabilityInfo effective_capabilities = 3; // If set as 'true', the container shares the pid namespace with // its parent. If the container is a top level container, it will // share the pid namespace with the agent. If the container is a // nested container, it will share the pid namespace with its // parent container. This field will be ignored if 'namespaces/pid' // isolator is not enabled. optional bool share_pid_namespace = 4; } /** * Encapsulation for POSIX rlimits, see * http://pubs.opengroup.org/onlinepubs/009695399/functions/getrlimit.html. * Note that some types might only be defined for Linux. * We use a custom prefix to avoid conflict with existing system macros * (e.g., `RLIMIT_CPU` or `NOFILE`). */ message RLimitInfo { message RLimit { enum Type { UNKNOWN = 0; RLMT_AS = 1; RLMT_CORE = 2; RLMT_CPU = 3; RLMT_DATA = 4; RLMT_FSIZE = 5; RLMT_LOCKS = 6; RLMT_MEMLOCK = 7; RLMT_MSGQUEUE = 8; RLMT_NICE = 9; RLMT_NOFILE = 10; RLMT_NPROC = 11; RLMT_RSS = 12; RLMT_RTPRIO = 13; RLMT_RTTIME = 14; RLMT_SIGPENDING = 15; RLMT_STACK = 16; } optional Type type = 1; // Either both are set or both are not set. // If both are not set, it represents unlimited. // If both are set, we require `soft` <= `hard`. optional uint64 hard = 2; optional uint64 soft = 3; } repeated RLimit rlimits = 1; } /** * Describes the information about (pseudo) TTY that can * be attached to a process running in a container. */ message TTYInfo { message WindowSize { required uint32 rows = 1; required uint32 columns = 2; } optional WindowSize window_size = 1; } /** * Describes a container configuration and allows extensible * configurations for different container implementations. * * NOTE: `ContainerInfo` may be specified, e.g., by a task, even if no * container image is provided. In this case neither `MesosInfo` nor * `DockerInfo` is set, the required `type` must be `MESOS`. This is to * address a case when a task without an image, e.g., a shell script * with URIs, wants to use features originally designed for containers, * for example custom network isolation via `NetworkInfo`. */ message ContainerInfo { // All container implementation types. enum Type { DOCKER = 1; MESOS = 2; } message DockerInfo { // The docker image that is going to be passed to the registry. required string image = 1; // Network options. enum Network { HOST = 1; BRIDGE = 2; NONE = 3; USER = 4; } optional Network network = 2 [default = HOST]; message PortMapping { required uint32 host_port = 1; required uint32 container_port = 2; // Protocol to expose as (ie: tcp, udp). optional string protocol = 3; } repeated PortMapping port_mappings = 3; optional bool privileged = 4 [default = false]; // Allowing arbitrary parameters to be passed to docker CLI. // Note that anything passed to this field is not guaranteed // to be supported moving forward, as we might move away from // the docker CLI. repeated Parameter parameters = 5; // With this flag set to true, the docker containerizer will // pull the docker image from the registry even if the image // is already downloaded on the agent. optional bool force_pull_image = 6; // The name of volume driver plugin. optional string volume_driver = 7 [deprecated = true]; // Since 1.0 } message MesosInfo { optional Image image = 1; } required Type type = 1; repeated Volume volumes = 2; optional string hostname = 4; // Only one of the following *Info messages should be set to match // the type. optional DockerInfo docker = 3; optional MesosInfo mesos = 5; // A list of network requests. A framework can request multiple IP addresses // for the container. repeated NetworkInfo network_infos = 7; // Linux specific information for the container. optional LinuxInfo linux_info = 8; // (POSIX only) rlimits of the container. optional RLimitInfo rlimit_info = 9; // If specified a tty will be attached to the container entrypoint. optional TTYInfo tty_info = 10; } /** * Container related information that is resolved during container * setup. The information is sent back to the framework as part of the * TaskStatus message. */ message ContainerStatus { optional ContainerID container_id = 4; // This field can be reliably used to identify the container IP address. repeated NetworkInfo network_infos = 1; // Information about Linux control group (cgroup). optional CgroupInfo cgroup_info = 2; // Information about Executor PID. optional uint32 executor_pid = 3; } /** * Linux control group (cgroup) information. */ message CgroupInfo { // Configuration of a blkio cgroup subsystem. message Blkio { enum Operation { UNKNOWN = 0; TOTAL = 1; READ = 2; WRITE = 3; SYNC = 4; ASYNC = 5; } // Describes a stat value without the device descriptor part. message Value { optional Operation op = 1; // Required. optional uint64 value = 2; // Required. } message CFQ { message Statistics { // Stats are grouped by block devices. If `device` is not // set, it represents `Total`. optional Device.Number device = 1; // blkio.sectors optional uint64 sectors = 2; // blkio.time optional uint64 time = 3; // blkio.io_serviced repeated Value io_serviced = 4; // blkio.io_service_bytes repeated Value io_service_bytes = 5; // blkio.io_service_time repeated Value io_service_time = 6; // blkio.io_wait_time repeated Value io_wait_time = 7; // blkio.io_merged repeated Value io_merged = 8; // blkio.io_queued repeated Value io_queued = 9; } // TODO(jasonlai): Add fields for blkio weight and weight // device. } message Throttling { message Statistics { // Stats are grouped by block devices. If `device` is not // set, it represents `Total`. optional Device.Number device = 1; // blkio.throttle.io_serviced repeated Value io_serviced = 2; // blkio.throttle.io_service_bytes repeated Value io_service_bytes = 3; } // TODO(jasonlai): Add fields for blkio.throttle.*_device. } message Statistics { repeated CFQ.Statistics cfq = 1; repeated CFQ.Statistics cfq_recursive = 2; repeated Throttling.Statistics throttling = 3; } } // Configuration of a net_cls cgroup subsystem. message NetCls { // The 32-bit classid consists of two parts, a 16 bit major handle // and a 16-bit minor handle. The major and minor handle are // represented using the format 0xAAAABBBB, where 0xAAAA is the // 16-bit major handle and 0xBBBB is the 16-bit minor handle. optional uint32 classid = 1; } optional NetCls net_cls = 1; } /** * Collection of labels. Labels should not contain duplicate key-value * pairs. */ message Labels { repeated Label labels = 1; } /** * Key, value pair used to store free form user-data. */ message Label { required string key = 1; optional string value = 2; } /** * Named port used for service discovery. */ message Port { // Port number on which the framework exposes a service. required uint32 number = 1; // Name of the service hosted on this port. optional string name = 2; // Layer 4-7 protocol on which the framework exposes its services. optional string protocol = 3; // This field restricts discovery within a framework (FRAMEWORK), // within a Mesos cluster (CLUSTER), or places no restrictions (EXTERNAL). // The visibility setting for a Port overrides the general visibility setting // in the DiscoveryInfo. optional DiscoveryInfo.Visibility visibility = 4; // This can be used to decorate the message with metadata to be // interpreted by external applications such as firewalls. optional Labels labels = 5; } /** * Collection of ports. */ message Ports { repeated Port ports = 1; } /** * Service discovery information. * The visibility field restricts discovery within a framework (FRAMEWORK), * within a Mesos cluster (CLUSTER), or places no restrictions (EXTERNAL). * Each port in the ports field also has an optional visibility field. * If visibility is specified for a port, it overrides the default service-wide * DiscoveryInfo.visibility for that port. * The environment, location, and version fields provide first class support for * common attributes used to differentiate between similar services. The * environment may receive values such as PROD/QA/DEV, the location field may * receive values like EAST-US/WEST-US/EUROPE/AMEA, and the version field may * receive values like v2.0/v0.9. The exact use of these fields is up to each * service discovery system. */ message DiscoveryInfo { enum Visibility { FRAMEWORK = 0; CLUSTER = 1; EXTERNAL = 2; } required Visibility visibility = 1; optional string name = 2; optional string environment = 3; optional string location = 4; optional string version = 5; optional Ports ports = 6; optional Labels labels = 7; } /** * Named WeightInfo to indicate resource allocation * priority between the different roles. */ message WeightInfo { required double weight = 1; // Related role name. optional string role = 2; } /** * Version information of a component. */ message VersionInfo { required string version = 1; optional string build_date = 2; optional double build_time = 3; optional string build_user = 4; optional string git_sha = 5; optional string git_branch = 6; optional string git_tag = 7; } /** * Flag consists of a name and optionally its value. */ message Flag { required string name = 1; optional string value = 2; } /** * Describes a Role. Roles can be used to specify that certain resources are * reserved for the use of one or more frameworks. */ message Role { required string name = 1; required double weight = 2; repeated FrameworkID frameworks = 3; repeated Resource resources = 4; } /** * Metric consists of a name and optionally its value. */ message Metric { required string name = 1; optional double value = 2; } /** * Describes a File. */ message FileInfo { // Absolute path to the file. required string path = 1; // Number of hard links. optional int32 nlink = 2; // Total size in bytes. optional uint64 size = 3; // Last modification time. optional TimeInfo mtime = 4; // Represents a file's mode and permission bits. The bits have the same // definition on all systems and is portable. optional uint32 mode = 5; // User ID of owner. optional string uid = 6; // Group ID of owner. optional string gid = 7; } /** * Describes information about a device. */ message Device { message Number { required uint64 major_number = 1; required uint64 minor_number = 2; } optional string path = 1; optional Number number = 2; } /** * Describes a device whitelist entry that expose from host to container. */ message DeviceAccess { message Access { optional bool read = 1; optional bool write = 2; optional bool mknod = 3; } required Device device = 1; required Access access = 2; } message DeviceWhitelist { repeated DeviceAccess allowed_devices = 1; } quick-protobuf-0.8.1/tests/rust_protobuf/common/test_enum_alias.rs000064400000000000000000000010431046102023000236730ustar 00000000000000use protobuf::ProtobufEnum; use super::test_enum_alias_pb::*; use test::*; #[test] fn test_enum() { assert_eq!(10, EnumWithAlias::A.value()); assert_eq!(10, EnumWithAlias::A_AGAIN.value()); assert_eq!(&[EnumWithAlias::UNKNOWN, EnumWithAlias::A, EnumWithAlias::B, EnumWithAlias::A_AGAIN], EnumWithAlias::values()); assert_eq!(EnumWithAlias::A, EnumWithAlias::A_AGAIN); } #[test] fn test_enum_in_message() { let mut m = TestEnumWithAlias::new(); m.set_en(EnumWithAlias::A); test_serialize_deserialize("08 0a", &m); } quick-protobuf-0.8.1/tests/rust_protobuf/common/test_oneof.rs000064400000000000000000000030341046102023000226660ustar 00000000000000use quick_protobuf::*; use super::test_oneof_pb::*; use crate::rust_protobuf::hex::{decode_hex, encode_hex}; fn t(f: F) where F: Fn(&mut TestOneof), { let mut o = TestOneof::default(); f(&mut o); test_serialize_deserialize_length_delimited!(&o, TestOneof); } #[test] fn test_simple() { let mut test_message = TestOneof::default(); test_message.one = mod_TestOneof::OneOfone::uint32_field(150); test_serialize_deserialize!("28 96 01", &test_message, TestOneof); } #[test] fn test_types() { t(|o| o.one = mod_TestOneof::OneOfone::double_field(10.0)); t(|o| o.one = mod_TestOneof::OneOfone::float_field(11.0)); t(|o| o.one = mod_TestOneof::OneOfone::int32_field(12)); t(|o| o.one = mod_TestOneof::OneOfone::int64_field(13)); t(|o| o.one = mod_TestOneof::OneOfone::uint32_field(14)); t(|o| o.one = mod_TestOneof::OneOfone::uint64_field(15)); t(|o| o.one = mod_TestOneof::OneOfone::sint32_field(16)); t(|o| o.one = mod_TestOneof::OneOfone::sint64_field(17)); t(|o| o.one = mod_TestOneof::OneOfone::fixed32_field(18)); t(|o| o.one = mod_TestOneof::OneOfone::fixed64_field(19)); t(|o| o.one = mod_TestOneof::OneOfone::sfixed32_field(20)); t(|o| o.one = mod_TestOneof::OneOfone::sfixed64_field(21)); t(|o| o.one = mod_TestOneof::OneOfone::bool_field(true)); t(|o| o.one = mod_TestOneof::OneOfone::string_field("asas".into())); t(|o| o.one = mod_TestOneof::OneOfone::bytes_field(vec![99, 100].into())); t(|o| o.one = mod_TestOneof::OneOfone::enum_field(EnumForOneof::A)); } quick-protobuf-0.8.1/tests/rust_protobuf/generate.sh000075500000000000000000000052261046102023000210210ustar 00000000000000#!/usr/bin/env bash # # Test harness for generating files under tests/rust_protobuf/v[23] # and expecting them either to succeed or fail for some known reason. # # Checked in the end for non-empty value which serves as a boolean flag have_failures="" # Expected codegen failures are marked in the associative array `must_fail` # with the relative path as the key and reason as value. # When adding new, remember not to add any whitespace around `=`. declare -A must_fail must_fail["v2/test_group_pb.proto"]="expected failure (empty read)" must_fail["v3/test_enum_alias_pb.proto"]="enum alias not implemented" must_fail["v2/test_enum_alias_pb.proto"]="enum alias not implemented" must_fail["v2/test_expose_oneof_pb.proto"]="missing file" must_fail["v2/test_enum_invalid_default.proto"]="enum variant does not exist" must_fail["v3/test_enum_invalid_default.proto"]="enum variant does not exist" # Custom arguments to pass to `pb-rs` for generating files used in testing declare -A custom_pbrs_args custom_pbrs_args["v2/test_owned_pb.proto"]="--owned" custom_pbrs_args["v3/test_owned_pb.proto"]="--owned" custom_pbrs_args["v2/test_deprecated_lifetime_can_compile.proto"]="--add-deprecated-fields" custom_pbrs_args["v3/test_deprecated_lifetime_can_compile.proto"]="--add-deprecated-fields" # Combined stdout and stderr for codegen of unexpectedly failed file. declare -A outs expecting_failure() { [ "${must_fail["$1"]+_}" ] } expecting_success() { ! expecting_failure "$1" } success_msg() { if expecting_failure "$1"; then echo "${must_fail["$1"]}" else echo "ok" fi } for f in v[23]/*.proto; do ret=0 rm -f "${f%.proto}.rs" out="$(cargo run -p pb-rs --quiet -- ${custom_pbrs_args[$f]} "$f" 2>&1)" || ret=$? if expecting_failure "$f" && [ "$ret" -eq 0 ]; then outs["$f"]="$out" have_failures="true" echo "$f: unexpected success" elif expecting_success "$f" && [ "$ret" -ne 0 ]; then have_failures="true" outs["$f"]="$out" echo "$f: unexpected failure $ret" else echo "$f: $(success_msg "$f")" fi done for f in common/*.proto; do ret=0 rm -f "${f%.proto}.rs" out="$(cargo run -p pb-rs --quiet -- ${custom_pbrs_args[$f]} "$f" 2>&1)" || ret=$? if expecting_failure "$f" && [ "$ret" -eq 0 ]; then outs["$f"]="$out" have_failures="true" echo "$f: unexpected success" elif expecting_success "$f" && [ "$ret" -ne 0 ]; then have_failures="true" outs["$f"]="$out" echo "$f: unexpected failure $ret" else echo "$f: $(success_msg "$f")" fi done echo if [ "$have_failures" ]; then echo "There were code generation failures:" for f in "${!outs[@]}"; do echo echo "$f:" echo "${outs["$f"]}" done exit 1 else echo "All files generated as expected" fi quick-protobuf-0.8.1/tests/rust_protobuf/hex.rs000064400000000000000000000040321046102023000200140ustar 00000000000000// hex encoder and decoder used by rust-protobuf unittests use std::char; fn decode_hex_digit(digit: char) -> u8 { match digit { '0'..='9' => digit as u8 - '0' as u8, 'a'..='f' => digit as u8 - 'a' as u8 + 10, 'A'..='F' => digit as u8 - 'A' as u8 + 10, _ => panic!(), } } pub fn decode_hex(hex: &str) -> Vec { let mut r: Vec = Vec::new(); let mut chars = hex.chars().enumerate(); loop { let (pos, first) = match chars.next() { None => break, Some(elt) => elt, }; if first == ' ' { continue; } let (_, second) = match chars.next() { None => panic!("pos = {}d", pos), Some(elt) => elt, }; r.push((decode_hex_digit(first) << 4) | decode_hex_digit(second)); } r } fn encode_hex_digit(digit: u8) -> char { match char::from_digit(digit as u32, 16) { Some(c) => c, _ => panic!(), } } fn encode_hex_byte(byte: u8) -> [char; 2] { [encode_hex_digit(byte >> 4), encode_hex_digit(byte & 0x0Fu8)] } pub fn encode_hex(bytes: &[u8]) -> String { let strs: Vec = bytes .iter() .map(|byte| encode_hex_byte(*byte).iter().map(|c| *c).collect()) .collect(); strs.join(" ") } #[cfg(test)] mod test { use super::decode_hex; use super::encode_hex; #[test] fn test_decode_hex() { assert_eq!(decode_hex(""), [].to_vec() as Vec); assert_eq!(decode_hex("00"), [0x00u8].to_vec()); assert_eq!(decode_hex("ff"), [0xffu8].to_vec()); assert_eq!(decode_hex("AB"), [0xabu8].to_vec()); assert_eq!(decode_hex("fa 19"), [0xfau8, 0x19].to_vec()); } #[test] fn test_encode_hex() { assert_eq!("".to_string(), encode_hex(&[])); assert_eq!("00".to_string(), encode_hex(&[0x00])); assert_eq!("ab".to_string(), encode_hex(&[0xab])); assert_eq!( "01 a2 1a fe".to_string(), encode_hex(&[0x01, 0xa2, 0x1a, 0xfe]) ); } } quick-protobuf-0.8.1/tests/rust_protobuf/mod.rs000064400000000000000000000003001046102023000200010ustar 00000000000000#![allow(dead_code)] // utility module from rust-protobuf to decode/encode in hexadecimal // ... used only for tests mod hex; #[macro_use] mod test_utils; mod v2; //#[cfg(proto3)] mod v3; quick-protobuf-0.8.1/tests/rust_protobuf/test_utils.rs000064400000000000000000000037221046102023000214340ustar 00000000000000//! A module to handle macro used for tests only //! //! Macro are used to work around lifetime issues when calling Message::from_reader //! from other fn. //! This is fine as this is not something that should be done in normal case macro_rules! test_serialize { ($hex:expr, $msg:expr) => { let mut serialized = Vec::new(); { let mut writer = Writer::new(&mut serialized); $msg.write_message(&mut writer).unwrap(); } let serialized_hex = encode_hex(&serialized); let hex = encode_hex(&decode_hex($hex)); assert_eq!(serialized_hex, hex); }; } macro_rules! test_deserialize { ($hex:expr, $msg:expr, $name:ident) => { let bytes = decode_hex($hex); { let mut reader = BytesReader::from_bytes(&bytes); let parsed = $name::from_reader(&mut reader, &bytes).unwrap(); assert!($msg.eq(&parsed)); } }; } macro_rules! test_serialize_deserialize_length_delimited { ($msg:expr, $name:ident) => { let mut serialized = Vec::new(); { let mut writer = Writer::new(&mut serialized); writer.write_message($msg).unwrap(); } let mut reader = BytesReader::from_bytes(&serialized); let parsed: $name = reader.read_message(&serialized).unwrap(); assert!($msg.eq(&parsed)); }; } macro_rules! test_serialize_deserialize { ($hex:expr, $msg:expr, $name:ident) => { let expected_bytes = decode_hex($hex); assert_eq!(expected_bytes.len(), $msg.get_size() as usize); test_serialize!($hex, $msg); test_deserialize!($hex, $msg, $name); test_serialize_deserialize_length_delimited!($msg, $name); }; } // pub fn test_serialize_deserialize_no_hex(msg: &M) { // let serialized_bytes = msg.write_to_bytes().unwrap(); // let parsed = parse_from_bytes::(&serialized_bytes).unwrap(); // assert!(*msg == parsed); // } quick-protobuf-0.8.1/tests/rust_protobuf/v2/issue118-2.proto000064400000000000000000000011531046102023000220200ustar 00000000000000/* -*- Mode: protobuf; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: */ syntax = "proto2"; message First { required Fourth first_field = 1; } message Second { optional First second_field = 1; } message Third { required First third_field = 1; } message Fourth { optional Third fourth_field = 1; required Fifth fourth_field_2 = 2; } message Fifth { required Second fifth_field = 1; } message Sixth { optional Sixth sixth_field = 1; } message Seventh { required Eighth seventh_field = 1; } message Eighth { optional Seventh eighth_field = 1; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/issue118.proto000064400000000000000000000007001046102023000216560ustar 00000000000000/* -*- Mode: protobuf; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: */ syntax = "proto2"; message ViaOneOf { oneof Alternatives { Data present = 1; bool absent = 2; } message Data { required ViaOneOf self_reference = 1; } } message ViaOptional { optional Data data = 1; message Data { required ViaOptional self_reference = 1; } } quick-protobuf-0.8.1/tests/rust_protobuf/v2/issue_170.rs000064400000000000000000000013321046102023000212760ustar 00000000000000#[test] fn t() { use super::{ issue_170_a::A, issue_170_b::B, issue_170_c::C, issue_170_common::Common, issue_170_d::D, issue_170_e::E, }; let b = B { common: Some(Common {}), }; let a = A { common: Some(Common {}), b: Some(b.clone()), }; let c = C { a: Some(a.clone()), b: Some(b.clone()), common: Some(Common {}), }; let d = D { a: Some(a.clone()), b: Some(b.clone()), c: Some(c.clone()), common: Some(Common {}), }; let e = E { a: Some(a.clone()), b: Some(b.clone()), c: Some(c.clone()), d: Some(d.clone()), common: Some(Common {}), }; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/issue_170_a.proto000064400000000000000000000002051046102023000223130ustar 00000000000000syntax = "proto2"; import "issue_170_common.proto"; import "issue_170_b.proto"; message A { Common common = 1; B b = 2; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/issue_170_b.proto000064400000000000000000000001331046102023000223140ustar 00000000000000syntax = "proto2"; import "issue_170_common.proto"; message B { Common common = 1; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/issue_170_c.proto000064400000000000000000000002551046102023000223220ustar 00000000000000syntax = "proto2"; import "issue_170_common.proto"; import "issue_170_b.proto"; import "issue_170_a.proto"; message C { Common common = 1; B b = 2; A a = 3; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/issue_170_common.proto000064400000000000000000000000461046102023000233660ustar 00000000000000syntax = "proto2"; message Common {} quick-protobuf-0.8.1/tests/rust_protobuf/v2/issue_170_d.proto000064400000000000000000000003261046102023000223220ustar 00000000000000syntax = "proto2"; import "issue_170_common.proto"; import "issue_170_b.proto"; import "issue_170_a.proto"; import "issue_170_c.proto"; message D { Common common = 1; B b = 2; A a = 3; C c = 4; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/issue_170_e.proto000064400000000000000000000003771046102023000223310ustar 00000000000000syntax = "proto2"; import "issue_170_common.proto"; import "issue_170_b.proto"; import "issue_170_a.proto"; import "issue_170_c.proto"; import "issue_170_d.proto"; message E { Common common = 1; B b = 2; A a = 3; C c = 4; D d = 5; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/mod.rs000064400000000000000000000025301046102023000203370ustar 00000000000000//! A module to import many generated files plus some custom tests //! //! For most generated files, the 'test' is just making sure it compiles! pub mod basic; pub mod issue118; pub mod issue118_2; pub mod issue_170; pub mod issue_170_a; pub mod issue_170_b; pub mod issue_170_c; pub mod issue_170_common; pub mod issue_170_d; pub mod issue_170_e; pub mod nonunique_1; pub mod nonunique_2; pub mod shared0; pub mod special; pub mod struct_pb; pub mod test_basic; pub mod test_default_values; pub mod test_default_values_test; pub mod test_deprecated_lifetime; pub mod test_deprecated_lifetime_can_compile; pub mod test_enum_values_pb; pub mod test_ident_pb; pub mod test_import_nested_imported_pb; pub mod test_import_nested_pb; pub mod test_import_nonunique_pb; pub mod test_import_root_imported_pb; pub mod test_import_root_pb; pub mod test_lite_runtime; pub mod test_lite_runtime_test; pub mod test_name_resolution; pub mod test_name_resolution_p2; pub mod test_name_resolution_p3; pub mod test_name_resolution_p4; pub mod test_nested_package_dir; pub mod test_nested_package_dir_alt; pub mod test_nonunique_enum_pb; pub mod test_oneof; pub mod test_oneof_pb; pub mod test_owned; pub mod test_owned_pb; pub mod test_required; pub mod test_required_test; pub mod test_sanitize_file_name_pb; pub mod test_sync; pub mod test_text_format; pub mod test_text_format_pb; quick-protobuf-0.8.1/tests/rust_protobuf/v2/struct.proto000064400000000000000000000001751046102023000216260ustar 00000000000000// this file just checks that file name is properly escaped, // because `struct` is a keyword in rust message KeepTheFile {} quick-protobuf-0.8.1/tests/rust_protobuf/v2/test-sanitize-file-name_pb.proto000064400000000000000000000000461046102023000254160ustar 00000000000000syntax = "proto2"; message FooBar {} quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_basic.rs000064400000000000000000000176121046102023000217070ustar 00000000000000use quick_protobuf::*; use super::basic::*; use crate::rust_protobuf::hex::{decode_hex, encode_hex}; #[test] fn test1() { let mut test1 = Test1::default(); test1.a = 150; test_serialize_deserialize!("08 96 01", &test1, Test1); } #[test] fn test2() { let mut test2 = Test2::default(); test2.b = "testing".into(); test_serialize_deserialize!("12 07 74 65 73 74 69 6e 67", &test2, Test2); } #[test] fn test3() { let mut test1 = Test1::default(); test1.a = 150; let mut test3 = Test3::default(); test3.c = test1; test_serialize_deserialize!("1a 03 08 96 01", &test3, Test3); } #[test] fn test4() { let mut test4 = Test4::default(); test4.d = vec![3i32, 270, 86942]; test_serialize_deserialize!("22 06 03 8E 02 9E A7 05", &test4, Test4); } #[test] fn test_read_unpacked_expect_packed() { let mut test_packed_unpacked = TestPackedUnpacked::default(); test_packed_unpacked.packed = Vec::new(); test_packed_unpacked.unpacked = vec![17i32, 1000]; test_deserialize!("20 11 20 e8 07", &test_packed_unpacked, TestPackedUnpacked); } #[test] fn test_read_packed_expect_unpacked() { let mut test_packed_unpacked = TestPackedUnpacked::default(); test_packed_unpacked.packed = vec![17i32, 1000]; test_packed_unpacked.unpacked = Vec::new(); test_deserialize!("2a 03 11 e8 07", &test_packed_unpacked, TestPackedUnpacked); } #[test] fn test_empty() { test_serialize_deserialize!("", &TestEmpty::default(), TestEmpty); } #[test] #[should_panic] fn test_read_junk() { let bytes = decode_hex("00"); let mut reader = BytesReader::from_bytes(&bytes); Test1::from_reader(&mut reader, &bytes).unwrap(); } // #[test] // fn test_unknown_fields_length_delimited() { // let mut message = TestUnknownFields::new(); // message.set_a(150); // message.mut_unknown_fields().add_length_delimited(4, [0x10u8, 0x20, 0x30].to_vec()); // test_serialize_deserialize("08 96 01 22 03 10 20 30", &message); // } // #[test] // fn test_unknown_fields_fixed32() { // let mut message = TestUnknownFields::new(); // message.set_a(150); // message.mut_unknown_fields().add_fixed32(4, 0x01020304); // message.mut_unknown_fields().add_fixed32(4, 0xA1A2A3A4); // test_serialize_deserialize("08 96 01 25 04 03 02 01 25 A4 A3 A2 A1", &message); // } #[test] fn test_types_singular() { let mut message = TestTypesSingular::default(); message.double_field = Some(19f64); message.float_field = Some(20f32); message.int32_field = Some(21); message.int64_field = Some(-22); message.uint32_field = Some(23); message.uint64_field = Some(24); message.sint32_field = Some(-25); message.sint64_field = Some(26); message.fixed32_field = Some(27); message.fixed64_field = Some(28); message.sfixed32_field = Some(-29); message.sfixed64_field = Some(30); message.bool_field = Some(true); message.string_field = Some("thirty two".into()); message.bytes_field = Some(vec![33u8, 34].into()); message.enum_field = Some(TestEnumDescriptor::BLUE); test_serialize_deserialize_length_delimited!(&message, TestTypesSingular); } #[test] fn test_types_repeated() { let mut message = TestTypesRepeated::default(); message.double_field = vec![19f64, 20f64]; message.float_field = vec![20f32]; message.int32_field = vec![21i32, -22, 23]; message.int64_field = vec![22i64]; message.uint32_field = vec![23u32, 24]; message.uint64_field = vec![24u64]; message.sint32_field = vec![25i32]; message.sint64_field = vec![26i64, -27]; message.fixed32_field = vec![27u32]; message.fixed64_field = vec![28u64]; message.sfixed32_field = vec![29i32, -30]; message.sfixed64_field = vec![30i64]; message.bool_field = vec![true, true]; message.string_field = vec!["thirty two".into(), "thirty three".into()]; message.bytes_field = vec![vec![33u8, 34].into(), vec![35u8].into()]; message.enum_field = vec![TestEnumDescriptor::BLUE, TestEnumDescriptor::GREEN]; test_serialize_deserialize_length_delimited!(&message, TestTypesRepeated); } #[test] fn test_types_repeated_packed() { let mut message = TestTypesRepeatedPacked::default(); message.double_field = vec![19f64, 20f64].into(); message.float_field = vec![20f32].into(); message.int32_field = vec![21i32, -22, 23]; message.int64_field = vec![22i64]; message.uint32_field = vec![23u32, 24]; message.uint64_field = vec![24u64]; message.sint32_field = vec![25i32]; message.sint64_field = vec![26i64, -27]; message.fixed32_field = vec![27u32].into(); message.fixed64_field = vec![28u64].into(); message.sfixed32_field = vec![29i32, -30].into(); message.sfixed64_field = vec![30i64].into(); message.bool_field = vec![true, true]; message.string_field = vec!["thirty two".into(), "thirty three".into()]; message.bytes_field = vec![vec![33u8, 34].into(), vec![35u8].into()]; message.enum_field = vec![TestEnumDescriptor::BLUE, TestEnumDescriptor::GREEN]; test_serialize_deserialize_length_delimited!(&message, TestTypesRepeatedPacked); } // #[test] // fn test_file_descriptor_proto() { // let p: &'static descriptor::FileDescriptorProto = file_descriptor_proto(); // assert!(p.has_name()); // assert_eq!("test_basic_pb.proto", p.get_name()); // } #[test] fn test_default_instance() { let d = TestDefaultInstance::default(); assert_eq!(None, d.field.and_then(|f| f.s)); } // #[test] // fn test_message_descriptor() { // assert_eq!("TestDescriptor", TestDescriptor::new().descriptor().name()); // // let d = reflect::MessageDescriptor::for_type::(); // assert_eq!("TestDescriptor", d.name()); // assert_eq!("basic.TestDescriptor", d.full_name()); // // let mut t = TestDescriptor::new(); // t.set_stuff(55); // // let field = d.field_by_name("stuff"); // assert_eq!(55, field.get_i32(&t)); // } // // #[test] // fn test_enum_descriptor() { // let d = TestEnumDescriptor::RED.enum_descriptor(); // assert_eq!("TestEnumDescriptor", d.name()); // assert_eq!("TestEnumDescriptor", \ // reflect::EnumDescriptor::for_type::().name()); // assert_eq!("GREEN", d.value_by_name("GREEN").name()); // } // #[test] // fn test_invalid_tag() { // // 01 is invalid tag, because field number for that tag would be 0 // let bytes = decode_hex("01 02 03"); // let mut reader = BytesReader::from_bytes(&bytes); // assert!(TestInvalidTag::from_reader(&mut reader, &bytes).is_err()); // } #[test] fn test_truncated_no_varint() { // 08 is valid tag that should be followed by varint let bytes = decode_hex("08"); let mut reader = BytesReader::from_bytes(&bytes); assert!(TestTruncated::from_reader(&mut reader, &bytes).is_err()); } #[test] fn test_truncated_middle_of_varint() { // 08 is field 1, wire type varint // 96 is non-final byte of varint let bytes = decode_hex("08 96"); let mut reader = BytesReader::from_bytes(&bytes); assert!(TestTruncated::from_reader(&mut reader, &bytes).is_err()); } #[test] fn test_truncated_middle_of_length_delimited() { // 0a is field 1, wire type length delimited // 03 is length 3 let bytes = decode_hex("0a 03 10"); let mut reader = BytesReader::from_bytes(&bytes); assert!(TestTruncated::from_reader(&mut reader, &bytes).is_err()); } #[test] fn test_truncated_repeated_packed() { // 12 is field 2, wire type length delimited // 04 is length 4 let bytes = decode_hex("12 04 10 20"); let mut reader = BytesReader::from_bytes(&bytes); assert!(TestTruncated::from_reader(&mut reader, &bytes).is_err()); } #[test] fn test_bug_sint() { { let mut x = TestBugSint::default(); x.s32 = Some(-1); test_serialize_deserialize!("08 01", &x, TestBugSint); } { let mut x = TestBugSint::default(); x.s64 = Some(-2); test_serialize_deserialize!("10 03", &x, TestBugSint); } } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_basic_pb.proto000064400000000000000000000065411046102023000231060ustar 00000000000000syntax = "proto2"; package basic; message Test1 { required int32 a = 1; } message Test2 { required string b = 2; } message Test3 { required Test1 c = 3; } message Test4 { repeated int32 d = 4 [packed=true]; } message TestPackedUnpacked { repeated int32 unpacked = 4; repeated int32 packed = 5 [packed=true]; } message TestEmpty { optional int32 foo = 10; } message TestUnknownFields { required int32 a = 1; } // just check it compiles message TestSelfReference { required TestSelfReference r1 = 1; optional TestSelfReference r2 = 2; } message TestDefaultInstanceField { optional string s = 1; } message TestDefaultInstance { optional TestDefaultInstanceField field = 1; } message TestDescriptor { optional int32 stuff = 10; } enum TestEnumDescriptor { RED = 1; BLUE = 2; GREEN = 3; } message TestTypesSingular { optional double double_field = 1; optional float float_field = 2; optional int32 int32_field = 3; optional int64 int64_field = 4; optional uint32 uint32_field = 5; optional uint64 uint64_field = 6; optional sint32 sint32_field = 7; optional sint64 sint64_field = 8; optional fixed32 fixed32_field = 9; optional fixed64 fixed64_field = 10; optional sfixed32 sfixed32_field = 11; optional sfixed64 sfixed64_field = 12; optional bool bool_field = 13; optional string string_field = 14; optional bytes bytes_field = 15; optional TestEnumDescriptor enum_field = 16; } message TestTypesRepeated { repeated double double_field = 1 [packed=false]; repeated float float_field = 2 [packed=false]; repeated int32 int32_field = 3 [packed=false]; repeated int64 int64_field = 4 [packed=false]; repeated uint32 uint32_field = 5 [packed=false]; repeated uint64 uint64_field = 6 [packed=false]; repeated sint32 sint32_field = 7 [packed=false]; repeated sint64 sint64_field = 8 [packed=false]; repeated fixed32 fixed32_field = 9 [packed=false]; repeated fixed64 fixed64_field = 10 [packed=false]; repeated sfixed32 sfixed32_field = 11 [packed=false]; repeated sfixed64 sfixed64_field = 12 [packed=false]; repeated bool bool_field = 13 [packed=false]; repeated string string_field = 14; repeated bytes bytes_field = 15; repeated TestEnumDescriptor enum_field = 16 [packed=false]; } message TestTypesRepeatedPacked { repeated double double_field = 1 [packed=true]; repeated float float_field = 2 [packed=true]; repeated int32 int32_field = 3 [packed=true]; repeated int64 int64_field = 4 [packed=true]; repeated uint32 uint32_field = 5 [packed=true]; repeated uint64 uint64_field = 6 [packed=true]; repeated sint32 sint32_field = 7 [packed=true]; repeated sint64 sint64_field = 8 [packed=true]; repeated fixed32 fixed32_field = 9 [packed=true]; repeated fixed64 fixed64_field = 10 [packed=true]; repeated sfixed32 sfixed32_field = 11 [packed=true]; repeated sfixed64 sfixed64_field = 12 [packed=true]; repeated bool bool_field = 13 [packed=true]; repeated string string_field = 14; repeated bytes bytes_field = 15; repeated TestEnumDescriptor enum_field = 16 [packed=true]; } message TestInvalidTag { } message TestTruncated { repeated fixed32 ints = 2 [packed=true]; } message TestBugSint { optional sint32 s32 = 1; optional sint64 s64 = 2; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_comments.proto000064400000000000000000000017271046102023000231720ustar 00000000000000syntax = "proto2"; // more stress testing for comments; BOOMs inspired by nerdrew :) // BOOM /* BOOM */ //////////////////// /////////////////// //BOOM//BOOM/\/BOOM/BOOM/*BOOM//BOOM*/ /*//BOOM*/ /// //! //!!//!/!! /** * some documentation * * */ /* /// */ /***************** message X { } ******************/ /* message Y { this would be a syntax error if uncommented } */ /*4 5 6 7 8 9 */ // BOOM /* BOOM */ message A { /*BOOM BOOM BOOM*/ // BOOM enum E1 { // BOOM // BOOM V1 = 1; // BOOM v2 = 2; } // BOOM enum E2 { // BOOM } enum E3 { /* BOOM */ } // BOOM message B { // BOOM // BOOM optional string b = 1; } message C { // BOOM } message D { /* BOOM */ } required string a = 1; oneof Thing { int32 a = 1; // empty oneofs not allowed currently // // } } // no newline at end of filequick-protobuf-0.8.1/tests/rust_protobuf/v2/test_default_values_pb.proto000064400000000000000000000032241046102023000250230ustar 00000000000000syntax = "proto2"; package test_default_values; enum EnumForDefaultValue { ONE = 1; TWO = 2; THREE = 3; } message TestDefaultValues { optional double double_field = 1 [default = 1]; optional float float_field = 2 [default = 2]; optional int32 int32_field = 3 [default = 3]; optional int64 int64_field = 4 [default = 4]; optional uint32 uint32_field = 5 [default = 5]; optional uint64 uint64_field = 6 [default = 6]; optional sint32 sint32_field = 7 [default = 7]; optional sint64 sint64_field = 8 [default = 8]; optional fixed32 fixed32_field = 9 [default = 9]; optional fixed64 fixed64_field = 10 [default = 10]; optional sfixed32 sfixed32_field = 11 [default = 11]; optional sfixed64 sfixed64_field = 12 [default = 12]; optional bool bool_field = 13 [default = true]; optional string string_field = 14 [default = "abc\n22"]; optional bytes bytes_field = 15 [default = "cde\n33"]; optional EnumForDefaultValue enum_field = 16 [default = TWO]; optional EnumForDefaultValue enum_field_without_default = 17; } message TestExtremeDefaultValues { // Text for nonfinite floating-point values. optional double inf_double = 14 [default = inf]; optional double neg_inf_double = 15 [default = -inf]; optional double nan_double = 16 [default = nan]; optional float inf_float = 17 [default = inf]; optional float neg_inf_float = 18 [default = -inf]; optional float nan_float = 19 [default = nan]; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_default_values_test.rs000064400000000000000000000027101046102023000246610ustar 00000000000000use std::f32; use std::f64; use super::test_default_values::*; use quick_protobuf::*; #[test] fn test_default_value_simple() { let bytes = &[]; let mut reader = BytesReader::from_bytes(bytes); let d = TestDefaultValues::from_reader(&mut reader, bytes).unwrap(); assert_eq!(1.0, d.double_field); assert_eq!(2.0, d.float_field); assert_eq!(3, d.int32_field); assert_eq!(4, d.int64_field); assert_eq!(5, d.uint32_field); assert_eq!(6, d.uint64_field); assert_eq!(7, d.sint32_field); assert_eq!(8, d.sint64_field); assert_eq!(9, d.fixed32_field); assert_eq!(10, d.fixed64_field); assert_eq!(11, d.sfixed32_field); assert_eq!(12, d.sfixed64_field); assert_eq!(true, d.bool_field); assert_eq!("abc\n22", d.string_field); assert_eq!(b"cde\n33".to_vec(), d.bytes_field.to_vec()); assert!(EnumForDefaultValue::TWO.eq(&d.enum_field)); assert!(d .enum_field_without_default .map_or(true, |e| e.eq(&EnumForDefaultValue::ONE))); } #[test] fn test_default_value_extreme() { let bytes = &[]; let mut reader = BytesReader::from_bytes(bytes); let d = TestExtremeDefaultValues::from_reader(&mut reader, bytes).unwrap(); assert_eq!(f64::INFINITY, d.inf_double); assert_eq!(f64::NEG_INFINITY, d.neg_inf_double); assert!(d.nan_double.is_nan()); assert_eq!(f32::INFINITY, d.inf_float); assert_eq!(f32::NEG_INFINITY, d.neg_inf_float); assert!(d.nan_float.is_nan()); } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_deprecated_lifetime.rs000064400000000000000000000014551046102023000246020ustar 00000000000000use super::test_deprecated_lifetime_can_compile::{ AndThisToo, HasLifetimeParameterIfAddDeprecatedFields, SameHere, }; #[test] fn test_deprecated_lifetime() { let t = trybuild::TestCases::new(); t.compile_fail("tests/rust_protobuf/v2/test_deprecated_lifetime_must_compile_error.rs"); } #[test] fn test_add_deprecated_lifetime() { // As long as compilation succeeds, we know that these fields must have had // lifetimes autogenerated for them, which is expected behaviour since // `generate.sh` should call pb-rs with the `add-deprecated-fields` option // on `test_deprecated_lifetime_can_compile.proto` for use in this test struct Thing<'a> { field_1: HasLifetimeParameterIfAddDeprecatedFields<'a>, field_2: SameHere<'a>, field_3: AndThisToo<'a>, } } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_deprecated_lifetime_can_compile.proto000064400000000000000000000005551046102023000276520ustar 00000000000000syntax = "proto2"; message HasLifetimeParameterIfAddDeprecatedFields { optional string dep = 1 [deprecated=true]; } message SameHere { oneof ContainsDeprecated { string dep = 1 [deprecated=true]; } } message AndThisToo { optional MessageWithLifetime dep = 1 [deprecated=true]; } message MessageWithLifetime { optional string s = 1; }quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_deprecated_lifetime_must_compile_error.rs000064400000000000000000000013701046102023000305670ustar 00000000000000// DO NOT link to module tree // This file is run remotely from test_deprecated_lifetime.rs and should fail to compile #[path = "./test_deprecated_lifetime_pb.rs"] pub mod proto; use crate::proto::{NorShouldThis, ThisShouldNotHaveALifetimeParameter, ThisShouldntEither}; // The structs that we set here as our fields should not have lifetimes // generated for their definitions; therefore, assigning lifetimes to these // fields should throw an error saying they don't have lifetimes, which we catch // using the `trybuild` crate in `test_deprecated_lifetime.rs`. struct Thing<'a> { throw_error_field_1: ThisShouldNotHaveALifetimeParameter<'a>, throw_error_field_2: ThisShouldntEither<'a>, throw_error_field_3: NorShouldThis<'a>, } fn main() {} quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_deprecated_lifetime_must_compile_error.stderr000064400000000000000000000035001046102023000314430ustar 00000000000000error[E0107]: this struct takes 0 lifetime arguments but 1 lifetime argument was supplied --> tests/rust_protobuf/v2/test_deprecated_lifetime_must_compile_error.rs:13:26 | 13 | throw_error_field_1: ThisShouldNotHaveALifetimeParameter<'a>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---- help: remove these generics | | | expected 0 lifetime arguments | note: struct defined here, with 0 lifetime parameters --> tests/rust_protobuf/v2/./test_deprecated_lifetime_pb.rs | | pub struct ThisShouldNotHaveALifetimeParameter { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0107]: this struct takes 0 lifetime arguments but 1 lifetime argument was supplied --> tests/rust_protobuf/v2/test_deprecated_lifetime_must_compile_error.rs:14:26 | 14 | throw_error_field_2: ThisShouldntEither<'a>, | ^^^^^^^^^^^^^^^^^^---- help: remove these generics | | | expected 0 lifetime arguments | note: struct defined here, with 0 lifetime parameters --> tests/rust_protobuf/v2/./test_deprecated_lifetime_pb.rs | | pub struct ThisShouldntEither { | ^^^^^^^^^^^^^^^^^^ error[E0107]: this struct takes 0 lifetime arguments but 1 lifetime argument was supplied --> tests/rust_protobuf/v2/test_deprecated_lifetime_must_compile_error.rs:15:26 | 15 | throw_error_field_3: NorShouldThis<'a>, | ^^^^^^^^^^^^^---- help: remove these generics | | | expected 0 lifetime arguments | note: struct defined here, with 0 lifetime parameters --> tests/rust_protobuf/v2/./test_deprecated_lifetime_pb.rs | | pub struct NorShouldThis { | ^^^^^^^^^^^^^ quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_deprecated_lifetime_pb.proto000064400000000000000000000005641046102023000260020ustar 00000000000000syntax = "proto2"; message ThisShouldNotHaveALifetimeParameter { optional string dep = 1 [deprecated=true]; } message ThisShouldntEither { oneof ContainsDeprecated { string dep = 1 [deprecated=true]; } } message NorShouldThis { optional MessageWithLifetime dep = 1 [deprecated=true]; } message MessageWithLifetime { optional string s = 1; }quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_enum_alias_pb.proto000064400000000000000000000003411046102023000241320ustar 00000000000000syntax = "proto2"; package test_enum_alias; enum EnumWithAlias { option allow_alias = true; UNKNOWN = 0; A = 10; B = 20; A_AGAIN = 10; } message TestEnumWithAlias { optional EnumWithAlias en = 1; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_enum_invalid_default.proto000064400000000000000000000003241046102023000255130ustar 00000000000000syntax = "proto2"; enum Enum1 { a = 0; b = 1; c = 2; } message Msg1 { optional Enum1 x = 9 [default = a]; // a field exists optional Enum1 y = 9 [default = does_not_exist]; // a field doesn't exist } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_enum_values_pb.proto000064400000000000000000000001641046102023000243430ustar 00000000000000syntax = "proto2"; enum TestEnumValuesEnum { WINTER = 11; SPRING = 22; SUMMER = 33; AUTUMN = 44; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_expose_oneof_pb.proto000064400000000000000000000002461046102023000245120ustar 00000000000000import "rustproto.proto"; option (rustproto.expose_oneof) = false; message TestExposeOneof { oneof foobar { string s = 1; int32 i = 2; } } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_group_pb.proto000064400000000000000000000002751046102023000231570ustar 00000000000000syntax = "proto2"; message MessageWithGroup { optional string aaa = 1; repeated group Identifier = 18 { optional int32 iii = 19; optional string sss = 20; } } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_ident_pb.proto000064400000000000000000000004031046102023000231170ustar 00000000000000syntax = "proto2"; message Vec { } message String { } message Option { } message None { } message Some { } message Message { } message TestType { oneof type { string s = 1; } repeated string struct = 2; repeated uint32 ref = 3; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_import_nested_imported_pb.proto000064400000000000000000000002031046102023000265710ustar 00000000000000syntax = "proto2"; message ContainerForNested { message NestedMessage { } enum NestedEnum { RED = 1; } } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_import_nested_pb.proto000064400000000000000000000003161046102023000246730ustar 00000000000000syntax = "proto2"; import "test_import_nested_imported_pb.proto"; message ContainsImportedNested { optional ContainerForNested.NestedMessage m = 1; optional ContainerForNested.NestedEnum e = 2; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_import_nonunique_1_pb.proto000064400000000000000000000000771046102023000256560ustar 00000000000000syntax = "proto2"; package nonunique_1; message Nonunique {} quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_import_nonunique_2_pb.proto000064400000000000000000000000771046102023000256570ustar 00000000000000syntax = "proto2"; package nonunique_2; message Nonunique {} quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_import_nonunique_pb.proto000064400000000000000000000003401046102023000254270ustar 00000000000000syntax = "proto2"; import "test_import_nonunique_1_pb.proto"; import "test_import_nonunique_2_pb.proto"; message TestImportNonunque { optional nonunique_1.Nonunique n1 = 1; optional nonunique_2.Nonunique n2 = 2; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_import_pkg_nested_imported_pb.proto000064400000000000000000000002251046102023000274360ustar 00000000000000syntax = "proto2"; package foo.baz; message ContainerForNested { message NestedMessage { } enum NestedEnum { RED = 1; } } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_import_pkg_nested_pb.proto000064400000000000000000000003641046102023000255370ustar 00000000000000syntax = "proto2"; import "test_import_pkg_nested_imported_pb.proto"; package foo.bar; message ContainsImportedNested { optional foo.baz.ContainerForNested.NestedMessage m = 1; optional foo.baz.ContainerForNested.NestedEnum e = 2; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_import_root_imported_pb.proto000064400000000000000000000001321046102023000262730ustar 00000000000000syntax = "proto2"; message ImportedMessage { } enum ImportedEnum { SOMETHING = 1; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_import_root_pb.proto000064400000000000000000000002771046102023000244020ustar 00000000000000syntax = "proto2"; import "test_import_root_imported_pb.proto"; message ContainsImported { optional ImportedMessage imported_message = 1; optional ImportedEnum imported_enum = 2; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_lite_runtime_pb.proto000064400000000000000000000003031046102023000245130ustar 00000000000000syntax = "proto2"; package test_lite_runtime; option optimize_for = LITE_RUNTIME; enum EnumTestLiteRuntime { ONE = 1; TWO = 2; } message TestLiteRuntime { optional int32 v = 1; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_lite_runtime_test.rs000064400000000000000000000005131046102023000243550ustar 00000000000000use crate::rust_protobuf::hex::{decode_hex, encode_hex}; use quick_protobuf::*; use super::test_lite_runtime::*; #[test] fn test_lite_runtime() { let mut m = TestLiteRuntime::default(); m.v = Some(10); test_serialize_deserialize!("08 0a", &m, TestLiteRuntime); // test it doesn't crash format!("{:?}", m); } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_name_resolution.rs000064400000000000000000000052611046102023000240260ustar 00000000000000// Within test_name_resolution_p0/p1/p2/p3/p4.rs, there are: // - Many different messages, which all share the name `B` // - Various messages, each of which include some message `B` as a field. // This series of tests aims to check whether each message includes the // *expected* version of `B` (from the expected package, from the expected // module) based on protobuf name resolution rules. As long as they compile, the // tests should pass; we don't actually need to do anything with the structs. #[test] fn test_p0() { let test_package_prefixed_field_names = super::shared0::shared1::shared2::first0::first1::first2::TestPackagePrefixedFieldNames { b: Some(super::shared0::shared1::shared2::second0::second1::second2::B {}), }; let test_non_unique_package_prefixed_field_names = super::shared0::shared1::shared2::first0::first1::first2::TestNonUniquePackagePrefixedFieldNames { b: Some(super::shared0::shared1::shared2::second0::second1::second2::B {}), }; let test_absolute_field_names = super::shared0::shared1::shared2::first0::first1::first2::TestAbsoluteFieldNames { b: Some(super::shared0::shared1::shared2::second0::second1::second2::B {}), }; let should_use_internal_b = super::shared0::shared1::shared2::first0::first1::first2::ShouldUseInternalB { b: Some(super::shared0::shared1::shared2::first0::first1::first2::mod_ShouldUseInternalB::B {}), }; let should_use_same_package_b = super::shared0::shared1::shared2::first0::first1::first2::ShouldUseSamePackageB { b: Some(super::shared0::shared1::shared2::first0::first1::first2::B {}), }; } /* Nothing to test in `test_name_resolution_p1.rs` */ #[test] fn test_p2() { let should_use_internal_b = super::test_name_resolution_p2::ShouldUseInternalB { b: Some(super::test_name_resolution_p2::mod_ShouldUseInternalB::B {}), }; let should_use_same_package_b = super::test_name_resolution_p2::ShouldUseSamePackageB { b: Some(super::test_name_resolution_p2::B {}), }; } #[test] fn test_p3() { let should_use_internal_b = super::test_name_resolution_p3::ShouldUseInternalB { b: Some(super::test_name_resolution_p3::mod_ShouldUseInternalB::B {}), }; let should_use_different_package_b = super::test_name_resolution_p3::ShouldUseDifferentPackageB { b: Some(super::test_name_resolution_p2::B {}), }; } #[test] fn test_p4() { let should_use_same_package_b_despite_import = super::test_name_resolution_p4::ShouldUseSamePackageBDespiteImport { b: Some(super::test_name_resolution_p4::B {}), }; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_name_resolution_p0.proto000064400000000000000000000010601046102023000251350ustar 00000000000000syntax = "proto2"; package shared0.shared1.shared2.first0.first1.first2; import "test_name_resolution_p1.proto"; message TestPackagePrefixedFieldNames { optional second0.second1.second2.B b = 1; } message TestNonUniquePackagePrefixedFieldNames { optional shared2.second0.second1.second2.B b = 1; } message TestAbsoluteFieldNames { optional .shared0.shared1.shared2.second0.second1.second2.B b = 1; } message ShouldUseInternalB { optional B b = 1; message B {} } message ShouldUseSamePackageB { optional B b = 1; } message B {} quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_name_resolution_p1.proto000064400000000000000000000001331046102023000251360ustar 00000000000000syntax = "proto2"; package shared0.shared1.shared2.second0.second1.second2; message B {} quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_name_resolution_p2.proto000064400000000000000000000002421046102023000251400ustar 00000000000000syntax = "proto2"; message ShouldUseInternalB { optional B b = 1; message B {} } message ShouldUseSamePackageB { optional B b = 1; } message B {} quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_name_resolution_p3.proto000064400000000000000000000003021046102023000251360ustar 00000000000000syntax = "proto2"; import "test_name_resolution_p2.proto"; message ShouldUseInternalB { optional B b = 1; message B {} } message ShouldUseDifferentPackageB { optional B b = 1; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_name_resolution_p4.proto000064400000000000000000000002201046102023000251360ustar 00000000000000syntax = "proto2"; import "test_name_resolution_p2.proto"; message ShouldUseSamePackageBDespiteImport { optional B b = 1; } message B {} quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_nested_basic_case_p0.proto000064400000000000000000000002571046102023000253570ustar 00000000000000syntax = "proto2"; package test_nested_package_dir.basic.case.nested; enum Enum { Variant_1 = 0; } message Outer { message Inner { optional Enum enum_1 = 0; } }quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_nested_basic_case_p1.proto000064400000000000000000000001611046102023000253520ustar 00000000000000syntax = "proto2"; package test_nested_package_dir.basic.case.remote; import "test_nested_basic_case_p0.proto";quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_nested_p0.proto000064400000000000000000000022411046102023000232160ustar 00000000000000syntax = "proto2"; package test_nested_package_dir.this.is.a.nested.package; message Level1 { message Level2 { message Level3 { message Level4 { message Level5 { optional int32 level5 = 1; enum Level5NativeEnum { LEVEL5 = 0; } } optional int32 level4 = 1; enum Level4NativeEnum { LEVEL4 = 0; } } optional int32 level3 = 1; enum Level3NativeEnum { LEVEL3 = 0; } } optional int32 level2 = 1; optional OutermostEnum OM = 2; optional Level1NativeEnum L1 = 3; optional Level2NativeEnum L2 = 4; optional Level3.Level3NativeEnum L3 = 5; optional Level3.Level4.Level4NativeEnum L4 = 6; optional Level3.Level4.Level5.Level5NativeEnum L5 = 7; enum Level2NativeEnum { LEVEL2 = 0; } } optional int32 level1 = 1; enum Level1NativeEnum { LEVEL1 = 0; } } enum OutermostEnum { OUTERMOST = 0; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_nested_p1.proto000064400000000000000000000024201046102023000232160ustar 00000000000000syntax = "proto2"; package test_nested_package_dir_alt.different.package.level.same.names; import "test_nested_p0.proto"; message Level1 { message Level2 { message Level3 { message Level4 { message Level5 { optional int32 level5 = 1; enum Level5NativeEnum { LEVEL5 = 0; } } optional int32 level4 = 1; enum Level4NativeEnum { LEVEL4 = 0; } } optional int32 level3 = 1; enum Level3NativeEnum { LEVEL3 = 0; } } optional int32 level2 = 1; optional OutermostEnum OM = 2; optional Level1NativeEnum L1 = 3; optional test_nested_package_dir.this.is.a.nested.package.Level1.Level2.Level2NativeEnum L2 = 4; optional Level3.Level3NativeEnum L3 = 5; optional Level3.Level4.Level4NativeEnum L4 = 6; optional Level3.Level4.Level5.Level5NativeEnum L5 = 7; enum Level2NativeEnum { LEVEL2 = 0; } } optional int32 level1 = 1; enum Level1NativeEnum { LEVEL1 = 0; } } enum OutermostEnum { P1_OUTERMOST = 0; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_nested_p2.proto000064400000000000000000000027501046102023000232250ustar 00000000000000syntax = "proto2"; package test_nested_package_dir_alt.different.package.level.with.some.different.names; import "test_nested_p1.proto"; message unique_p2_Level1 { message Level2 { message Level3 { message Level4 { message unique_p2_Level5 { optional int32 P2_level5 = 1; enum Level5NativeEnum { P2_LEVEL5 = 0; } } optional int32 P2_level4 = 1; enum Level4NativeEnum { P2_LEVEL4 = 0; } } optional int32 P2_level3 = 1; enum Level3NativeEnum { P2_LEVEL3 = 0; } } optional int32 P2_level2 = 1; optional test_nested_package_dir_alt.different.package.level.same.names.OutermostEnum OM = 2; optional Level1NativeEnum L1 = 3; optional Level2NativeEnum L2 = 4; optional test_nested_package_dir_alt.different.package.level.same.names.Level1.Level2.Level3.Level3NativeEnum L3 = 5; optional Level3.Level4.Level4NativeEnum L4 = 6; optional test_nested_package_dir.this.is.a.nested.package.Level1.Level2.Level3.Level4.Level5.Level5NativeEnum L5 = 7; enum Level2NativeEnum { P2_LEVEL2 = 0; } } optional int32 P2_level1 = 1; enum Level1NativeEnum { P2_LEVEL1 = 0; } } enum unique_p2_OutermostEnum { P2_OUTERMOST = 0; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_nested_p3.proto000064400000000000000000000001431046102023000232200ustar 00000000000000syntax = "proto2"; package test_nested_package_dir.empty.package; import "test_nested_p2.proto"; quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_nonunique_enum_pb.proto000064400000000000000000000001661046102023000250670ustar 00000000000000syntax = "proto2"; message MessageA { enum EnumA { FOO = 0; } } message MessageB { enum EnumB { FOO = 0; } } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_oneof.rs000064400000000000000000000003561046102023000217310ustar 00000000000000// generated include!("../common/test_oneof.rs"); #[test] fn test_types_v2() { t(|o| { let mut msg = MessageForOneof::default(); msg.f = Some(22); o.one = mod_TestOneof::OneOfone::message_field(msg); }) } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_oneof_pb.proto000064400000000000000000000013751046102023000231330ustar 00000000000000syntax = "proto2"; enum EnumForOneof { A = 10; } message MessageForOneof { optional int32 f = 1; } message TestOneof { optional string s = 29; oneof one { double double_field = 1; float float_field = 2; int32 int32_field = 3; int64 int64_field = 4; uint32 uint32_field = 5; uint64 uint64_field = 6; sint32 sint32_field = 7; sint64 sint64_field = 8; fixed32 fixed32_field = 9; fixed64 fixed64_field = 10; sfixed32 sfixed32_field = 11; sfixed64 sfixed64_field = 12; bool bool_field = 13; string string_field = 14; bytes bytes_field = 15; EnumForOneof enum_field = 16; MessageForOneof message_field = 17; } } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_owned.rs000064400000000000000000000034221046102023000217340ustar 00000000000000use std::{ borrow::Cow, convert::{TryFrom, TryInto}, }; use quick_protobuf::{BytesReader, MessageRead}; use super::test_owned_pb::*; #[test] fn test_owned() { let t = trybuild::TestCases::new(); t.compile_fail("tests/rust_protobuf/v2/test_owned_must_compile_error.rs"); } #[test] fn test_owned_basic_functionality() { let encoded: Vec = vec![ 0x0au8, 0x07u8, 0x74u8, 0x65u8, 0x73u8, 0x74u8, 0x69u8, 0x6eu8, 0x67u8, ]; // s = "testing" let foo_owned = FooOwned::try_from(encoded.clone()).unwrap(); let foo_owned_temp = FooOwned::try_from(encoded.clone()).unwrap(); let mut foo_owned_temp_mut = FooOwned::try_from(encoded.clone()).unwrap(); let reencoded: Vec = foo_owned_temp.try_into().unwrap(); let mut br_temp = BytesReader::from_bytes(&encoded); let foo = Foo::from_reader(&mut br_temp, &encoded).unwrap(); let buf = foo_owned.buf().clone(); let proto = foo_owned.proto().clone(); let proto_mut = foo_owned_temp_mut.proto_mut(); proto_mut.s = Some(Cow::from("the secret cow level")); let static_foo = Foo { s: Some(Cow::from("the moo moo farm")), }; let from_static_foo = FooOwned::from(static_foo); assert_eq!(Some(Cow::from("testing")), foo_owned.proto().s); // test decoding and `proto()` assert_eq!(encoded, buf); // test `buf()` assert_eq!(foo, proto); // test `proto()` assert_eq!( foo_owned_temp_mut.proto().s, Some(Cow::from("the secret cow level")) ); // test `proto_mut()` assert_eq!(format!("{:?}", foo_owned), format!("{:?}", foo)); // test `try_from()` and `Debug` assert_eq!(encoded, reencoded); // test `try_into()` assert_eq!( Some(Cow::from("the moo moo farm")), from_static_foo.proto().s ); // test `from()` } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_owned_must_compile_error.rs000064400000000000000000000007631046102023000257320ustar 00000000000000// DO NOT link to module tree // This file is run remotely from test_owned.rs and should fail to compile #[path = "./test_owned_pb.rs"] pub mod proto; fn main() { use std::convert::TryFrom; let encoded: Vec = vec![ 0x0au8, 0x07u8, 0x74u8, 0x65u8, 0x73u8, 0x74u8, 0x69u8, 0x6eu8, 0x67u8, ]; // s = "testing" let proto = proto::FooOwned::try_from(encoded).unwrap(); let owned_copy = proto.proto().s.to_owned(); drop(proto); println!("{:?}", owned_copy); } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_owned_must_compile_error.stderr000064400000000000000000000006671046102023000266140ustar 00000000000000error[E0505]: cannot move out of `proto` because it is borrowed --> tests/rust_protobuf/v2/test_owned_must_compile_error.rs:17:10 | 15 | let owned_copy = proto.proto().s.to_owned(); | ------------- borrow of `proto` occurs here 16 | 17 | drop(proto); | ^^^^^ move out of `proto` occurs here 18 | println!("{:?}", owned_copy); | ---------- borrow later used here quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_owned_pb.proto000064400000000000000000000000531046102023000231310ustar 00000000000000message Foo { optional string s = 1; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_required_pb.proto000064400000000000000000000001361046102023000236370ustar 00000000000000syntax = "proto2"; package test_required; message TestRequired { required bool b = 5; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_required_test.rs000064400000000000000000000010711046102023000234750ustar 00000000000000use quick_protobuf::*; use super::test_required::*; #[test] fn test_write_missing_required() { let mut buf = Vec::new(); let mut writer = Writer::new(&mut buf); // every message implements Default and required fields do have a value TestRequired::default().write_message(&mut writer).unwrap(); } #[test] fn test_read_missing_required() { let bytes = &[]; let mut reader = BytesReader::from_bytes(bytes); // every message implements Default and required fields do have a value TestRequired::from_reader(&mut reader, bytes).unwrap(); } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_root_pb.proto000064400000000000000000000002661046102023000230060ustar 00000000000000syntax = "proto2"; // no namespace declaration message Root { message Nested { } // accessing nested message in root namespace repeated .Root.Nested nested = 1; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_special~characters file{name}_pb.proto000064400000000000000000000001621046102023000300250ustar 00000000000000syntax = "proto2"; // More test that filenames are sanitized, even imports. package special; message Outer { } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_sync.rs000064400000000000000000000020631046102023000215740ustar 00000000000000use std::sync::Arc; use std::thread; // use protobuf::CodedInputStream; // use protobuf::Message; use quick_protobuf::*; use super::basic::*; // test messages are sync #[test] fn test_sync() { let m = Arc::new({ let mut r = TestTypesSingular::default(); r.int32_field = Some(23); r }); let threads: Vec<_> = (0..4) .map(|_| { let m_copy = m.clone(); thread::spawn(move || { let mut bytes = Vec::new(); { let mut writer = Writer::new(&mut bytes); m_copy.write_message(&mut writer).unwrap(); } let mut reader = BytesReader::from_bytes(&bytes); let read = TestTypesSingular::from_reader(&mut reader, &bytes).unwrap(); read.int32_field }) }) .collect(); let results = threads .into_iter() .map(|t| t.join().unwrap()) .collect::>(); assert_eq!(&[Some(23), Some(23), Some(23), Some(23)], &results[..]); } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_text_format.rs000064400000000000000000000104551046102023000231600ustar 00000000000000use super::test_text_format_pb::*; fn t(_: &str, mut setter: F) { let mut m = TestTypes::default(); setter(&mut m); // validates that it compiles properly only // assert_eq!(&*print_to_string(&m), expected); } #[test] fn test_singular() { t("int32_singular: 99", |m| m.int32_singular = Some(99)); t("double_singular: 99", |m| m.double_singular = Some(99.0)); t("float_singular: 99", |m| m.float_singular = Some(99.0)); t("int32_singular: 99", |m| m.int32_singular = Some(99)); t("int64_singular: 99", |m| m.int64_singular = Some(99)); t("uint32_singular: 99", |m| m.uint32_singular = Some(99)); t("uint64_singular: 99", |m| m.uint64_singular = Some(99)); t("sint32_singular: 99", |m| m.sint32_singular = Some(99)); t("sint64_singular: 99", |m| m.sint64_singular = Some(99)); t("fixed32_singular: 99", |m| m.fixed32_singular = Some(99)); t("fixed64_singular: 99", |m| m.fixed64_singular = Some(99)); t("sfixed32_singular: 99", |m| m.sfixed32_singular = Some(99)); t("sfixed64_singular: 99", |m| m.sfixed64_singular = Some(99)); t("bool_singular: false", |m| m.bool_singular = Some(false)); t("string_singular: \"abc\"", |m| { m.string_singular = Some("abc".into()) }); t("bytes_singular: \"def\"", |m| { m.bytes_singular = Some(b"def".to_vec().into()) }); t("test_enum_singular: DARK", |m| { m.test_enum_singular = Some(TestEnum::DARK) }); t("test_message_singular {}", |m| { m.test_message_singular = Some(TestMessage::default()); }); } #[test] fn test_repeated_one() { t("int32_repeated: 99", |m| m.int32_repeated.push(99)); t("double_repeated: 99", |m| m.double_repeated.push(99.0)); t("float_repeated: 99", |m| m.float_repeated.push(99.0)); t("int32_repeated: 99", |m| m.int32_repeated.push(99)); t("int64_repeated: 99", |m| m.int64_repeated.push(99)); t("uint32_repeated: 99", |m| m.uint32_repeated.push(99)); t("uint64_repeated: 99", |m| m.uint64_repeated.push(99)); t("sint32_repeated: 99", |m| m.sint32_repeated.push(99)); t("sint64_repeated: 99", |m| m.sint64_repeated.push(99)); t("fixed32_repeated: 99", |m| m.fixed32_repeated.push(99)); t("fixed64_repeated: 99", |m| m.fixed64_repeated.push(99)); t("sfixed32_repeated: 99", |m| m.sfixed32_repeated.push(99)); t("sfixed64_repeated: 99", |m| m.sfixed64_repeated.push(99)); t("bool_repeated: false", |m| m.bool_repeated.push(false)); t("string_repeated: \"abc\"", |m| { m.string_repeated.push("abc".into()) }); t("bytes_repeated: \"def\"", |m| { m.bytes_repeated.push(b"def".to_vec().into()) }); t("test_enum_repeated: DARK", |m| { m.test_enum_repeated.push(TestEnum::DARK) }); t("test_message_repeated {}", |m| { m.test_message_repeated.push(TestMessage::default()); }); } #[test] fn test_repeated_multiple() { t( "uint32_singular: 30 int32_repeated: 10 int32_repeated: -20", |m| { m.uint32_singular = Some(30); m.int32_repeated.push(10); m.int32_repeated.push(-20); }, ); } #[test] fn test_complex_message() { t("test_message_singular {value: 30}", |m| { m.test_message_singular = Some(TestMessage { value: Some(30) }) }); } // #[test] // fn test_string_escaped() { // let mut m = TestTypes::new(); // m.set_string_singular("quote\"newline\nbackslash\\del\x7f".to_string()); // assert_eq!("string_singular: \ // \"quote\\\"newline\\nbackslash\\\\del\\177\"", &*format!("{:?}", m)); // } // // #[test] // fn test_pretty() { // let mut tm = TestMessage::new(); // tm.set_value(23); // let mut m = TestTypes::new(); // m.set_test_message_singular(tm); // m.set_string_singular("abc".to_string()); // m.mut_string_repeated().push("def".to_string()); // m.mut_string_repeated().push("ghi".to_string()); // assert_eq!("string_singular: \"abc\"\ntest_message_singular \ // {\n value: 23\n}\nstring_repeated: \"def\"\n\ // string_repeated: \"ghi\"\n", &*format!("{:#?}", m)); // } // // #[test] // fn test_rust_identifier() { // let mut m = TestTextFormatRustIdentifier::new(); // m.set_field_const(true); // assert_eq!("const: true", &*format!("{:?}", m)); // } quick-protobuf-0.8.1/tests/rust_protobuf/v2/test_text_format_pb.proto000064400000000000000000000032051046102023000243530ustar 00000000000000syntax = "proto2"; enum TestEnum { DARK = 1; LIGHT = 2; } message TestMessage { optional int32 value = 10; } message TestTypes { optional double double_singular = 1; optional float float_singular = 2; optional int32 int32_singular = 3; optional int64 int64_singular = 4; optional uint32 uint32_singular = 5; optional uint64 uint64_singular = 6; optional sint32 sint32_singular = 7; optional sint64 sint64_singular = 8; optional fixed32 fixed32_singular = 9; optional fixed64 fixed64_singular = 10; optional sfixed32 sfixed32_singular = 11; optional sfixed64 sfixed64_singular = 12; optional bool bool_singular = 13; optional string string_singular = 14; optional bytes bytes_singular = 15; optional TestEnum test_enum_singular = 16; optional TestMessage test_message_singular = 17; repeated double double_repeated = 31; repeated float float_repeated = 32; repeated int32 int32_repeated = 33; repeated int64 int64_repeated = 34; repeated uint32 uint32_repeated = 35; repeated uint64 uint64_repeated = 36; repeated sint32 sint32_repeated = 37; repeated sint64 sint64_repeated = 38; repeated fixed32 fixed32_repeated = 39; repeated fixed64 fixed64_repeated = 40; repeated sfixed32 sfixed32_repeated = 41; repeated sfixed64 sfixed64_repeated = 42; repeated bool bool_repeated = 43; repeated string string_repeated = 44; repeated bytes bytes_repeated = 45; repeated TestEnum test_enum_repeated = 46; repeated TestMessage test_message_repeated = 47; } message TestTextFormatRustIdentifier { optional bool const = 1; } quick-protobuf-0.8.1/tests/rust_protobuf/v2/vector_tile.proto000064400000000000000000000054541046102023000226260ustar 00000000000000package vector_tile; option optimize_for = LITE_RUNTIME; message Tile { // GeomType is described in section 4.3.4 of the specification enum GeomType { UNKNOWN = 0; POINT = 1; LINESTRING = 2; POLYGON = 3; } // Variant type encoding // The use of values is described in section 4.1 of the specification message Value { // Exactly one of these values must be present in a valid message optional string string_value = 1; optional float float_value = 2; optional double double_value = 3; optional int64 int_value = 4; optional uint64 uint_value = 5; optional sint64 sint_value = 6; optional bool bool_value = 7; extensions 8 to max; } // Features are described in section 4.2 of the specification message Feature { optional uint64 id = 1 [ default = 0 ]; // Tags of this feature are encoded as repeated pairs of // integers. // A detailed description of tags is located in sections // 4.2 and 4.4 of the specification repeated uint32 tags = 2 [ packed = true ]; // The type of geometry stored in this feature. optional GeomType type = 3 [ default = UNKNOWN ]; // Contains a stream of commands and parameters (vertices). // A detailed description on geometry encoding is located in // section 4.3 of the specification. repeated uint32 geometry = 4 [ packed = true ]; } // Layers are described in section 4.1 of the specification message Layer { // Any compliant implementation must first read the version // number encoded in this message and choose the correct // implementation for this version number before proceeding to // decode other parts of this message. required uint32 version = 15 [ default = 1 ]; required string name = 1; // The actual features in this tile. repeated Feature features = 2; // Dictionary encoding for keys repeated string keys = 3; // Dictionary encoding for values repeated Value values = 4; // Although this is an "optional" field it is required by the specification. // See https://github.com/mapbox/vector-tile-spec/issues/47 optional uint32 extent = 5 [ default = 4096 ]; extensions 16 to max; } repeated Layer layers = 3; extensions 16 to 8191; } quick-protobuf-0.8.1/tests/rust_protobuf/v3/mod.rs000064400000000000000000000005271046102023000203440ustar 00000000000000mod basic; mod test_basic; // mod test_zeros_are_not_written; mod test_map; mod test_map_pb; // mod test_enum_alias_pb; // mod test_enum_alias; mod test_ident_pb; mod test_deprecated_lifetime; mod test_deprecated_lifetime_can_compile; mod test_deprecated_lifetime_pb; mod test_oneof; mod test_oneof_pb; mod test_owned; mod test_owned_pb; quick-protobuf-0.8.1/tests/rust_protobuf/v3/test_basic.rs000064400000000000000000000176101046102023000217060ustar 00000000000000use quick_protobuf::*; use super::basic::*; use crate::rust_protobuf::hex::{decode_hex, encode_hex}; #[test] fn test1() { let mut test1 = Test1::default(); test1.a = 150; test_serialize_deserialize!("08 96 01", &test1, Test1); } #[test] fn test2() { let mut test2 = Test2::default(); test2.b = "testing".into(); test_serialize_deserialize!("12 07 74 65 73 74 69 6e 67", &test2, Test2); } #[test] fn test3() { let mut test1 = Test1::default(); test1.a = 150; let mut test3 = Test3::default(); test3.c = Some(test1); test_serialize_deserialize!("1a 03 08 96 01", &test3, Test3); } #[test] fn test4() { let mut test4 = Test4::default(); test4.d = vec![3i32, 270, 86942]; test_serialize_deserialize!("22 06 03 8E 02 9E A7 05", &test4, Test4); } #[test] fn test_read_unpacked_expect_packed() { let mut test_packed_unpacked = TestPackedUnpacked::default(); test_packed_unpacked.packed = Vec::new(); test_packed_unpacked.unpacked = vec![17i32, 1000]; test_deserialize!("20 11 20 e8 07", &test_packed_unpacked, TestPackedUnpacked); } #[test] fn test_read_packed_expect_unpacked() { let mut test_packed_unpacked = TestPackedUnpacked::default(); test_packed_unpacked.packed = vec![17i32, 1000]; test_packed_unpacked.unpacked = Vec::new(); test_deserialize!("2a 03 11 e8 07", &test_packed_unpacked, TestPackedUnpacked); test_serialize_deserialize_length_delimited!(&test_packed_unpacked, TestPackedUnpacked); } #[test] fn test_empty() { test_serialize_deserialize!("", &TestEmpty::default(), TestEmpty); } #[test] #[should_panic] fn test_read_junk() { let bytes = decode_hex("00"); let mut reader = BytesReader::from_bytes(&bytes); Test1::from_reader(&mut reader, &bytes).unwrap(); } // #[test] // fn test_unknown_fields_length_delimited() { // let mut message = TestUnknownFields::new(); // message.set_a(150); // message.mut_unknown_fields().add_length_delimited(4, [0x10u8, 0x20, 0x30].to_vec()); // test_serialize_deserialize("08 96 01 22 03 10 20 30", &message); // } // #[test] // fn test_unknown_fields_fixed32() { // let mut message = TestUnknownFields::new(); // message.set_a(150); // message.mut_unknown_fields().add_fixed32(4, 0x01020304); // message.mut_unknown_fields().add_fixed32(4, 0xA1A2A3A4); // test_serialize_deserialize("08 96 01 25 04 03 02 01 25 A4 A3 A2 A1", &message); // } #[test] fn test_types_singular() { let mut message = TestTypesSingular::default(); message.double_field = 19f64; message.float_field = 20f32; message.int32_field = 21; message.int64_field = -22; message.uint32_field = 23; message.uint64_field = 24; message.sint32_field = -25; message.sint64_field = 26; message.fixed32_field = 27; message.fixed64_field = 28; message.sfixed32_field = -29; message.sfixed64_field = 30; message.bool_field = true; message.string_field = "thirty two".into(); message.bytes_field = vec![33u8, 34].into(); message.enum_field = TestEnumDescriptor::BLUE; test_serialize_deserialize_length_delimited!(&message, TestTypesSingular); } #[test] fn test_types_repeated() { let mut message = TestTypesRepeated::default(); message.double_field = vec![19f64, 20f64]; message.float_field = vec![20f32]; message.int32_field = vec![21i32, -22, 23]; message.int64_field = vec![22i64]; message.uint32_field = vec![23u32, 24]; message.uint64_field = vec![24u64]; message.sint32_field = vec![25i32]; message.sint64_field = vec![26i64, -27]; message.fixed32_field = vec![27u32]; message.fixed64_field = vec![28u64]; message.sfixed32_field = vec![29i32, -30]; message.sfixed64_field = vec![30i64]; message.bool_field = vec![true, true]; message.string_field = vec!["thirty two".into(), "thirty three".into()]; message.bytes_field = vec![vec![33u8, 34].into(), vec![35u8].into()]; message.enum_field = vec![TestEnumDescriptor::BLUE, TestEnumDescriptor::GREEN]; test_serialize_deserialize_length_delimited!(&message, TestTypesRepeated); } #[test] fn test_types_repeated_packed() { let mut message = TestTypesRepeatedPacked::default(); message.double_field = vec![19f64, 20f64].into(); message.float_field = vec![20f32].into(); message.int32_field = vec![21i32, -22, 23]; message.int64_field = vec![22i64]; message.uint32_field = vec![23u32, 24]; message.uint64_field = vec![24u64]; message.sint32_field = vec![25i32]; message.sint64_field = vec![26i64, -27]; message.fixed32_field = vec![27u32].into(); message.fixed64_field = vec![28u64].into(); message.sfixed32_field = vec![29i32, -30].into(); message.sfixed64_field = vec![30i64].into(); message.bool_field = vec![true, true]; message.string_field = vec!["thirty two".into(), "thirty three".into()]; message.bytes_field = vec![vec![33u8, 34].into(), vec![35u8].into()]; message.enum_field = vec![TestEnumDescriptor::BLUE, TestEnumDescriptor::GREEN]; test_serialize_deserialize_length_delimited!(&message, TestTypesRepeatedPacked); } // #[test] // fn test_file_descriptor_proto() { // let p: &'static descriptor::FileDescriptorProto = file_descriptor_proto(); // assert!(p.has_name()); // assert_eq!("test_basic_pb.proto", p.get_name()); // } #[test] fn test_default_instance() { let d = TestDefaultInstance::default(); assert_eq!(None, d.field.and_then(|f| Some(f.s))); } // #[test] // fn test_message_descriptor() { // assert_eq!("TestDescriptor", TestDescriptor::new().descriptor().name()); // // let d = reflect::MessageDescriptor::for_type::(); // assert_eq!("TestDescriptor", d.name()); // assert_eq!("basic.TestDescriptor", d.full_name()); // // let mut t = TestDescriptor::new(); // t.set_stuff(55); // // let field = d.field_by_name("stuff"); // assert_eq!(55, field.get_i32(&t)); // } // // #[test] // fn test_enum_descriptor() { // let d = TestEnumDescriptor::RED.enum_descriptor(); // assert_eq!("TestEnumDescriptor", d.name()); // assert_eq!("TestEnumDescriptor", \ // reflect::EnumDescriptor::for_type::().name()); // assert_eq!("GREEN", d.value_by_name("GREEN").name()); // } // #[test] // fn test_invalid_tag() { // // 01 is invalid tag, because field number for that tag would be 0 // let bytes = decode_hex("01 02 03"); // let mut reader = BytesReader::from_bytes(&bytes); // assert!(TestInvalidTag::from_reader(&mut reader, &bytes).is_err()); // } #[test] fn test_truncated_no_varint() { // 08 is valid tag that should be followed by varint let bytes = decode_hex("08"); let mut reader = BytesReader::from_bytes(&bytes); assert!(TestTruncated::from_reader(&mut reader, &bytes).is_err()); } #[test] fn test_truncated_middle_of_varint() { // 08 is field 1, wire type varint // 96 is non-final byte of varint let bytes = decode_hex("08 96"); let mut reader = BytesReader::from_bytes(&bytes); assert!(TestTruncated::from_reader(&mut reader, &bytes).is_err()); } #[test] fn test_truncated_middle_of_length_delimited() { // 0a is field 1, wire type length delimited // 03 is length 3 let bytes = decode_hex("0a 03 10"); let mut reader = BytesReader::from_bytes(&bytes); assert!(TestTruncated::from_reader(&mut reader, &bytes).is_err()); } #[test] fn test_truncated_repeated_packed() { // 12 is field 2, wire type length delimited // 04 is length 4 let bytes = decode_hex("12 04 10 20"); let mut reader = BytesReader::from_bytes(&bytes); assert!(TestTruncated::from_reader(&mut reader, &bytes).is_err()); } #[test] fn test_bug_sint() { { let mut x = TestBugSint::default(); x.s32 = -1; test_serialize_deserialize!("08 01", &x, TestBugSint); } { let mut x = TestBugSint::default(); x.s64 = -2; test_serialize_deserialize!("10 03", &x, TestBugSint); } } quick-protobuf-0.8.1/tests/rust_protobuf/v3/test_basic_pb.proto000064400000000000000000000062301046102023000231020ustar 00000000000000syntax = "proto3"; package basic; message Test1 { int32 a = 1; } message Test2 { string b = 2; } message Test3 { Test1 c = 3; } message Test4 { repeated int32 d = 4 [packed=true]; } message TestPackedUnpacked { repeated int32 unpacked = 4 [packed=false]; repeated int32 packed = 5; } message TestEmpty { int32 foo = 10; } message Test{ bool b = 5; } message TestUnknownFields { int32 a = 1; } // just check it compiles message TestSelfReference { TestSelfReference r1 = 1; TestSelfReference r2 = 2; } message TestDefaultInstanceField { string s = 1; } message TestDefaultInstance { TestDefaultInstanceField field = 1; } message TestDescriptor { int32 stuff = 10; } enum TestEnumDescriptor { UNKNOWN = 0; RED = 1; BLUE = 2; GREEN = 3; } message TestTypesSingular { double double_field = 1; float float_field = 2; int32 int32_field = 3; int64 int64_field = 4; uint32 uint32_field = 5; uint64 uint64_field = 6; sint32 sint32_field = 7; sint64 sint64_field = 8; fixed32 fixed32_field = 9; fixed64 fixed64_field = 10; sfixed32 sfixed32_field = 11; sfixed64 sfixed64_field = 12; bool bool_field = 13; string string_field = 14; bytes bytes_field = 15; TestEnumDescriptor enum_field = 16; } message TestTypesRepeated { repeated double double_field = 1 [packed=false]; repeated float float_field = 2 [packed=false]; repeated int32 int32_field = 3 [packed=false]; repeated int64 int64_field = 4 [packed=false]; repeated uint32 uint32_field = 5 [packed=false]; repeated uint64 uint64_field = 6 [packed=false]; repeated sint32 sint32_field = 7 [packed=false]; repeated sint64 sint64_field = 8 [packed=false]; repeated fixed32 fixed32_field = 9 [packed=false]; repeated fixed64 fixed64_field = 10 [packed=false]; repeated sfixed32 sfixed32_field = 11 [packed=false]; repeated sfixed64 sfixed64_field = 12 [packed=false]; repeated bool bool_field = 13 [packed=false]; repeated string string_field = 14; repeated bytes bytes_field = 15; repeated TestEnumDescriptor enum_field = 16 [packed=false]; } message TestTypesRepeatedPacked { repeated double double_field = 1 [packed=true]; repeated float float_field = 2 [packed=true]; repeated int32 int32_field = 3 [packed=true]; repeated int64 int64_field = 4 [packed=true]; repeated uint32 uint32_field = 5 [packed=true]; repeated uint64 uint64_field = 6 [packed=true]; repeated sint32 sint32_field = 7 [packed=true]; repeated sint64 sint64_field = 8 [packed=true]; repeated fixed32 fixed32_field = 9 [packed=true]; repeated fixed64 fixed64_field = 10 [packed=true]; repeated sfixed32 sfixed32_field = 11 [packed=true]; repeated sfixed64 sfixed64_field = 12 [packed=true]; repeated bool bool_field = 13 [packed=true]; repeated string string_field = 14; repeated bytes bytes_field = 15; repeated TestEnumDescriptor enum_field = 16 [packed=true]; } message TestInvalidTag { } message TestTruncated { repeated fixed32 ints = 2 [packed=true]; } message TestBugSint { sint32 s32 = 1; sint64 s64 = 2; } quick-protobuf-0.8.1/tests/rust_protobuf/v3/test_comments.proto000064400000000000000000000017331046102023000231700ustar 00000000000000syntax = "proto3"; // more stress testing for comments; BOOMs inspired by nerdrew :) // BOOM /* BOOM */ //////////////////// /////////////////// //BOOM//BOOM/\/BOOM/BOOM/*BOOM//BOOM*/ /*//BOOM*/ /// //! //!!//!/!! /** * some documentation * * */ /* /// */ /***************** message X { } ******************/ /* message Y { this would be a syntax error if uncommented } */ /*4 5 6 7 8 9 */ // BOOM /* BOOM */ message A { /*BOOM BOOM BOOM*/ // BOOM enum E1 { // BOOM // BOOM V1 = 1; // BOOM v2 = 2; } // BOOM enum E2 { // BOOM } enum E3 { /* BOOM */ } // BOOM message B { // BOOM // BOOM optional string b = 1; } message C { // BOOM } message D { /* BOOM */ } required string a = 1; oneof Thing { int32 a = 1; // empty oneofs not allowed currently // // } } // no newline at end of filequick-protobuf-0.8.1/tests/rust_protobuf/v3/test_deprecated_lifetime.rs000064400000000000000000000014371046102023000246030ustar 00000000000000use super::test_deprecated_lifetime_can_compile::{ AndThisToo, HasLifetimeParameterIfAddDeprecatedFields, SameHere, }; #[test] fn test_owned() { let t = trybuild::TestCases::new(); t.compile_fail("tests/rust_protobuf/v3/test_deprecated_lifetime_must_compile_error.rs"); } #[test] fn test_add_deprecated_lifetime() { // As long as compilation succeeds, we know that these fields must have had // lifetimes autogenerated for them, which is expected behaviour since // `generate.sh` should call pb-rs with the `add-deprecated-fields` option // on `test_deprecated_lifetime_can_compile.proto` for use in this test struct Thing<'a> { field_1: HasLifetimeParameterIfAddDeprecatedFields<'a>, field_2: SameHere<'a>, field_3: AndThisToo<'a>, } } quick-protobuf-0.8.1/tests/rust_protobuf/v3/test_deprecated_lifetime_can_compile.proto000064400000000000000000000005551046102023000276530ustar 00000000000000syntax = "proto3"; message HasLifetimeParameterIfAddDeprecatedFields { optional string dep = 1 [deprecated=true]; } message SameHere { oneof ContainsDeprecated { string dep = 1 [deprecated=true]; } } message AndThisToo { optional MessageWithLifetime dep = 1 [deprecated=true]; } message MessageWithLifetime { optional string s = 1; }quick-protobuf-0.8.1/tests/rust_protobuf/v3/test_deprecated_lifetime_must_compile_error.rs000064400000000000000000000013701046102023000305700ustar 00000000000000// DO NOT link to module tree // This file is run remotely from test_deprecated_lifetime.rs and should fail to compile #[path = "./test_deprecated_lifetime_pb.rs"] pub mod proto; use crate::proto::{NorShouldThis, ThisShouldNotHaveALifetimeParameter, ThisShouldntEither}; // The structs that we set here as our fields should not have lifetimes // generated for their definitions; therefore, assigning lifetimes to these // fields should throw an error saying they don't have lifetimes, which we catch // using the `trybuild` crate in `test_deprecated_lifetime.rs`. struct Thing<'a> { throw_error_field_1: ThisShouldNotHaveALifetimeParameter<'a>, throw_error_field_2: ThisShouldntEither<'a>, throw_error_field_3: NorShouldThis<'a>, } fn main() {} quick-protobuf-0.8.1/tests/rust_protobuf/v3/test_deprecated_lifetime_must_compile_error.stderr000064400000000000000000000035001046102023000314440ustar 00000000000000error[E0107]: this struct takes 0 lifetime arguments but 1 lifetime argument was supplied --> tests/rust_protobuf/v3/test_deprecated_lifetime_must_compile_error.rs:13:26 | 13 | throw_error_field_1: ThisShouldNotHaveALifetimeParameter<'a>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---- help: remove these generics | | | expected 0 lifetime arguments | note: struct defined here, with 0 lifetime parameters --> tests/rust_protobuf/v3/./test_deprecated_lifetime_pb.rs | | pub struct ThisShouldNotHaveALifetimeParameter { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0107]: this struct takes 0 lifetime arguments but 1 lifetime argument was supplied --> tests/rust_protobuf/v3/test_deprecated_lifetime_must_compile_error.rs:14:26 | 14 | throw_error_field_2: ThisShouldntEither<'a>, | ^^^^^^^^^^^^^^^^^^---- help: remove these generics | | | expected 0 lifetime arguments | note: struct defined here, with 0 lifetime parameters --> tests/rust_protobuf/v3/./test_deprecated_lifetime_pb.rs | | pub struct ThisShouldntEither { | ^^^^^^^^^^^^^^^^^^ error[E0107]: this struct takes 0 lifetime arguments but 1 lifetime argument was supplied --> tests/rust_protobuf/v3/test_deprecated_lifetime_must_compile_error.rs:15:26 | 15 | throw_error_field_3: NorShouldThis<'a>, | ^^^^^^^^^^^^^---- help: remove these generics | | | expected 0 lifetime arguments | note: struct defined here, with 0 lifetime parameters --> tests/rust_protobuf/v3/./test_deprecated_lifetime_pb.rs | | pub struct NorShouldThis { | ^^^^^^^^^^^^^ quick-protobuf-0.8.1/tests/rust_protobuf/v3/test_deprecated_lifetime_pb.proto000064400000000000000000000005641046102023000260030ustar 00000000000000syntax = "proto3"; message ThisShouldNotHaveALifetimeParameter { optional string dep = 1 [deprecated=true]; } message ThisShouldntEither { oneof ContainsDeprecated { string dep = 1 [deprecated=true]; } } message NorShouldThis { optional MessageWithLifetime dep = 1 [deprecated=true]; } message MessageWithLifetime { optional string s = 1; }quick-protobuf-0.8.1/tests/rust_protobuf/v3/test_enum_alias_pb.proto000064400000000000000000000003301046102023000241310ustar 00000000000000syntax = "proto3"; package test_enum_alias; enum EnumWithAlias { option allow_alias = true; UNKNOWN = 0; A = 10; B = 20; A_AGAIN = 10; } message TestEnumWithAlias { EnumWithAlias en = 1; } quick-protobuf-0.8.1/tests/rust_protobuf/v3/test_enum_invalid_default.proto000064400000000000000000000003241046102023000255140ustar 00000000000000syntax = "proto2"; enum Enum1 { a = 0; b = 1; c = 2; } message Msg1 { optional Enum1 x = 9 [default = a]; // a field exists optional Enum1 y = 9 [default = does_not_exist]; // a field doesn't exist } quick-protobuf-0.8.1/tests/rust_protobuf/v3/test_ident_pb.proto000064400000000000000000000004031046102023000231200ustar 00000000000000syntax = "proto3"; message Vec { } message String { } message Option { } message None { } message Some { } message Message { } message TestType { oneof type { string s = 1; } repeated string struct = 2; repeated uint32 ref = 3; } quick-protobuf-0.8.1/tests/rust_protobuf/v3/test_map.rs000064400000000000000000000023641046102023000214020ustar 00000000000000use quick_protobuf::*; use super::test_map_pb::*; use crate::rust_protobuf::hex::{decode_hex, encode_hex}; #[test] fn test_map() { let mut map = TestMap::default(); let mut entry = TestMapEntry::default(); entry.v = 10; test_serialize_deserialize!("", &map, TestMap); map.m.insert("two".into(), 2); test_serialize_deserialize!("0a 07 0a 03 74 77 6f 10 02", &map, TestMap); map.m.insert("sixty six".into(), 66); // Insert map entry sub message map.mm.insert("map".into(), entry); // cannot (easily) test hex, because order is not specified test_serialize_deserialize_length_delimited!(&map, TestMap); } #[test] fn test_map_with_object() { let mut map = TestMap::default(); let mut entry = TestMapEntry::default(); entry.v = 10; test_serialize_deserialize!("", &map, TestMap); map.mm.insert("map".into(), entry); // cannot (easily) test hex, because order is not specified test_serialize_deserialize_length_delimited!(&map, TestMap); } // #[test] // fn text_format() { // let mut map = TestMap::new(); // // assert_eq!(&*print_to_string(&map), ""); // // map.mut_m().insert("two".to_owned(), 2); // // assert_eq!(&*print_to_string(&map), "m {key: \"two\" value: 2}") // } quick-protobuf-0.8.1/tests/rust_protobuf/v3/test_map_pb.proto000064400000000000000000000002261046102023000225750ustar 00000000000000syntax = "proto3"; message TestMap { map m = 1; map mm = 2; } message TestMapEntry { int64 v = 1; } quick-protobuf-0.8.1/tests/rust_protobuf/v3/test_oneof.rs000064400000000000000000000003501046102023000217240ustar 00000000000000// generated include!("../common/test_oneof.rs"); #[test] fn test_types_v3() { t(|o| { let mut msg = MessageForOneof::default(); msg.f = 22; o.one = mod_TestOneof::OneOfone::message_field(msg); }) } quick-protobuf-0.8.1/tests/rust_protobuf/v3/test_oneof_pb.proto000064400000000000000000000013661046102023000231340ustar 00000000000000syntax = "proto3"; enum EnumForOneof { Z = 0; A = 10; } message MessageForOneof { int32 f = 1; } message TestOneof { string s = 29; oneof one { double double_field = 1; float float_field = 2; int32 int32_field = 3; int64 int64_field = 4; uint32 uint32_field = 5; uint64 uint64_field = 6; sint32 sint32_field = 7; sint64 sint64_field = 8; fixed32 fixed32_field = 9; fixed64 fixed64_field = 10; sfixed32 sfixed32_field = 11; sfixed64 sfixed64_field = 12; bool bool_field = 13; string string_field = 14; bytes bytes_field = 15; EnumForOneof enum_field = 16; MessageForOneof message_field = 17; } } quick-protobuf-0.8.1/tests/rust_protobuf/v3/test_owned.rs000064400000000000000000000034221046102023000217350ustar 00000000000000use std::{ borrow::Cow, convert::{TryFrom, TryInto}, }; use quick_protobuf::{BytesReader, MessageRead}; use super::test_owned_pb::*; #[test] fn test_owned() { let t = trybuild::TestCases::new(); t.compile_fail("tests/rust_protobuf/v3/test_owned_must_compile_error.rs"); } #[test] fn test_owned_basic_functionality() { let encoded: Vec = vec![ 0x0au8, 0x07u8, 0x74u8, 0x65u8, 0x73u8, 0x74u8, 0x69u8, 0x6eu8, 0x67u8, ]; // s = "testing" let foo_owned = FooOwned::try_from(encoded.clone()).unwrap(); let foo_owned_temp = FooOwned::try_from(encoded.clone()).unwrap(); let mut foo_owned_temp_mut = FooOwned::try_from(encoded.clone()).unwrap(); let reencoded: Vec = foo_owned_temp.try_into().unwrap(); let mut br_temp = BytesReader::from_bytes(&encoded); let foo = Foo::from_reader(&mut br_temp, &encoded).unwrap(); let buf = foo_owned.buf().clone(); let proto = foo_owned.proto().clone(); let proto_mut = foo_owned_temp_mut.proto_mut(); proto_mut.s = Some(Cow::from("the secret cow level")); let static_foo = Foo { s: Some(Cow::from("the moo moo farm")), }; let from_static_foo = FooOwned::from(static_foo); assert_eq!(Some(Cow::from("testing")), foo_owned.proto().s); // test decoding and `proto()` assert_eq!(encoded, buf); // test `buf()` assert_eq!(foo, proto); // test `proto()` assert_eq!( foo_owned_temp_mut.proto().s, Some(Cow::from("the secret cow level")) ); // test `proto_mut()` assert_eq!(format!("{:?}", foo_owned), format!("{:?}", foo)); // test `try_from()` and `Debug` assert_eq!(encoded, reencoded); // test `try_into()` assert_eq!( Some(Cow::from("the moo moo farm")), from_static_foo.proto().s ); // test `from()` } quick-protobuf-0.8.1/tests/rust_protobuf/v3/test_owned_must_compile_error.rs000064400000000000000000000007631046102023000257330ustar 00000000000000// DO NOT link to module tree // This file is run remotely from test_owned.rs and should fail to compile #[path = "./test_owned_pb.rs"] pub mod proto; fn main() { use std::convert::TryFrom; let encoded: Vec = vec![ 0x0au8, 0x07u8, 0x74u8, 0x65u8, 0x73u8, 0x74u8, 0x69u8, 0x6eu8, 0x67u8, ]; // s = "testing" let proto = proto::FooOwned::try_from(encoded).unwrap(); let owned_copy = proto.proto().s.to_owned(); drop(proto); println!("{:?}", owned_copy); } quick-protobuf-0.8.1/tests/rust_protobuf/v3/test_owned_must_compile_error.stderr000064400000000000000000000006671046102023000266150ustar 00000000000000error[E0505]: cannot move out of `proto` because it is borrowed --> tests/rust_protobuf/v3/test_owned_must_compile_error.rs:17:10 | 15 | let owned_copy = proto.proto().s.to_owned(); | ------------- borrow of `proto` occurs here 16 | 17 | drop(proto); | ^^^^^ move out of `proto` occurs here 18 | println!("{:?}", owned_copy); | ---------- borrow later used here quick-protobuf-0.8.1/tests/rust_protobuf/v3/test_owned_pb.proto000064400000000000000000000000531046102023000231320ustar 00000000000000message Foo { optional string s = 1; } quick-protobuf-0.8.1/tests/rust_protobuf/v3/test_zeros_are_not_written.rs000064400000000000000000000005231046102023000252450ustar 00000000000000use quick_protobuf::*; use crate::rust_protobuf::hex::{encode_hex, decode_hex}; use super::basic::*; #[test] fn test_zeros_are_not_written() { let mut m = TestTypesSingular::default(); m.bool_field = Some(false); m.enum_field = Some(TestEnumDescriptor::UNKNOWN); m.fixed32_field = Some(0); test_serialize!("", &m); } quick-protobuf-0.8.1/tests/write_read.rs000064400000000000000000000230621046102023000164440ustar 00000000000000extern crate quick_protobuf; use quick_protobuf::sizeofs::*; use quick_protobuf::{deserialize_from_slice, serialize_into_slice, serialize_into_vec}; use quick_protobuf::{ BytesReader, MessageRead, MessageWrite, Reader, Result, Writer, WriterBackend, }; use std::borrow::Cow; use std::collections::HashMap; macro_rules! write_read_primitive { ($name:ident, $read:ident, $write:ident) => { write_read_primitive!($name, $read, $write, 145); }; ($name:ident, $read:ident, $write:ident, $def:expr) => { #[test] fn $name() { let v = $def; let mut buf = Vec::new(); { let mut w = Writer::new(&mut buf); w.$write(v).unwrap(); } let mut r = BytesReader::from_bytes(&*buf); assert_eq!(v, r.$read(&buf).unwrap()); } }; } write_read_primitive!(wr_u8, read_u8, write_u8); write_read_primitive!(wr_int32, read_int32, write_int32); write_read_primitive!(wr_int64, read_int64, write_int64); write_read_primitive!(wr_uint32, read_uint32, write_uint32); write_read_primitive!(wr_uint64, read_uint64, write_uint64); write_read_primitive!(wr_sint32, read_sint32, write_sint32); write_read_primitive!(wr_sint64, read_sint64, write_sint64); write_read_primitive!(wr_bool, read_bool, write_bool, true); write_read_primitive!(wr_fixed32, read_fixed32, write_fixed32); write_read_primitive!(wr_fixed64, read_fixed64, write_fixed64); write_read_primitive!(wr_sfixed32, read_sfixed32, write_sfixed32); write_read_primitive!(wr_sfixed64, read_sfixed64, write_sfixed64); write_read_primitive!(wr_float, read_float, write_float, 5.8); write_read_primitive!(wr_double, read_double, write_double, 5.8); #[test] fn wr_bytes() { let v = b"test_write_read"; let mut buf = Vec::new(); { let mut w = Writer::new(&mut buf); w.write_bytes(v).unwrap(); } let mut r = BytesReader::from_bytes(&*buf); assert_eq!(v, r.read_bytes(&buf).unwrap()); } #[test] fn wr_string() { let v = "test_write_read"; let mut buf = Vec::new(); { let mut w = Writer::new(&mut buf); w.write_string(v).unwrap(); } let mut r = BytesReader::from_bytes(&buf); assert_eq!(v, r.read_string(&buf).unwrap()); } #[derive(PartialEq, Eq, Debug, Clone, Copy)] enum TestEnum { A = 0, B = 1, C = 2, } impl From for TestEnum { fn from(v: i32) -> TestEnum { match v { 0 => TestEnum::A, 1 => TestEnum::B, 2 => TestEnum::C, _ => unreachable!(), } } } #[test] fn wr_enum() { let v = TestEnum::C; let mut buf = Vec::new(); { let mut w = Writer::new(&mut buf); w.write_enum(v as i32).unwrap(); } let mut r = BytesReader::from_bytes(&buf); assert_eq!(v, r.read_enum(&buf).unwrap()); } #[derive(PartialEq, Eq, Debug, Clone, Default)] struct TestMessage { id: Option, val: Vec, } impl<'a> MessageRead<'a> for TestMessage { fn from_reader(r: &mut BytesReader, bytes: &[u8]) -> Result { let mut msg = TestMessage::default(); while !r.is_eof() { match r.next_tag(bytes) { Ok(10) => msg.id = Some(r.read_uint32(bytes)?), Ok(18) => msg.val.push(r.read_sint64(bytes)?), Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } } Ok(msg) } } impl MessageWrite for TestMessage { fn get_size(&self) -> usize { self.id.as_ref().map_or(0, |m| 1 + sizeof_uint32(*m)) + self .val .iter() .map(|m| 1 + sizeof_sint64(*m)) .sum::() } fn write_message(&self, r: &mut Writer) -> Result<()> { if let Some(ref s) = self.id { r.write_with_tag(10, |r| r.write_uint32(*s))?; } for s in &self.val { r.write_with_tag(18, |r| r.write_sint64(*s))?; } Ok(()) } } #[test] fn wr_message() { let v = TestMessage { id: Some(63), val: vec![53, 5, 76, 743, 23, 753], }; let buf = serialize_into_vec(&v).unwrap(); assert_eq!(v, deserialize_from_slice(&buf).unwrap()); // test get_size! assert_eq!(buf.len(), sizeof_varint(8) + v.get_size()); } #[test] fn wr_message_slice() { let v = TestMessage { id: Some(63), val: vec![53, 5, 76, 743, 23, 753], }; let mut buf = [0u8; 1024]; serialize_into_slice(&v, &mut buf).unwrap(); assert_eq!(v, deserialize_from_slice(&buf).unwrap()); } #[derive(PartialEq, Eq, Debug, Clone, Default)] struct TestMessageBorrow<'a> { id: Option, val: Vec<&'a str>, } impl<'a> MessageRead<'a> for TestMessageBorrow<'a> { fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result> { let mut msg = TestMessageBorrow::default(); while !r.is_eof() { match r.next_tag(bytes) { Ok(10) => msg.id = Some(r.read_uint32(bytes)?), Ok(18) => msg.val.push(r.read_string(bytes)?), Ok(t) => { r.read_unknown(bytes, t)?; } Err(e) => return Err(e), } println!("{:?}", msg); } Ok(msg) } } impl<'a> MessageWrite for TestMessageBorrow<'a> { fn get_size(&self) -> usize { self.id.as_ref().map_or(0, |m| 1 + sizeof_uint32(*m)) + self .val .iter() .map(|m| 1 + sizeof_len(m.len())) .sum::() } fn write_message(&self, r: &mut Writer) -> Result<()> { if let Some(ref s) = self.id { r.write_with_tag(10, |r| r.write_uint32(*s))?; } for s in &self.val { r.write_with_tag(18, |r| r.write_string(*s))?; } Ok(()) } } #[test] fn wr_message_length_prefixed() { let test = "eajhawbdkjblncljbdskjbclas"; let v = TestMessageBorrow { id: Some(63), val: vec![&test[0..2], &test[3..7], &test[7..10]], }; let buf = serialize_into_vec(&v).unwrap(); assert_eq!(v, deserialize_from_slice(&buf).unwrap()); // test get_size! assert_eq!(buf.len(), sizeof_varint(8) + v.get_size()); } #[test] fn wr_message_wo_prefix() { let test = "eajhawbdkjblncljbdskjbclas"; let v = TestMessageBorrow { id: Some(63), val: vec![&test[0..2], &test[3..7], &test[7..10]], }; let mut buf = Vec::new(); { let mut writer = Writer::new(&mut buf); v.write_message(&mut writer).unwrap(); } let mut r = BytesReader::from_bytes(&buf); assert_eq!(v, TestMessageBorrow::from_reader(&mut r, &buf).unwrap()); // test get_size! assert_eq!(buf.len(), v.get_size()); } #[test] fn wr_message_with_prefix_wrapper() { let test = "eajhawbdkjblncljbdskjbclas"; let v = TestMessageBorrow { id: Some(63), val: vec![&test[0..2], &test[3..7], &test[7..10]], }; let mut buf = Vec::new(); { let mut writer = Writer::new(&mut buf); writer.write_message(&v).unwrap(); } let mut r = Reader::from_bytes(buf); assert_eq!( v, r.read(|r, b| r.read_message::(b)) .unwrap() ); // test get_size! assert_eq!(r.buffer().len(), sizeof_varint(8) + v.get_size()); } #[test] fn wr_message_wo_prefix_wrapper() { let test = "eajhawbdkjblncljbdskjbclas"; let v = TestMessageBorrow { id: Some(63), val: vec![&test[0..2], &test[3..7], &test[7..10]], }; let mut buf = Vec::new(); { let mut writer = Writer::new(&mut buf); v.write_message(&mut writer).unwrap(); } let mut r = Reader::from_bytes(buf); assert_eq!(v, r.read(TestMessageBorrow::from_reader).unwrap()); // test get_size! assert_eq!(r.buffer().len(), v.get_size()); } #[test] fn wr_packed_uint32() { let v = vec![43, 54, 64, 234, 6123, 643]; let mut buf = Vec::new(); { let mut w = Writer::new(&mut buf); w.write_packed(&v, |r, m| r.write_uint32(*m), &|m| sizeof_uint32(*m)) .unwrap(); } let mut r = BytesReader::from_bytes(&buf); assert_eq!(v, r.read_packed(&buf, |r, b| r.read_uint32(b)).unwrap()); } #[test] fn wr_packed_float() { let v = vec![43, 54, 64, 234, 6123, 643]; let mut buf = Vec::new(); { let mut w = Writer::new(&mut buf); w.write_packed_fixed(&v).unwrap(); } let mut r = BytesReader::from_bytes(&buf); assert_eq!(v, r.read_packed_fixed(&buf).unwrap()); } #[test] fn wr_map() { let v = { let mut v = HashMap::new(); v.insert(Cow::Borrowed("foo"), 1i32); v.insert(Cow::Borrowed("bar"), 2); v }; let mut buf = Vec::new(); { let mut w = Writer::new(&mut buf); for (k, v) in v.iter() { w.write_map( 2 + sizeof_len(k.len()) + sizeof_varint(*v as u64), 10, |w| w.write_string(&**k), 16, |w| w.write_int32(*v), ) .unwrap(); } } let mut r = BytesReader::from_bytes(&buf); let mut read_back = HashMap::new(); while !r.is_eof() { let (key, value) = r .read_map( &buf, |r, bytes| r.read_string(bytes).map(Cow::Borrowed), |r, bytes| r.read_int32(bytes), ) .unwrap(); read_back.insert(key, value); } assert_eq!(v, read_back); }