mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
Squashed 'src/beast/' changes from 6d5547a..3bcd986
3bcd986 Set version to 79 4f42f8c Remove spurious fallthrough guidance fa1ac16 Set version to 78 0cb9b63 Fix warning in root ca declaration f78c73a Tidy up file_posix unused variable 72ce9ef Tidy up dstream for existing Boost versions efe8e58 Add Boost.Locale utf8 benchmark comparison f7c745e Remove string_view_body 7a79efa Tidy up FieldsReader doc e51aefd Header file tidying 69898f4 Fix warning in zlib 53723c0 Add message keep_alive, chunked, content_length members d7af73b Fix spurious uninitialized warning ca42cc0 Tidy up invalid characters in test vector 48d3e60 Use make_unique_noinit a1ff804 span, string, vector bodies are public feab6a0 Documentation work 116c0b0 Add span_body 7fb901d Tidy up includes and javadocs f0f58be Add span 7a8982b Add vector_body 524f73a Tidy up basic_string_body a8ad67b Set version to 77 d555859 file_posix works without large file support 1bc30cb Set version to 76 9a1e7a8 Disable SSE4.2 optimizations 09af312 Fix parse illegal characters in obs-fold 7dd684c Add file_body_win32: 1bbc71c serializing file_body is not const 9a4b55e BodyReader, BodyWriter use two-phase init (API Change): dc400ce Serializer members are not const 1a33c37 Rename to serializer::keep_alive (API Change): 55935c5 Add serializer::chunked 63ace37 Add serializer::get 2c4047b BodyReader may construct from a non-const message 0a0a225 Use Boost.Config 6f83d07 Always go through write_some: 0e23066 Set version to 75 28f3ece Doc tidying 3495331 Using SSE4.2 intrinsics in basic_parser if available bc1f0ac file_body tests 4e03d7e Add serializer::limit 85e3ee8 Shrink serializer buffers using buffers_ref 78bcdb1 Tidy up BEAST_NO_BIG_VARIANTS 3ea6cf2 Construct buffer_prefix_view in-place 69f9f7a Use file_body for valid requests, string_body otherwise. 6f88f01 Set version to 74 93fed8e remove redundant flush() from example: e0f56da Fix Beast include directories for cmake targets 5ff9e0a Add file_posix 1bb5705 Remove common/file_body.hpp 5c89d87 Add file_body 67a55c8 Add file_win32 647d3b0 Add file_stdio and File concept 89c416c Set version to 73 0efc32f Fixes for gcc-4.8 c8910ab Initialize local variable in basic_parser 8a28193 Adjust benchmarks 81e51d8 Verify certificates in SSL clients a43f6d4 Jamroot tweak 8c85ee8 Put more... links on overload reference pages ff1104e Documentation tidy 826ff0e serializer::next replaces serializer::get (API Change): 8d67775 Refactor header and message constructors: 6c79f19 Add basic_parser tests 25127d9 basic_parser optimizations: 9d082fd Set version to 72 c88e2b9 Various improvements to http_server_fast.cpp: 20b0fdb Documentation tidying afd1fa7 Add websocket-server-async example 954b597 Add http-server-threaded example df8f253 Refactor file_body for best practices 11c1037 Newly constructed responses have a 200 OK result a648817 Refine Body::size specification 40aad37 Tidy up set payload in http-server-fast 52cefbc Set version to 71 8c51c77 Tidy up Jamroot /permissive- 5efecea Update README.md 5a47acd Tidy up http_sync_port error check a2af2b5 Concept check in basic_dynamic_body 8b80a6f Fix buffer overflow handling for string_body and mutable_body ec3b4fd Return std::size_t from Body::writer::put (API Change) effbb37 Check trailers in test f5368cf Call prepare_payload in HTTP example a3e5e01 Fix spurious on_chunk invocation 96d94eb Add options for building examples and tests. Move zlib test sources to test/zlib e0efdc7 Allow close, ping, and write to happen concurrently 9c1a419 Refactor websocket composed ops d5659a4 Fine tune websocket asserts b8e8943 std::pair "last" -> "first" in http_message.qbk c691bf4 Fix can/cannot thinko in FAQ. 6dd006b Documentation revision 6d2e315 Fix extra ; warning 78a065b Set version to 70 00c7e9d Fix HEAD response in file_service 67d70d2 Fix BEAST_FALLTHROUGH in config 4f33655 Add parser::on_header to set a callback 9c16b21 Add basic_parser header and body limits: b64d1f7 Rename to message::base (API Change): 436c66a Serialize in one step when possible 3e1061b Set version to 69 f709273 Add /permissive- to msvc toolchain 0dae464 Use BEAST_FALLTHROUGH to silence warnings a70d386 basic_parser optimizations 4269f35 Set version to 68 544327f Link statically on cmake MSVC e213ffe Add const_body, mutable_body to examples 0568763 Optimize field lookups 8fc3001 Use string_ref in older Boost versions 8af77da bad_target replaces bad_path (API Change): 325dd62 Adjust buffer size in fast server be59785 Doc erratum d9b44f3 Small speed up in fields comparisons 3e6ce38 Use Boost master on Appveyor 09f3d64 Split common tests to a new project adfd22a Remove BodyReader::is_deferred (API Change): 582d28d Change BodyReader, BodyWriter requirements (API Change): 8982e14 Set version to 67 daa58a2 Group common example headers afd8f1a Rename to http-server-fast 07cb9f7 control_callback replaces ping_callback (API Change): 91e83ed Use boost::string_view 067db87 Merge stream_base to stream and tidy d61241a Add http-server-small example eb08e92 Fix doc example link 7fb75d0 Set version to 66 df86723 Respect debug flag for marked output c08565a Squelch spurious warning on gcc 188ef7c Documentation work 1c62d3a Add http-server example 3f54582 basic_fields optimizations a8b05b8 Add header aliases b94eac3 Tidy up message piecewise ctors 9c48b52 Handle bad_alloc in parser 1b57c54 Fix costly potential value-init in parser 1edc41e Make consuming_buffers smaller 72ac918 Add serializer request/response aliases 18f7606 string_param optimizations c675252 Set version to 65 c398cdd Enable msvc warnings in Jamfile 380cceb Fix unused variable warnings 4172e7e Enable unused variable warning on msvc cmake f04d227 Fix integer warnings ca975b3 Fix narrowing in deflate_stream 2fab796 Fix narrowing in inflate_stream cff87f6 Fix narrowing in ostream 1956886 Fix narrowing in static_ostream 69cdc4b Fix integer types in deflate_stream::bi_reverse 3e3dd21 Enable narrowing warning on msvc cmake e11a294 Set version to 64 a00e070 Remove make_serializer (API Change): 8449697 Add link_directories to cmake 7b2b066 Doc tidying 158d3e8 async_write requires a non-const message: d13328b Better User-Agent in examples ebcb2c0 Exemplars are compiled code b9054d3 Simplify websocket write_op c2571fe Simplify ssl teardown composed op d8ad3d1 Simplify buffered_read_stream composed op f68dc34 Set version to 63 a99f7ef Control running with valgrind explicitly 4eb7af4 Tidy up Jamfiles 49b42a5 Tidy up CMakeLists.txt dadb54f Only run the tests under ubasan d1c7696 Move benchmarks to a separate project a4aada8 Only build and run tests in variant=coverage f835b9a Don't use cached Boost a0edd82 Put num_jobs back up on Travis a8d5823 Use std::to_string instead of lexical_cast 45d8b81 Set version to 62 09e07ce Put slow tests back for coverage builds 295b1d7 Doc tidy f58425c Squelch harmless not_connected errors 9b537f7 Add http::is_fields trait d43701b message::prepare_payload replaces message::prepare (API Change): 42ba289 Refine FieldsReader concept (API Change) bde90a1 Narrow the use of Fields parameters: 5f47526 parser requires basic_fields (API Change): 60f58e4 Avoid explicit operator bool for error 352f8de Clear the error faster 34befd8 Tidy up namespaces in examples 9e0b4b5 Doc fixes and tidy c003a2a Tidy up test build scripts and projects b929130 Add server-framework tests 03d4301 Increase detail::static_ostream coverage 80d7cbc Remove libssl-dev from a Travis matrix item 4c15db4 Set version to 61 1dfbd0b Don't run slow tests on certain targets 6bb1109 Use one job less on CI 6f58342 Tidy up resolver calls 530b044 Add multi_port to server-framework bfef5d1 Tidy up http-crawl example e2f2f33 Reorganize SSL examples adc301b Fix shadowing warnings c60185e Add server-framework SSL HTTP and WebSocket ports 7912fb8 Refactor WebSocket, HTTP examples: cd4b9e0 Flush the output stream in the example d046b20 Tidy up names in error categories 9d4a422 status-codes is unsigned (API Change) e3599b0 header::version is unsigned (API Change) a26b043 Add message::header_part() fc8d2e9 Tidy up some integer conversion warnings c91732e Reorganize SSL examples 9907b31 Documentation work 4b2e78e Use generic_cateogry for errno 38c46cd Remove Spirit dependency c111d6f Set version to 60 d78dc12 Documentation work 141a524 New server-framework, full featured server example: 3f7ffd9 Fix response message type in async websocket accept 13f3750 String comparisons are public interfaces 4e4bcf8 Set version to 59 5015cdb Remove obsolete doc/README.md 71c3f0a Fix base64 alphabet aa2b843 Change Body::size signature (API Change): 80a599a Documentation work 9c19449 Integrated Beast interface. 3f8097b Set version to 58 4581777 Better message formal parameter names 5879cd8 Fix parsing chunk size with leading zeroes 56bd228 Remove redundant code 534ca63 Use static string in basic_fields::reader a7b3810 basic_parser::put doc 1e4413f basic_fields::set optimization: 9b244c1 Fix basic_fields insert ordering 4f854d0 Avoid std::string in websocket dc8f146 Renamed to basic_fields::set (API Change): 660c465 Specification for http read 981285b Documentation tidy 983d676 Reorganize examples: a2a5c57 Qualify size_t in message template d86769c Fix unaligned reads in utf8-checker 8ba182c Set version to 57 42e2791 Update doc/ for docca 1ee0afd Merge commit '101d7dbfb9725674cb9ce5a4196f19aa1d4bb801' as 'doc/docca' 101d7db Squashed 'doc/docca/' content from commit c50b3ba5 900c04e Documentation work 8eee932 Fix warning in basic_parser.cpp 437a616 Fix message.hpp javadocs 18c68ce Set version to 56 b058e90 Convert buffer in range loops cbc9212 Add Beast INTERFACE CMake target 2914b59 More basic_parser tests ed5c317 Reset error codes ba14251 Test error code handling e45e50b Tidy up README.md build instructions 16efb9b Try harder to find Boost (cmake) e281d91 HTTP/1 is the default version 916fe4a Call on_chunk when the extension is empty 9855598 Add string_view_body 19d4520 Tidy up 6e59f9b Add provisional IANA header field names 84722f2 Revert "Add a Beast CMake interface target:" fde6929 Set version to 55 01f6cc4 Documentation work a7a388c read_size replaces read_size_helper: ed8f0bb Tidy up type_traits c2f6268 Avoid a parser allocation using non-flat buffer: 906db45 Add a Beast CMake interface target: 47f2541 Don't allocate memory to handle obs-fold 6a8912a Set version to 54 296ef3b Documentation work e3e9b61 Fix incorrect use of [[fallthrough]] 3c44398 Retain ownership when reading using a message a71bb2b basic_fields refactor (API Change): d8d3562 Add string_param 83b2558 basic_fields members and coverage c4f5fa5 consuming_buffers members and coverage e10507c multi_buffer members and coverage 0e6bd3f flat_buffer coverage 7351d6e static_buffer coverage 18a8ef5 Set version to 53 452df59 Remove extraneous doc file 3ef0359 Fix read_size_helper usage: b64e6d3 Fix basic_parser::maybe_flatten (#462) 76402f7 Set version to 52 b0ceb2a Add drain_buffer class 4c6735a flat_buffer is an AllocatorAwareContainer 9d5d4d5 Documentation work d4ec693 finish(error_code&) is a BodyReader requirement (API Change) 7b24cad opcode is private (API Change): 068c2ac Documentation work a1ff89b Disable std::future snippet for libstdc++ bug b5ef664 read_frame returns `bool` fin (API Change): 7911847 Remove opcode from read, async_read (API Change): c72d70f ping_callback is a member of stream (API Change): 720a309 write_buffer_size is a member of stream (API Change): 7ff0178 read_message_max is a member of stream (API Change): cd40964 read_buffer_size is a member of stream (API Change): a58e5e1 binary, text are members of stream (API Change): ad35846 auto_fragment is a member of stream (API Change): ccee139 Documentation work 13c64e3 Set version to 51 cafc8e2 Fix infinite loop in basic_parser dc4b69d Add construct, destroy to handler_alloc 58c2739 multi_buffer implementation change (API Change): dd7f5c0 DynamicBuffer benchmarks 1c4811b Use BOOST_STRINGIZE 31051ac Use BOOST_FALLTHROUGH 8f2430b Documentation work eb35b92 Fix file_body::get() not setting the more flag correctly 566244a Tidy up file_body 53cbeea Tune up static_buffer (API Change): 20c59b7 Fix operator<< for header a2c1117 Set version to 50 6045b74 http read_some, async_read_some don't return bytes (API Change): 4df6885 Fix chunk header parsing 36bf32b Fix test::pipe read_size bf69ce1 Fix chunk delimiter parsing 0c6b6b1 Add missing handler_alloc nested types a06b8f9 Tidy up message javadoc 3bd8260 Remove obsolete serializer allocator (API Change) 001c979 Remove message free functions (API Change) 745876b Remove message connection settings (API Change) bcf2c33 Body documentation work 1e303b0 Fields concept work 9d0464a Tidy up basic_fields, header, and concepts 3ba81b5 Use field in basic_parser b5f6cc1 Use field in basic_fields and call sites cfd6d14 Documentation reference tidy 2adc80a Protect basic_fields special members (API Change) d55b079 Fix basic_fields allocator awareness d8febda Documentation work 485a6e5 Refactor prepare (API Change) 9fd3071 Derive header from Fields (API Change) 8ad26b8 Use allocator more in basic_fields 0071039 Add verb to on_request for parsers (API Change) 74f6cbb Add field enumeration be0d74f Documentation fixes 054ac40 Remove header_parser (API Change) a007eba parser is constructible from other body types d89809f Documentation work (buffer_body) ac5bc4f Set version to 49 af47128 Documentation work a1848f0 Add HEAD request example ddfbfbf Use <iosfwd> instead of <ostream> e67c0ab Refactor header status, reason, and target (API Change): 60f044a Tidy up empty_body writer error 7d267f4 Canonicalize string_view parameter types ac175cb Refactor method and verb (API Change): e18efed Documentation work d77e423 Set version to 48 d3a5a05 Documentation work acf18fb Tidy up traits 6cb188e Remove detail::sync_ostream d6092bc Documentation work 4707b21 Rename to parser (API Change): 3cb385d Consolidate parsers to parser.hpp 290bdf1 Documentation work 7cb442c Make buffer_prefix_view public ef0b121 Rename to buffer_cat_view (API Change) b9df187 Tidy up chunk decorator (API Change): 458fa7e Set version to 47 fc83a03 Documentation work 1ee3013 Fix leak in basic_flat_buffer 55fbf76 Fix undefined behavior in pausation fe75a7c Refactor HTTP serialization and parsing (API Change): 50cba32 buffer_size overload for basic_multi_buffer::const_buffers_type d977bf2 Disable operator<< for buffer_body 5db707a Refactor treatment of status code and obsolete reason (API Change): 9a585a8 Refactor treatment of request-method (API Change): 3ae76d0 Set version to 46 6004712 Documentation work 34ea0b3 Refactor serialization algorithms: 407b046 Rename to make_serializer c29451a Refactor type_traits (API Change): 8578419 Refactor HTTP serialization (API Change): f8612aa Remove HTTP header aliases (API Change): b0054e3 Add test::pipe dfba72b Set version to 45 6ba3697 Disable reverse_iterator buffer_view test 266ebac buffer_view skips empty buffer sequences 96b9892 Documentation work c23f1e2 Fix header::reason 9796106 Better test::enable_yield_to 9a8bcb7 Fix message doc image 7a5e87e Workaround for boost::asio::basic_streambuf type check 663c275 Set version to 44 f205976 Make buffers_adapter meet requirements 8e39c60 Tidy up is_dynamic_buffer traits test 0088f7c Add buffers_adapter regression test 8a23de1 Fix README websocket example 949504a Fix async return values in docs cd9f41b Use BOOST_STATIC_ASSERT 1b616fa Tidy up and make get_lowest_layer public 612e616 Require Boost 1.58 or later 1b1daa7 Tidy up read_size_helper and dynamic buffers bf0145d Use BOOST_THROW_EXCEPTION e762818 Add GitHub issue template dab679c Set version to 43 386b817 Reformat README.md QR code 50e5123 Additional constructors for consuming_buffers f7289b9 Add write limit to test::string_ostream 3aa87e0 Tidy up buffer_prefix overloads and test bee583c Fix strict aliasing warnings in buffers_view 6b54d3a Require Boost 1.64.0 76f1084 Set version to 42 0bdb148 Make buffers_view a public interface 338fc81 Add formal review notes 784f965 Fix javadoc typo 823aee2 Set version to v41 88adbdd Remove handler helpers, tidy up hook invocations (API Change) 4974af2 Rename prepare_buffer(s) to buffer_prefix (API Change) ebd459b Tidy up websocket::close_code enum and constructors c3fd6f9 Tidy up formal parameter names 210cd70 Remove coveralls integration d811962 Concept revision and documentation (API Change): bdae92a Replace asynchronous helper macros with template aliases (API Change) df95a09 Move prepare_buffers to prepare_buffer.hpp (API Change) 787de21 Remove placeholders (API Change) c59b544 Trim Appveyor matrix rows b7184f3 Return http::error::end_of_stream on HTTP read eof (API Change) f2d8255 Set version to 40 40b9194 Tidy up .travis.yml: 9b240c7 Fix basic_streambuf movable trait 76a2617 Consolidate get_lowest_layer in type_traits.hpp 6d00321 Add to_static_string: f888136 Set version to 39 47c82b5 Better travis deps 4ed7865 Squelch openssl spurious leak and memory errors b6bc26f Fixed braced-init error with older gcc 59b2f8f ostream workaround for gcc 4.8.4 8363d86 Increase ostream test coverage 5631936 Tidy up HTTP reason_string (API Change): 2bf5150 Harmonize concepts and identifiers with net-ts (API Change): 728e9d8 Tidy up basic_parser javadocs 1c9067b Use beast::string_view alias 771c5ca Doc fixes and tidying e2b5c31 Rename to buffered_read_stream (API Change): a753f1c Rename to static_buffer, static_buffer_n (API Change): 24b6686 Rename to flat_buffer, basic_flat_buffer (API Change): 69259ef Rename to multi_buffer, basic_multi_buffer (API Change): bef9ae1 New buffers() replaces to_string() (API Change): a7ef4f5 New ostream() returns dynamic buffer output stream (API Change): 87fd60c Fix eof error on ssl::stream shutdown 606fc9d Add websocket async echo ssl server test: ff5e659 Refactor http::header contents (API Change): dd02097 Set version to 1.0.0-b38 5596e97 Prevent basic_fields operator[] assignment c2b32dc Remove websocket::keep_alive option (API Change): 32dbfb2 Refactor WebSocket error codes (API Change): dd6b500 WebSocket doc work 0b4d87c More flat_streambuf tests aacefb4 Add test_allocator to extras/test 931a5fb Simplify get_lowest_layer test ba4228a Use static_string for WebSocket handshakes: 6df3ff3 Refactor base64: 19b124d Refactor static_string: 30e8d16 Set version to 1.0.0-b37 b141020 Fix narrowing warning in table constants d554b81 Add -funsigned-char to asan build target bcc6ad8 Add ub sanitizer blacklist e1f08e9 Fix flat_streambuf: 7d08f59 Fix typo in documentation example 21ef97d Rename to http::dynamic_body, consolidate header: 45a2d73 Rename project to http-bench c86fee9 Move everything in basic_fields.hpp to fields.hpp (API Change) a14a5d6 Rename to detail::is_invocable 540d037 Rename to websocket::detail::pausation 84e1739 Document websocket::stream thread safety dc274af Add is_upgrade() free function: 2c17d04 Refactor websocket decorators (API Change): 235fe68 Provide websocket::stream accept() overloads (API Change): a715825 CMake hide command lines in .vcxproj Output windows 32024d8 Set version to 1.0.0-b36 f48b95f Update README.md d8db5f1 Set version to 1.0.0-b35 dd2a514 Tidy up doc declarations 2c50aba Fix README.md CMake instructions 4ffdce2 Update .gitignore for VS2017 403011f Remove http::empty_body (API Change) f47b661 New HTTP interfaces (API Change): f6835b8 Rename to BEAST_DOXYGEN 7e37723 Add flat_streambuf: 5b68faa Doc XSL support for list and table markdown 3de46de Make websocket::close_code a proper enum: 0128743 Tidy up MSVC CMake configuration ccd188e Add appveyor build script git-subtree-dir: src/beast git-subtree-split: 3bcd9865f80f12ba5faad35c564918f85b02e271
This commit is contained in:
19
include/beast.hpp
Normal file
19
include/beast.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HPP
|
||||
#define BEAST_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
|
||||
#include <beast/core.hpp>
|
||||
#include <beast/http.hpp>
|
||||
#include <beast/version.hpp>
|
||||
#include <beast/websocket.hpp>
|
||||
#include <beast/zlib.hpp>
|
||||
|
||||
#endif
|
||||
@@ -8,17 +8,23 @@
|
||||
#ifndef BEAST_CONFIG_HPP
|
||||
#define BEAST_CONFIG_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
// Available to every header
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
#include <boost/static_assert.hpp>
|
||||
|
||||
/*
|
||||
_MSC_VER and _MSC_FULL_VER by version:
|
||||
|
||||
14.0 (2015) 1900 190023026
|
||||
14.0 (2015 Update 1) 1900 190023506
|
||||
14.0 (2015 Update 2) 1900 190023918
|
||||
14.0 (2015 Update 3) 1900 190024210
|
||||
14.0 (2015) 1900 190023026
|
||||
14.0 (2015 Update 1) 1900 190023506
|
||||
14.0 (2015 Update 2) 1900 190023918
|
||||
14.0 (2015 Update 3) 1900 190024210
|
||||
*/
|
||||
|
||||
#if defined(_MSC_FULL_VER)
|
||||
#if _MSC_FULL_VER < 190024210
|
||||
#ifdef BOOST_MSVC
|
||||
#if BOOST_MSVC_FULL_VER < 190024210
|
||||
static_assert(false,
|
||||
"This library requires Visual Studio 2015 Update 3 or later");
|
||||
#endif
|
||||
|
||||
@@ -10,25 +10,31 @@
|
||||
|
||||
#include <beast/config.hpp>
|
||||
|
||||
#include <beast/core/async_completion.hpp>
|
||||
#include <beast/core/async_result.hpp>
|
||||
#include <beast/core/bind_handler.hpp>
|
||||
#include <beast/core/buffer_cat.hpp>
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/buffer_prefix.hpp>
|
||||
#include <beast/core/buffered_read_stream.hpp>
|
||||
#include <beast/core/buffers_adapter.hpp>
|
||||
#include <beast/core/consuming_buffers.hpp>
|
||||
#include <beast/core/drain_buffer.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/file.hpp>
|
||||
#include <beast/core/file_base.hpp>
|
||||
#include <beast/core/file_posix.hpp>
|
||||
#include <beast/core/file_stdio.hpp>
|
||||
#include <beast/core/file_win32.hpp>
|
||||
#include <beast/core/flat_buffer.hpp>
|
||||
#include <beast/core/handler_alloc.hpp>
|
||||
#include <beast/core/handler_concepts.hpp>
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <beast/core/placeholders.hpp>
|
||||
#include <beast/core/prepare_buffers.hpp>
|
||||
#include <beast/core/static_streambuf.hpp>
|
||||
#include <beast/core/multi_buffer.hpp>
|
||||
#include <beast/core/ostream.hpp>
|
||||
#include <beast/core/read_size.hpp>
|
||||
#include <beast/core/span.hpp>
|
||||
#include <beast/core/static_buffer.hpp>
|
||||
#include <beast/core/static_string.hpp>
|
||||
#include <beast/core/stream_concepts.hpp>
|
||||
#include <beast/core/streambuf.hpp>
|
||||
#include <beast/core/dynabuf_readstream.hpp>
|
||||
#include <beast/core/to_string.hpp>
|
||||
#include <beast/core/write_dynabuf.hpp>
|
||||
#include <beast/core/string.hpp>
|
||||
#include <beast/core/string_param.hpp>
|
||||
#include <beast/core/type_traits.hpp>
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_ASYNC_COMPLETION_HPP
|
||||
#define BEAST_ASYNC_COMPLETION_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/handler_concepts.hpp>
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <boost/asio/handler_type.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Helper for customizing the return type of asynchronous initiation functions.
|
||||
|
||||
This class template is used to transform caller-provided completion
|
||||
handlers in calls to asynchronous initiation functions. The transformation
|
||||
allows customization of the return type of the initiating function, and the
|
||||
function signature of the final handler.
|
||||
|
||||
@tparam CompletionHandler A completion handler, or a user defined type
|
||||
with specializations for customizing the return type (for example,
|
||||
`boost::asio::use_future` or `boost::asio::yield_context`).
|
||||
|
||||
@tparam Signature The callable signature of the final completion handler.
|
||||
|
||||
Example:
|
||||
@code
|
||||
...
|
||||
template<class CompletionHandler>
|
||||
typename async_completion<CompletionHandler,
|
||||
void(error_code)>::result_type
|
||||
async_initfn(..., CompletionHandler&& handler)
|
||||
{
|
||||
async_completion<CompletionHandler,
|
||||
void(error_code)> completion{handler};
|
||||
...
|
||||
return completion.result.get();
|
||||
}
|
||||
@endcode
|
||||
|
||||
@note See <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3896.pdf">
|
||||
Library Foundations For Asynchronous Operations</a>
|
||||
*/
|
||||
template<class CompletionHandler, class Signature>
|
||||
struct async_completion
|
||||
{
|
||||
/** The type of the final handler called by the asynchronous initiation function.
|
||||
|
||||
Objects of this type will be callable with the specified signature.
|
||||
*/
|
||||
using handler_type =
|
||||
typename boost::asio::handler_type<
|
||||
CompletionHandler, Signature>::type;
|
||||
|
||||
/// The type of the value returned by the asynchronous initiation function.
|
||||
using result_type = typename
|
||||
boost::asio::async_result<handler_type>::type;
|
||||
|
||||
/** Construct the helper.
|
||||
|
||||
@param token The completion handler. Copies will be made as
|
||||
required. If `CompletionHandler` is movable, it may also be moved.
|
||||
*/
|
||||
async_completion(typename std::remove_reference<CompletionHandler>::type& token)
|
||||
: handler(std::forward<CompletionHandler>(token))
|
||||
, result(handler)
|
||||
{
|
||||
static_assert(is_CompletionHandler<handler_type, Signature>::value,
|
||||
"Handler requirements not met");
|
||||
}
|
||||
|
||||
/// The final completion handler, callable with the specified signature.
|
||||
handler_type handler;
|
||||
|
||||
/// The return value of the asynchronous initiation function.
|
||||
boost::asio::async_result<handler_type> result;
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
205
include/beast/core/async_result.hpp
Normal file
205
include/beast/core/async_result.hpp
Normal file
@@ -0,0 +1,205 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_ASYNC_COMPLETION_HPP
|
||||
#define BEAST_ASYNC_COMPLETION_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/type_traits.hpp>
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <boost/asio/handler_type.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** An interface for customising the behaviour of an asynchronous initiation function.
|
||||
|
||||
This class is used for determining:
|
||||
|
||||
@li The concrete completion handler type to be called at the end of the
|
||||
asynchronous operation;
|
||||
|
||||
@li the initiating function return type; and
|
||||
|
||||
@li how the return value of the initiating function is obtained.
|
||||
|
||||
The trait allows the handler and return types to be determined at the point
|
||||
where the specific completion handler signature is known.
|
||||
|
||||
This template takes advantage of specializations of both
|
||||
`boost::asio::async_result` and `boost::asio::handler_type` for user-defined
|
||||
completion token types. The primary template assumes that the
|
||||
@b CompletionToken is the completion handler.
|
||||
|
||||
@par Example
|
||||
|
||||
The example shows how to define an asynchronous initiation function
|
||||
whose completion handler receives an error code:
|
||||
|
||||
@code
|
||||
template<
|
||||
class AsyncStream, // A stream supporting asynchronous read and write
|
||||
class Handler // The handler to call with signature void(error_code)
|
||||
>
|
||||
async_return_type< // This provides the return type customization
|
||||
Handler, void(error_code)>
|
||||
do_async(
|
||||
AsyncStream& stream, // The stream to work on
|
||||
Handler&& handler) // Could be an rvalue or const reference
|
||||
{
|
||||
// Make sure we have an async stream
|
||||
static_assert(is_async_stream<AsyncWriteStream>::value,
|
||||
"AsyncStream requirements not met");
|
||||
|
||||
// This helper converts the handler into the real handler type
|
||||
async_completion<WriteHandler, void(error_code)> init{handler};
|
||||
|
||||
... // Create and invoke the composed operation
|
||||
|
||||
// This provides the return value and executor customization
|
||||
return init.result.get();
|
||||
}
|
||||
@endcode
|
||||
|
||||
@see @ref async_completion, @ref async_return_type, @ref handler_type
|
||||
*/
|
||||
template<class CompletionToken, class Signature>
|
||||
class async_result
|
||||
{
|
||||
BOOST_STATIC_ASSERT(
|
||||
! std::is_reference<CompletionToken>::value);
|
||||
|
||||
boost::asio::async_result<typename
|
||||
boost::asio::handler_type<CompletionToken,
|
||||
Signature>::type> impl_;
|
||||
|
||||
async_result(async_result const&) = delete;
|
||||
async_result& operator=(async_result const&) = delete;
|
||||
|
||||
public:
|
||||
/// The concrete completion handler type for the specific signature.
|
||||
using completion_handler_type =
|
||||
typename boost::asio::handler_type<
|
||||
CompletionToken, Signature>::type;
|
||||
|
||||
/// The return type of the initiating function.
|
||||
using return_type =
|
||||
typename boost::asio::async_result<
|
||||
completion_handler_type>::type;
|
||||
|
||||
/** Construct an async result from a given handler.
|
||||
|
||||
When using a specalised async_result, the constructor has
|
||||
an opportunity to initialise some state associated with the
|
||||
completion handler, which is then returned from the initiating
|
||||
function.
|
||||
*/
|
||||
explicit
|
||||
async_result(completion_handler_type& h)
|
||||
: impl_(h)
|
||||
{
|
||||
}
|
||||
|
||||
/// Obtain the value to be returned from the initiating function.
|
||||
return_type
|
||||
get()
|
||||
{
|
||||
return impl_.get();
|
||||
}
|
||||
};
|
||||
|
||||
/** Helper for customizing the return type of asynchronous initiation functions.
|
||||
|
||||
This class template is used to transform caller-provided completion
|
||||
handlers in calls to asynchronous initiation functions. The transformation
|
||||
allows customization of the return type of the initiating function, and the
|
||||
function signature of the final handler.
|
||||
|
||||
Example:
|
||||
@code
|
||||
...
|
||||
template<class CompletionToken>
|
||||
typename async_completion<CompletionToken, void(error_code)>::result_type
|
||||
async_initfn(..., CompletionToken&& handler)
|
||||
{
|
||||
async_completion<CompletionToken, void(error_code)> completion{handler};
|
||||
...
|
||||
return completion.result.get();
|
||||
}
|
||||
@endcode
|
||||
|
||||
@tparam CompletionToken Specifies the model used to obtain the result of
|
||||
the asynchronous operation.
|
||||
|
||||
@tparam Signature The call signature for the completion handler type invoked
|
||||
on completion of the asynchronous operation.
|
||||
|
||||
@note See <a href="http://cplusplus.github.io/networking-ts/draft.pdf">
|
||||
Working Draft, C++ Extensions for Networking</a>
|
||||
|
||||
@see @ref async_return_type, @ref handler_type
|
||||
*/
|
||||
template<class CompletionToken, class Signature>
|
||||
struct async_completion
|
||||
{
|
||||
/** The type of the final handler called by the asynchronous initiation function.
|
||||
|
||||
Objects of this type will be callable with the specified signature.
|
||||
*/
|
||||
using completion_handler_type = typename async_result<
|
||||
typename std::decay<CompletionToken>::type,
|
||||
Signature>::completion_handler_type;
|
||||
|
||||
/** Constructor
|
||||
|
||||
The constructor creates the concrete completion handler and
|
||||
makes the link between the handler and the asynchronous
|
||||
result.
|
||||
|
||||
@param token The completion token. If this is a regular completion
|
||||
handler, copies may be made as needed. If the handler is movable,
|
||||
it may also be moved.
|
||||
*/
|
||||
explicit
|
||||
async_completion(CompletionToken& token)
|
||||
: completion_handler(static_cast<typename std::conditional<
|
||||
std::is_same<CompletionToken, completion_handler_type>::value,
|
||||
completion_handler_type&, CompletionToken&&>::type>(token))
|
||||
, result(completion_handler)
|
||||
{
|
||||
// CompletionToken is not invokable with the given signature
|
||||
static_assert(is_completion_handler<
|
||||
completion_handler_type, Signature>::value,
|
||||
"CompletionToken requirements not met: signature mismatch");
|
||||
}
|
||||
|
||||
/// The final completion handler, callable with the specified signature.
|
||||
typename std::conditional<std::is_same<
|
||||
CompletionToken, completion_handler_type>::value,
|
||||
completion_handler_type&,
|
||||
completion_handler_type
|
||||
>::type completion_handler;
|
||||
|
||||
/// The return value of the asynchronous initiation function.
|
||||
async_result<typename std::decay<
|
||||
CompletionToken>::type, Signature> result;
|
||||
};
|
||||
|
||||
template<class CompletionToken, typename Signature>
|
||||
using handler_type = typename beast::async_result<
|
||||
typename std::decay<CompletionToken>::type,
|
||||
Signature>::completion_handler_type;
|
||||
|
||||
template<class CompletionToken, typename Signature>
|
||||
using async_return_type = typename beast::async_result<
|
||||
typename std::decay<CompletionToken>::type,
|
||||
Signature>::return_type;
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -9,7 +9,7 @@
|
||||
#define BEAST_BIND_HANDLER_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/handler_concepts.hpp>
|
||||
#include <beast/core/type_traits.hpp>
|
||||
#include <beast/core/detail/bind_handler.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
@@ -24,24 +24,23 @@ namespace beast {
|
||||
the returned handler, which provides the same `io_service`
|
||||
execution guarantees as the original handler.
|
||||
|
||||
Unlike `io_service::wrap`, the returned handler can be used in
|
||||
a subsequent call to `io_service::post` instead of
|
||||
`io_service::dispatch`, to ensure that the handler will not be
|
||||
invoked immediately by the calling function.
|
||||
Unlike `boost::asio::io_service::wrap`, the returned handler can
|
||||
be used in a subsequent call to `boost::asio::io_service::post`
|
||||
instead of `boost::asio::io_service::dispatch`, to ensure that
|
||||
the handler will not be invoked immediately by the calling
|
||||
function.
|
||||
|
||||
Example:
|
||||
|
||||
@code
|
||||
|
||||
template<class AsyncReadStream, class ReadHandler>
|
||||
void
|
||||
do_cancel(AsyncReadStream& stream, ReadHandler&& handler)
|
||||
signal_aborted(AsyncReadStream& stream, ReadHandler&& handler)
|
||||
{
|
||||
stream.get_io_service().post(
|
||||
bind_handler(std::forward<ReadHandler>(handler),
|
||||
boost::asio::error::operation_aborted, 0));
|
||||
}
|
||||
|
||||
@endcode
|
||||
|
||||
@param handler The handler to wrap.
|
||||
@@ -50,7 +49,7 @@ namespace beast {
|
||||
arguments are forwarded into the returned object.
|
||||
*/
|
||||
template<class Handler, class... Args>
|
||||
#if GENERATING_DOCS
|
||||
#if BEAST_DOXYGEN
|
||||
implementation_defined
|
||||
#else
|
||||
detail::bound_handler<
|
||||
@@ -58,7 +57,7 @@ detail::bound_handler<
|
||||
#endif
|
||||
bind_handler(Handler&& handler, Args&&... args)
|
||||
{
|
||||
static_assert(is_CompletionHandler<
|
||||
static_assert(is_completion_handler<
|
||||
Handler, void(Args...)>::value,
|
||||
"Handler requirements not met");
|
||||
return detail::bound_handler<typename std::decay<
|
||||
|
||||
@@ -9,17 +9,73 @@
|
||||
#define BEAST_BUFFER_CAT_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/detail/buffer_cat.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <new>
|
||||
#include <stdexcept>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** A buffer sequence representing a concatenation of buffer sequences.
|
||||
|
||||
@see @ref buffer_cat
|
||||
*/
|
||||
template<class... Buffers>
|
||||
class buffer_cat_view
|
||||
{
|
||||
#if 0
|
||||
static_assert(
|
||||
detail::is_all_const_buffer_sequence<Buffers...>::value,
|
||||
"BufferSequence requirements not met");
|
||||
#endif
|
||||
|
||||
std::tuple<Buffers...> bn_;
|
||||
|
||||
public:
|
||||
/** The type of buffer returned when dereferencing an iterator.
|
||||
|
||||
If every buffer sequence in the view is a @b MutableBufferSequence,
|
||||
then `value_type` will be `boost::asio::mutable_buffer`.
|
||||
Otherwise, `value_type` will be `boost::asio::const_buffer`.
|
||||
*/
|
||||
using value_type =
|
||||
#if BEAST_DOXYGEN
|
||||
implementation_defined;
|
||||
#else
|
||||
typename detail::common_buffers_type<Buffers...>::type;
|
||||
#endif
|
||||
|
||||
/// The type of iterator used by the concatenated sequence
|
||||
class const_iterator;
|
||||
|
||||
/// Move constructor
|
||||
buffer_cat_view(buffer_cat_view&&) = default;
|
||||
|
||||
/// Copy constructor
|
||||
buffer_cat_view(buffer_cat_view const&) = default;
|
||||
|
||||
/// Move assignment
|
||||
buffer_cat_view& operator=(buffer_cat_view&&) = default;
|
||||
|
||||
// Copy assignment
|
||||
buffer_cat_view& operator=(buffer_cat_view const&) = default;
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param buffers The list of buffer sequences to concatenate.
|
||||
Copies of the arguments will be made; however, the ownership
|
||||
of memory is not transferred.
|
||||
*/
|
||||
explicit
|
||||
buffer_cat_view(Buffers const&... buffers);
|
||||
|
||||
/// Return an iterator to the beginning of the concatenated sequence.
|
||||
const_iterator
|
||||
begin() const;
|
||||
|
||||
/// Return an iterator to the end of the concatenated sequence.
|
||||
const_iterator
|
||||
end() const;
|
||||
};
|
||||
|
||||
/** Concatenate 2 or more buffer sequences.
|
||||
|
||||
This function returns a constant or mutable buffer sequence which,
|
||||
@@ -34,26 +90,30 @@ namespace beast {
|
||||
@return A new buffer sequence that represents the concatenation of
|
||||
the input buffer sequences. This buffer sequence will be a
|
||||
@b MutableBufferSequence if each of the passed buffer sequences is
|
||||
also a @b MutableBufferSequence, else the returned buffer sequence
|
||||
will be a @b ConstBufferSequence.
|
||||
also a @b MutableBufferSequence; otherwise the returned buffer
|
||||
sequence will be a @b ConstBufferSequence.
|
||||
|
||||
@see @ref buffer_cat_view
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
#if BEAST_DOXYGEN
|
||||
template<class... BufferSequence>
|
||||
implementation_defined
|
||||
buffer_cat_view<BufferSequence...>
|
||||
buffer_cat(BufferSequence const&... buffers)
|
||||
#else
|
||||
template<class B1, class B2, class... Bn>
|
||||
detail::buffer_cat_helper<B1, B2, Bn...>
|
||||
inline
|
||||
buffer_cat_view<B1, B2, Bn...>
|
||||
buffer_cat(B1 const& b1, B2 const& b2, Bn const&... bn)
|
||||
#endif
|
||||
{
|
||||
static_assert(
|
||||
detail::is_all_ConstBufferSequence<B1, B2, Bn...>::value,
|
||||
detail::is_all_const_buffer_sequence<B1, B2, Bn...>::value,
|
||||
"BufferSequence requirements not met");
|
||||
return detail::buffer_cat_helper<
|
||||
B1, B2, Bn...>{b1, b2, bn...};
|
||||
return buffer_cat_view<B1, B2, Bn...>{b1, b2, bn...};
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/core/impl/buffer_cat.ipp>
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_BUFFER_CONCEPTS_HPP
|
||||
#define BEAST_BUFFER_CONCEPTS_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/detail/buffer_concepts.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/// Determine if `T` meets the requirements of @b `BufferSequence`.
|
||||
template<class T, class BufferType>
|
||||
#if GENERATING_DOCS
|
||||
struct is_BufferSequence : std::integral_constant<bool, ...>
|
||||
#else
|
||||
struct is_BufferSequence : detail::is_BufferSequence<T, BufferType>::type
|
||||
#endif
|
||||
{
|
||||
};
|
||||
|
||||
/// Determine if `T` meets the requirements of @b `ConstBufferSequence`.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct is_ConstBufferSequence : std::integral_constant<bool, ...>
|
||||
#else
|
||||
struct is_ConstBufferSequence :
|
||||
is_BufferSequence<T, boost::asio::const_buffer>
|
||||
#endif
|
||||
{
|
||||
};
|
||||
|
||||
/// Determine if `T` meets the requirements of @b `DynamicBuffer`.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct is_DynamicBuffer : std::integral_constant<bool, ...>
|
||||
#else
|
||||
struct is_DynamicBuffer : detail::is_DynamicBuffer<T>::type
|
||||
#endif
|
||||
{
|
||||
};
|
||||
|
||||
/// Determine if `T` meets the requirements of @b `MutableBufferSequence`.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct is_MutableBufferSequence : std::integral_constant<bool, ...>
|
||||
#else
|
||||
struct is_MutableBufferSequence :
|
||||
is_BufferSequence<T, boost::asio::mutable_buffer>
|
||||
#endif
|
||||
{
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
208
include/beast/core/buffer_prefix.hpp
Normal file
208
include/beast/core/buffer_prefix.hpp
Normal file
@@ -0,0 +1,208 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_BUFFER_PREFIX_HPP
|
||||
#define BEAST_BUFFER_PREFIX_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/type_traits.hpp>
|
||||
#include <beast/core/detail/in_place_init.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** A buffer sequence adapter that shortens the sequence size.
|
||||
|
||||
The class adapts a buffer sequence to efficiently represent
|
||||
a shorter subset of the original list of buffers starting
|
||||
with the first byte of the original sequence.
|
||||
|
||||
@tparam BufferSequence The buffer sequence to adapt.
|
||||
*/
|
||||
template<class BufferSequence>
|
||||
class buffer_prefix_view
|
||||
{
|
||||
using buffers_type = typename
|
||||
std::decay<BufferSequence>::type;
|
||||
|
||||
using iter_type = typename buffers_type::const_iterator;
|
||||
|
||||
BufferSequence bs_;
|
||||
iter_type back_;
|
||||
iter_type end_;
|
||||
std::size_t size_;
|
||||
|
||||
template<class Deduced>
|
||||
buffer_prefix_view(Deduced&& other,
|
||||
std::size_t nback, std::size_t nend)
|
||||
: bs_(std::forward<Deduced>(other).bs_)
|
||||
, back_(std::next(bs_.begin(), nback))
|
||||
, end_(std::next(bs_.begin(), nend))
|
||||
, size_(other.size_)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
setup(std::size_t n);
|
||||
|
||||
public:
|
||||
/// The type for each element in the list of buffers.
|
||||
using value_type = typename std::conditional<
|
||||
std::is_convertible<typename
|
||||
std::iterator_traits<iter_type>::value_type,
|
||||
boost::asio::mutable_buffer>::value,
|
||||
boost::asio::mutable_buffer,
|
||||
boost::asio::const_buffer>::type;
|
||||
|
||||
#if BEAST_DOXYGEN
|
||||
/// A bidirectional iterator type that may be used to read elements.
|
||||
using const_iterator = implementation_defined;
|
||||
|
||||
#else
|
||||
class const_iterator;
|
||||
|
||||
#endif
|
||||
|
||||
/// Move constructor.
|
||||
buffer_prefix_view(buffer_prefix_view&&);
|
||||
|
||||
/// Copy constructor.
|
||||
buffer_prefix_view(buffer_prefix_view const&);
|
||||
|
||||
/// Move assignment.
|
||||
buffer_prefix_view& operator=(buffer_prefix_view&&);
|
||||
|
||||
/// Copy assignment.
|
||||
buffer_prefix_view& operator=(buffer_prefix_view const&);
|
||||
|
||||
/** Construct a buffer sequence prefix.
|
||||
|
||||
@param n The maximum number of bytes in the prefix.
|
||||
If this is larger than the size of passed, buffers,
|
||||
the resulting sequence will represent the entire
|
||||
input sequence.
|
||||
|
||||
@param buffers The buffer sequence to adapt. A copy of
|
||||
the sequence will be made, but ownership of the underlying
|
||||
memory is not transferred.
|
||||
*/
|
||||
buffer_prefix_view(std::size_t n, BufferSequence const& buffers);
|
||||
|
||||
/** Construct a buffer sequence prefix in-place.
|
||||
|
||||
@param n The maximum number of bytes in the prefix.
|
||||
If this is larger than the size of passed, buffers,
|
||||
the resulting sequence will represent the entire
|
||||
input sequence.
|
||||
|
||||
@param args Arguments forwarded to the contained buffers constructor.
|
||||
*/
|
||||
template<class... Args>
|
||||
buffer_prefix_view(std::size_t n,
|
||||
boost::in_place_init_t, Args&&... args);
|
||||
|
||||
/// Get a bidirectional iterator to the first element.
|
||||
const_iterator
|
||||
begin() const;
|
||||
|
||||
/// Get a bidirectional iterator to one past the last element.
|
||||
const_iterator
|
||||
end() const;
|
||||
};
|
||||
|
||||
/** Returns a prefix of a constant buffer.
|
||||
|
||||
The returned buffer points to the same memory as the
|
||||
passed buffer, but with a size that is equal to or less
|
||||
than the size of the original buffer.
|
||||
|
||||
@param n The size of the returned buffer.
|
||||
|
||||
@param buffer The buffer to shorten. The underlying
|
||||
memory is not modified.
|
||||
|
||||
@return A new buffer that points to the first `n` bytes
|
||||
of the original buffer.
|
||||
*/
|
||||
inline
|
||||
boost::asio::const_buffer
|
||||
buffer_prefix(std::size_t n,
|
||||
boost::asio::const_buffer buffer)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
return { buffer_cast<void const*>(buffer),
|
||||
(std::min)(n, buffer_size(buffer)) };
|
||||
}
|
||||
|
||||
/** Returns a prefix of a mutable buffer.
|
||||
|
||||
The returned buffer points to the same memory as the
|
||||
passed buffer, but with a size that is equal to or less
|
||||
than the size of the original buffer.
|
||||
|
||||
@param n The size of the returned buffer.
|
||||
|
||||
@param buffer The buffer to shorten. The underlying
|
||||
memory is not modified.
|
||||
|
||||
@return A new buffer that points to the first `n` bytes
|
||||
of the original buffer.
|
||||
*/
|
||||
inline
|
||||
boost::asio::mutable_buffer
|
||||
buffer_prefix(std::size_t n,
|
||||
boost::asio::mutable_buffer buffer)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
return { buffer_cast<void*>(buffer),
|
||||
(std::min)(n, buffer_size(buffer)) };
|
||||
}
|
||||
|
||||
/** Returns a prefix of a buffer sequence.
|
||||
|
||||
This function returns a new buffer sequence which when iterated,
|
||||
presents a shorter subset of the original list of buffers starting
|
||||
with the first byte of the original sequence.
|
||||
|
||||
@param n The maximum number of bytes in the wrapped
|
||||
sequence. If this is larger than the size of passed,
|
||||
buffers, the resulting sequence will represent the
|
||||
entire input sequence.
|
||||
|
||||
@param buffers An instance of @b ConstBufferSequence or
|
||||
@b MutableBufferSequence to adapt. A copy of the sequence
|
||||
will be made, but ownership of the underlying memory is
|
||||
not transferred.
|
||||
*/
|
||||
template<class BufferSequence>
|
||||
#if BEAST_DOXYGEN
|
||||
buffer_prefix_view<BufferSequence>
|
||||
#else
|
||||
inline
|
||||
typename std::enable_if<
|
||||
! std::is_same<BufferSequence, boost::asio::const_buffer>::value &&
|
||||
! std::is_same<BufferSequence, boost::asio::mutable_buffer>::value,
|
||||
buffer_prefix_view<BufferSequence>>::type
|
||||
#endif
|
||||
buffer_prefix(std::size_t n, BufferSequence const& buffers)
|
||||
{
|
||||
static_assert(
|
||||
is_const_buffer_sequence<BufferSequence>::value ||
|
||||
is_mutable_buffer_sequence<BufferSequence>::value,
|
||||
"BufferSequence requirements not met");
|
||||
return buffer_prefix_view<BufferSequence>(n, buffers);
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/core/impl/buffer_prefix.ipp>
|
||||
|
||||
#endif
|
||||
@@ -5,16 +5,14 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DYNABUF_READSTREAM_HPP
|
||||
#define BEAST_DYNABUF_READSTREAM_HPP
|
||||
#ifndef BEAST_BUFFERED_READ_STREAM_HPP
|
||||
#define BEAST_BUFFERED_READ_STREAM_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/async_completion.hpp>
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/async_result.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/stream_concepts.hpp>
|
||||
#include <beast/core/streambuf.hpp>
|
||||
#include <beast/core/detail/get_lowest_layer.hpp>
|
||||
#include <beast/core/multi_buffer.hpp>
|
||||
#include <beast/core/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <cstdint>
|
||||
@@ -22,11 +20,11 @@
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** A @b `Stream` with attached @b `DynamicBuffer` to buffer reads.
|
||||
/** A @b Stream with attached @b DynamicBuffer to buffer reads.
|
||||
|
||||
This wraps a @b `Stream` implementation so that calls to write are
|
||||
This wraps a @b Stream implementation so that calls to write are
|
||||
passed through to the underlying stream, while calls to read will
|
||||
first consume the input sequence stored in a @b `DynamicBuffer` which
|
||||
first consume the input sequence stored in a @b DynamicBuffer which
|
||||
is part of the object.
|
||||
|
||||
The use-case for this class is different than that of the
|
||||
@@ -52,7 +50,7 @@ namespace beast {
|
||||
//
|
||||
template<class DynamicBuffer>
|
||||
void process_http_message(
|
||||
dynabuf_readstream<DynamicBuffer>& stream)
|
||||
buffered_read_stream<DynamicBuffer>& stream)
|
||||
{
|
||||
// Read up to and including the end of the HTTP
|
||||
// header, leaving the sequence in the stream's
|
||||
@@ -64,11 +62,11 @@ namespace beast {
|
||||
boost::asio::read_until(
|
||||
stream.next_layer(), stream.buffer(), "\r\n\r\n");
|
||||
|
||||
// Use prepare_buffers() to limit the input
|
||||
// Use buffer_prefix() to limit the input
|
||||
// sequence to only the data up to and including
|
||||
// the trailing "\r\n\r\n".
|
||||
//
|
||||
auto header_buffers = prepare_buffers(
|
||||
auto header_buffers = buffer_prefix(
|
||||
bytes_transferred, stream.buffer().data());
|
||||
|
||||
...
|
||||
@@ -88,9 +86,9 @@ namespace beast {
|
||||
@tparam DynamicBuffer The type of stream buffer to use.
|
||||
*/
|
||||
template<class Stream, class DynamicBuffer>
|
||||
class dynabuf_readstream
|
||||
class buffered_read_stream
|
||||
{
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
|
||||
template<class Buffers, class Handler>
|
||||
@@ -102,7 +100,7 @@ class dynabuf_readstream
|
||||
|
||||
public:
|
||||
/// The type of the internal buffer
|
||||
using dynabuf_type = DynamicBuffer;
|
||||
using buffer_type = DynamicBuffer;
|
||||
|
||||
/// The type of the next layer.
|
||||
using next_layer_type =
|
||||
@@ -110,26 +108,21 @@ public:
|
||||
|
||||
/// The type of the lowest layer.
|
||||
using lowest_layer_type =
|
||||
#if GENERATING_DOCS
|
||||
implementation_defined;
|
||||
#else
|
||||
typename detail::get_lowest_layer<
|
||||
next_layer_type>::type;
|
||||
#endif
|
||||
typename get_lowest_layer<next_layer_type>::type;
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
@note The behavior of move assignment on or from streams
|
||||
with active or pending operations is undefined.
|
||||
*/
|
||||
dynabuf_readstream(dynabuf_readstream&&) = default;
|
||||
buffered_read_stream(buffered_read_stream&&) = default;
|
||||
|
||||
/** Move assignment.
|
||||
|
||||
@note The behavior of move assignment on or from streams
|
||||
with active or pending operations is undefined.
|
||||
*/
|
||||
dynabuf_readstream& operator=(dynabuf_readstream&&) = default;
|
||||
buffered_read_stream& operator=(buffered_read_stream&&) = default;
|
||||
|
||||
/** Construct the wrapping stream.
|
||||
|
||||
@@ -137,7 +130,7 @@ public:
|
||||
*/
|
||||
template<class... Args>
|
||||
explicit
|
||||
dynabuf_readstream(Args&&... args);
|
||||
buffered_read_stream(Args&&... args);
|
||||
|
||||
/// Get a reference to the next layer.
|
||||
next_layer_type&
|
||||
@@ -272,10 +265,10 @@ public:
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
*/
|
||||
template<class MutableBufferSequence, class ReadHandler>
|
||||
#if GENERATING_DOCS
|
||||
#if BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
#else
|
||||
typename async_completion<ReadHandler, void(error_code)>::result_type
|
||||
async_return_type<ReadHandler, void(error_code)>
|
||||
#endif
|
||||
async_read_some(MutableBufferSequence const& buffers,
|
||||
ReadHandler&& handler);
|
||||
@@ -296,7 +289,7 @@ public:
|
||||
std::size_t
|
||||
write_some(ConstBufferSequence const& buffers)
|
||||
{
|
||||
static_assert(is_SyncWriteStream<next_layer_type>::value,
|
||||
static_assert(is_sync_write_stream<next_layer_type>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
return next_layer_.write_some(buffers);
|
||||
}
|
||||
@@ -318,7 +311,7 @@ public:
|
||||
write_some(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_SyncWriteStream<next_layer_type>::value,
|
||||
static_assert(is_sync_write_stream<next_layer_type>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
return next_layer_.write_some(buffers, ec);
|
||||
}
|
||||
@@ -347,10 +340,10 @@ public:
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
*/
|
||||
template<class ConstBufferSequence, class WriteHandler>
|
||||
#if GENERATING_DOCS
|
||||
#if BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
#else
|
||||
typename async_completion<WriteHandler, void(error_code)>::result_type
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
#endif
|
||||
async_write_some(ConstBufferSequence const& buffers,
|
||||
WriteHandler&& handler);
|
||||
@@ -358,6 +351,6 @@ public:
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/core/impl/dynabuf_readstream.ipp>
|
||||
#include <beast/core/impl/buffered_read_stream.ipp>
|
||||
|
||||
#endif
|
||||
@@ -9,16 +9,16 @@
|
||||
#define BEAST_BUFFERS_ADAPTER_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Adapts a @b `MutableBufferSequence` into a @b `DynamicBuffer`.
|
||||
/** Adapts a @b MutableBufferSequence into a @b DynamicBuffer.
|
||||
|
||||
This class wraps a @b `MutableBufferSequence` to meet the requirements
|
||||
of @b `DynamicBuffer`. Upon construction the input and output sequences are
|
||||
This class wraps a @b MutableBufferSequence to meet the requirements
|
||||
of @b DynamicBuffer. Upon construction the input and output sequences are
|
||||
empty. A copy of the mutable buffer sequence object is stored; however,
|
||||
ownership of the underlying memory is not transferred. The caller is
|
||||
responsible for making sure that referenced memory remains valid
|
||||
@@ -32,7 +32,7 @@ namespace beast {
|
||||
template<class MutableBufferSequence>
|
||||
class buffers_adapter
|
||||
{
|
||||
static_assert(is_MutableBufferSequence<MutableBufferSequence>::value,
|
||||
static_assert(is_mutable_buffer_sequence<MutableBufferSequence>::value,
|
||||
"MutableBufferSequence requirements not met");
|
||||
|
||||
using iter_type = typename MutableBufferSequence::const_iterator;
|
||||
@@ -64,7 +64,7 @@ class buffers_adapter
|
||||
}
|
||||
|
||||
public:
|
||||
#if GENERATING_DOCS
|
||||
#if BEAST_DOXYGEN
|
||||
/// The type used to represent the input sequence as a list of buffers.
|
||||
using const_buffers_type = implementation_defined;
|
||||
|
||||
@@ -112,6 +112,13 @@ public:
|
||||
{
|
||||
return in_size_;
|
||||
}
|
||||
|
||||
/// Returns the maximum sum of the sizes of the input sequence and output sequence the buffer can hold without requiring reallocation.
|
||||
std::size_t
|
||||
capacity() const
|
||||
{
|
||||
return max_size_;
|
||||
}
|
||||
|
||||
/** Get a list of buffers that represents the output sequence, with the given size.
|
||||
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
#define BEAST_CONSUMING_BUFFERS_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/detail/in_place_init.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
@@ -40,7 +41,6 @@ class consuming_buffers
|
||||
|
||||
BufferSequence bs_;
|
||||
iter_type begin_;
|
||||
iter_type end_;
|
||||
std::size_t skip_ = 0;
|
||||
|
||||
template<class Deduced>
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
`boost::asio::mutable_buffer`, else this type will be
|
||||
`boost::asio::const_buffer`.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
#if BEAST_DOXYGEN
|
||||
using value_type = ...;
|
||||
#else
|
||||
using value_type = typename std::conditional<
|
||||
@@ -70,7 +70,7 @@ public:
|
||||
boost::asio::const_buffer>::type;
|
||||
#endif
|
||||
|
||||
#if GENERATING_DOCS
|
||||
#if BEAST_DOXYGEN
|
||||
/// A bidirectional iterator type that may be used to read elements.
|
||||
using const_iterator = implementation_defined;
|
||||
|
||||
@@ -79,18 +79,15 @@ public:
|
||||
|
||||
#endif
|
||||
|
||||
/// Move constructor.
|
||||
/// Constructor
|
||||
consuming_buffers();
|
||||
|
||||
/// Move constructor
|
||||
consuming_buffers(consuming_buffers&&);
|
||||
|
||||
/// Copy constructor.
|
||||
/// Copy constructor
|
||||
consuming_buffers(consuming_buffers const&);
|
||||
|
||||
/// Move assignment.
|
||||
consuming_buffers& operator=(consuming_buffers&&);
|
||||
|
||||
/// Copy assignment.
|
||||
consuming_buffers& operator=(consuming_buffers const&);
|
||||
|
||||
/** Construct to represent a buffer sequence.
|
||||
|
||||
A copy of the buffer sequence is made. Ownership of the
|
||||
@@ -99,6 +96,19 @@ public:
|
||||
explicit
|
||||
consuming_buffers(BufferSequence const& buffers);
|
||||
|
||||
/** Construct a buffer sequence in-place.
|
||||
|
||||
@param args Arguments forwarded to the contained buffers constructor.
|
||||
*/
|
||||
template<class... Args>
|
||||
consuming_buffers(boost::in_place_init_t, Args&&... args);
|
||||
|
||||
/// Move assignment
|
||||
consuming_buffers& operator=(consuming_buffers&&);
|
||||
|
||||
/// Copy assignmen
|
||||
consuming_buffers& operator=(consuming_buffers const&);
|
||||
|
||||
/// Get a bidirectional iterator to the first element.
|
||||
const_iterator
|
||||
begin() const;
|
||||
|
||||
@@ -5,15 +5,6 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_BASE64_HPP
|
||||
#define BEAST_DETAIL_BASE64_HPP
|
||||
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
/*
|
||||
Portions from http://www.adp-gmbh.ch/cpp/common/base64.html
|
||||
Copyright notice:
|
||||
@@ -44,77 +35,195 @@ namespace detail {
|
||||
|
||||
*/
|
||||
|
||||
template<class = void>
|
||||
std::string const&
|
||||
base64_alphabet()
|
||||
#ifndef BEAST_DETAIL_BASE64_HPP
|
||||
#define BEAST_DETAIL_BASE64_HPP
|
||||
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
namespace base64 {
|
||||
|
||||
inline
|
||||
char const*
|
||||
get_alphabet()
|
||||
{
|
||||
static std::string const alphabet =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
return alphabet;
|
||||
static char constexpr tab[] = {
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
};
|
||||
return &tab[0];
|
||||
}
|
||||
|
||||
inline
|
||||
bool
|
||||
is_base64(unsigned char c)
|
||||
signed char const*
|
||||
get_inverse()
|
||||
{
|
||||
return (std::isalnum(c) || (c == '+') || (c == '/'));
|
||||
static signed char constexpr tab[] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0-15
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16-31
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, // 32-47
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 48-63
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 64-79
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 80-95
|
||||
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 112-127
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128-143
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144-159
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160-175
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176-191
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192-207
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208-223
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224-239
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240-255
|
||||
};
|
||||
return &tab[0];
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
std::string
|
||||
base64_encode (std::uint8_t const* data,
|
||||
std::size_t in_len)
|
||||
|
||||
/// Returns max chars needed to encode a base64 string
|
||||
inline
|
||||
std::size_t constexpr
|
||||
encoded_size(std::size_t n)
|
||||
{
|
||||
return 4 * ((n + 2) / 3);
|
||||
}
|
||||
|
||||
/// Returns max bytes needed to decode a base64 string
|
||||
inline
|
||||
std::size_t constexpr
|
||||
decoded_size(std::size_t n)
|
||||
{
|
||||
return n / 4 * 3; // requires n&3==0, smaller
|
||||
//return 3 * n / 4;
|
||||
}
|
||||
|
||||
/** Encode a series of octets as a padded, base64 string.
|
||||
|
||||
The resulting string will not be null terminated.
|
||||
|
||||
@par Requires
|
||||
|
||||
The memory pointed to by `out` points to valid memory
|
||||
of at least `encoded_size(len)` bytes.
|
||||
|
||||
@return The number of characters written to `out`. This
|
||||
will exclude any null termination.
|
||||
*/
|
||||
template<class = void>
|
||||
std::size_t
|
||||
encode(void* dest, void const* src, std::size_t len)
|
||||
{
|
||||
char* out = static_cast<char*>(dest);
|
||||
char const* in = static_cast<char const*>(src);
|
||||
auto const tab = base64::get_alphabet();
|
||||
|
||||
for(auto n = len / 3; n--;)
|
||||
{
|
||||
*out++ = tab[ (in[0] & 0xfc) >> 2];
|
||||
*out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)];
|
||||
*out++ = tab[((in[2] & 0xc0) >> 6) + ((in[1] & 0x0f) << 2)];
|
||||
*out++ = tab[ in[2] & 0x3f];
|
||||
in += 3;
|
||||
}
|
||||
|
||||
switch(len % 3)
|
||||
{
|
||||
case 2:
|
||||
*out++ = tab[ (in[0] & 0xfc) >> 2];
|
||||
*out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)];
|
||||
*out++ = tab[ (in[1] & 0x0f) << 2];
|
||||
*out++ = '=';
|
||||
break;
|
||||
|
||||
case 1:
|
||||
*out++ = tab[ (in[0] & 0xfc) >> 2];
|
||||
*out++ = tab[((in[0] & 0x03) << 4)];
|
||||
*out++ = '=';
|
||||
*out++ = '=';
|
||||
break;
|
||||
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
|
||||
return out - static_cast<char*>(dest);
|
||||
}
|
||||
|
||||
/** Decode a padded base64 string into a series of octets.
|
||||
|
||||
@par Requires
|
||||
|
||||
The memory pointed to by `out` points to valid memory
|
||||
of at least `decoded_size(len)` bytes.
|
||||
|
||||
@return The number of octets written to `out`, and
|
||||
the number of characters read from the input string,
|
||||
expressed as a pair.
|
||||
*/
|
||||
template<class = void>
|
||||
std::pair<std::size_t, std::size_t>
|
||||
decode(void* dest, char const* src, std::size_t len)
|
||||
{
|
||||
char* out = static_cast<char*>(dest);
|
||||
auto in = reinterpret_cast<unsigned char const*>(src);
|
||||
unsigned char c3[3], c4[4];
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
std::string ret;
|
||||
ret.reserve (3 + in_len * 8 / 6);
|
||||
auto const inverse = base64::get_inverse();
|
||||
|
||||
char const* alphabet (base64_alphabet().data());
|
||||
|
||||
while(in_len--)
|
||||
while(len-- && *in != '=')
|
||||
{
|
||||
c3[i++] = *(data++);
|
||||
if(i == 3)
|
||||
auto const v = inverse[*in];
|
||||
if(v == -1)
|
||||
break;
|
||||
++in;
|
||||
c4[i] = v;
|
||||
if(++i == 4)
|
||||
{
|
||||
c4[0] = (c3[0] & 0xfc) >> 2;
|
||||
c4[1] = ((c3[0] & 0x03) << 4) + ((c3[1] & 0xf0) >> 4);
|
||||
c4[2] = ((c3[1] & 0x0f) << 2) + ((c3[2] & 0xc0) >> 6);
|
||||
c4[3] = c3[2] & 0x3f;
|
||||
for(i = 0; (i < 4); i++)
|
||||
ret += alphabet[c4[i]];
|
||||
c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
|
||||
c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
|
||||
c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
|
||||
|
||||
for(i = 0; i < 3; i++)
|
||||
*out++ = c3[i];
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(i)
|
||||
{
|
||||
for(j = i; j < 3; j++)
|
||||
c3[j] = '\0';
|
||||
c3[0] = ( c4[0] << 2) + ((c4[1] & 0x30) >> 4);
|
||||
c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
|
||||
c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
|
||||
|
||||
c4[0] = (c3[0] & 0xfc) >> 2;
|
||||
c4[1] = ((c3[0] & 0x03) << 4) + ((c3[1] & 0xf0) >> 4);
|
||||
c4[2] = ((c3[1] & 0x0f) << 2) + ((c3[2] & 0xc0) >> 6);
|
||||
c4[3] = c3[2] & 0x3f;
|
||||
|
||||
for(j = 0; (j < i + 1); j++)
|
||||
ret += alphabet[c4[j]];
|
||||
|
||||
while((i++ < 3))
|
||||
ret += '=';
|
||||
for(j = 0; j < i - 1; j++)
|
||||
*out++ = c3[j];
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
return {out - static_cast<char*>(dest),
|
||||
in - reinterpret_cast<unsigned char const*>(src)};
|
||||
}
|
||||
|
||||
} // base64
|
||||
|
||||
template<class = void>
|
||||
std::string
|
||||
base64_encode (std::string const& s)
|
||||
base64_encode (std::uint8_t const* data,
|
||||
std::size_t len)
|
||||
{
|
||||
std::string dest;
|
||||
dest.resize(base64::encoded_size(len));
|
||||
dest.resize(base64::encode(&dest[0], data, len));
|
||||
return dest;
|
||||
}
|
||||
|
||||
inline
|
||||
std::string
|
||||
base64_encode(std::string const& s)
|
||||
{
|
||||
return base64_encode (reinterpret_cast <
|
||||
std::uint8_t const*> (s.data()), s.size());
|
||||
@@ -124,52 +233,12 @@ template<class = void>
|
||||
std::string
|
||||
base64_decode(std::string const& data)
|
||||
{
|
||||
auto in_len = data.size();
|
||||
unsigned char c3[3], c4[4];
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int in_ = 0;
|
||||
|
||||
std::string ret;
|
||||
ret.reserve (in_len * 6 / 8); // ???
|
||||
|
||||
while(in_len-- && (data[in_] != '=') &&
|
||||
is_base64(data[in_]))
|
||||
{
|
||||
c4[i++] = data[in_]; in_++;
|
||||
if(i == 4) {
|
||||
for(i = 0; i < 4; i++)
|
||||
c4[i] = static_cast<unsigned char>(
|
||||
base64_alphabet().find(c4[i]));
|
||||
|
||||
c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
|
||||
c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
|
||||
c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
|
||||
|
||||
for(i = 0; (i < 3); i++)
|
||||
ret += c3[i];
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(i)
|
||||
{
|
||||
for(j = i; j < 4; j++)
|
||||
c4[j] = 0;
|
||||
|
||||
for(j = 0; j < 4; j++)
|
||||
c4[j] = static_cast<unsigned char>(
|
||||
base64_alphabet().find(c4[j]));
|
||||
|
||||
c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
|
||||
c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
|
||||
c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
|
||||
|
||||
for(j = 0; (j < i - 1); j++)
|
||||
ret += c3[j];
|
||||
}
|
||||
|
||||
return ret;
|
||||
std::string dest;
|
||||
dest.resize(base64::decoded_size(data.size()));
|
||||
auto const result = base64::decode(
|
||||
&dest[0], data.data(), data.size());
|
||||
dest.resize(result.first);
|
||||
return dest;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
#ifndef BEAST_BIND_DETAIL_HANDLER_HPP
|
||||
#define BEAST_BIND_DETAIL_HANDLER_HPP
|
||||
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <beast/core/detail/integer_sequence.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
@@ -68,8 +70,9 @@ public:
|
||||
asio_handler_allocate(
|
||||
std::size_t size, bound_handler* h)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, h->h_);
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(h->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
@@ -77,16 +80,17 @@ public:
|
||||
asio_handler_deallocate(
|
||||
void* p, std::size_t size, bound_handler* h)
|
||||
{
|
||||
beast_asio_helpers::
|
||||
deallocate(p, size, h->h_);
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(h->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
bool
|
||||
asio_handler_is_continuation(bound_handler* h)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
is_continuation (h->h_);
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return asio_handler_is_continuation(std::addressof(h->h_));
|
||||
}
|
||||
|
||||
template<class F>
|
||||
@@ -94,8 +98,9 @@ public:
|
||||
void
|
||||
asio_handler_invoke(F&& f, bound_handler* h)
|
||||
{
|
||||
beast_asio_helpers::
|
||||
invoke(f, h->h_);
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(h->h_));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_BUFFER_CONCEPTS_HPP
|
||||
#define BEAST_DETAIL_BUFFER_CONCEPTS_HPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
// Types that meet the requirements,
|
||||
// for use with std::declval only.
|
||||
template<class BufferType>
|
||||
struct BufferSequence
|
||||
{
|
||||
using value_type = BufferType;
|
||||
using const_iterator = BufferType const*;
|
||||
~BufferSequence();
|
||||
BufferSequence(BufferSequence const&) = default;
|
||||
const_iterator begin() const noexcept;
|
||||
const_iterator end() const noexcept;
|
||||
};
|
||||
using ConstBufferSequence =
|
||||
BufferSequence<boost::asio::const_buffer>;
|
||||
using MutableBufferSequence =
|
||||
BufferSequence<boost::asio::mutable_buffer>;
|
||||
|
||||
template<class T, class BufferType>
|
||||
class is_BufferSequence
|
||||
{
|
||||
template<class U, class R = std::is_convertible<
|
||||
typename U::value_type, BufferType> >
|
||||
static R check1(int);
|
||||
template<class>
|
||||
static std::false_type check1(...);
|
||||
using type1 = decltype(check1<T>(0));
|
||||
|
||||
template<class U, class R = std::is_base_of<
|
||||
#if 0
|
||||
std::bidirectional_iterator_tag,
|
||||
typename std::iterator_traits<
|
||||
typename U::const_iterator>::iterator_category>>
|
||||
#else
|
||||
// workaround:
|
||||
// boost::asio::detail::consuming_buffers::const_iterator
|
||||
// is not bidirectional
|
||||
std::forward_iterator_tag,
|
||||
typename std::iterator_traits<
|
||||
typename U::const_iterator>::iterator_category>>
|
||||
#endif
|
||||
static R check2(int);
|
||||
template<class>
|
||||
static std::false_type check2(...);
|
||||
using type2 = decltype(check2<T>(0));
|
||||
|
||||
template<class U, class R = typename
|
||||
std::is_convertible<decltype(
|
||||
std::declval<U>().begin()),
|
||||
typename U::const_iterator>::type>
|
||||
static R check3(int);
|
||||
template<class>
|
||||
static std::false_type check3(...);
|
||||
using type3 = decltype(check3<T>(0));
|
||||
|
||||
template<class U, class R = typename std::is_convertible<decltype(
|
||||
std::declval<U>().end()),
|
||||
typename U::const_iterator>::type>
|
||||
static R check4(int);
|
||||
template<class>
|
||||
static std::false_type check4(...);
|
||||
using type4 = decltype(check4<T>(0));
|
||||
|
||||
public:
|
||||
using type = std::integral_constant<bool,
|
||||
std::is_copy_constructible<T>::value &&
|
||||
std::is_destructible<T>::value &&
|
||||
type1::value && type2::value &&
|
||||
type3::value && type4::value>;
|
||||
};
|
||||
|
||||
template<class B1, class... Bn>
|
||||
struct is_all_ConstBufferSequence
|
||||
: std::integral_constant<bool,
|
||||
is_BufferSequence<B1, boost::asio::const_buffer>::type::value &&
|
||||
is_all_ConstBufferSequence<Bn...>::value>
|
||||
{
|
||||
};
|
||||
|
||||
template<class B1>
|
||||
struct is_all_ConstBufferSequence<B1>
|
||||
: is_BufferSequence<B1, boost::asio::const_buffer>::type
|
||||
{
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class is_DynamicBuffer
|
||||
{
|
||||
// size()
|
||||
template<class U, class R = std::is_convertible<decltype(
|
||||
std::declval<U const>().size()), std::size_t>>
|
||||
static R check1(int);
|
||||
template<class>
|
||||
static std::false_type check1(...);
|
||||
using type1 = decltype(check1<T>(0));
|
||||
|
||||
// max_size()
|
||||
template<class U, class R = std::is_convertible<decltype(
|
||||
std::declval<U const>().max_size()), std::size_t>>
|
||||
static R check2(int);
|
||||
template<class>
|
||||
static std::false_type check2(...);
|
||||
using type2 = decltype(check2<T>(0));
|
||||
|
||||
// capacity()
|
||||
template<class U, class R = std::is_convertible<decltype(
|
||||
std::declval<U const>().capacity()), std::size_t>>
|
||||
static R check3(int);
|
||||
template<class>
|
||||
static std::false_type check3(...);
|
||||
using type3 = decltype(check3<T>(0));
|
||||
|
||||
// data()
|
||||
template<class U, class R = std::integral_constant<
|
||||
bool, is_BufferSequence<decltype(
|
||||
std::declval<U const>().data()),
|
||||
boost::asio::const_buffer>::type::value>>
|
||||
static R check4(int);
|
||||
template<class>
|
||||
static std::false_type check4(...);
|
||||
using type4 = decltype(check4<T>(0));
|
||||
|
||||
// prepare()
|
||||
template<class U, class R = std::integral_constant<
|
||||
bool, is_BufferSequence<decltype(
|
||||
std::declval<U>().prepare(1)),
|
||||
boost::asio::mutable_buffer>::type::value>>
|
||||
static R check5(int);
|
||||
template<class>
|
||||
static std::false_type check5(...);
|
||||
using type5 = decltype(check5<T>(0));
|
||||
|
||||
// commit()
|
||||
template<class U, class R = decltype(
|
||||
std::declval<U>().commit(1), std::true_type{})>
|
||||
static R check6(int);
|
||||
template<class>
|
||||
static std::false_type check6(...);
|
||||
using type6 = decltype(check6<T>(0));
|
||||
|
||||
// consume
|
||||
template<class U, class R = decltype(
|
||||
std::declval<U>().consume(1), std::true_type{})>
|
||||
static R check7(int);
|
||||
template<class>
|
||||
static std::false_type check7(...);
|
||||
using type7 = decltype(check7<T>(0));
|
||||
|
||||
public:
|
||||
using type = std::integral_constant<bool,
|
||||
type1::value
|
||||
&& type2::value
|
||||
//&& type3::value // Networking TS
|
||||
&& type4::value
|
||||
&& type5::value
|
||||
&& type6::value
|
||||
&& type7::value
|
||||
>;
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
61
include/beast/core/detail/buffers_ref.hpp
Normal file
61
include/beast/core/detail/buffers_ref.hpp
Normal file
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_BUFFERS_REF_HPP
|
||||
#define BEAST_DETAIL_BUFFERS_REF_HPP
|
||||
|
||||
#include <beast/core/type_traits.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
// A very lightweight reference to a buffer sequence
|
||||
template<class BufferSequence>
|
||||
class buffers_ref
|
||||
{
|
||||
BufferSequence const& buffers_;
|
||||
|
||||
public:
|
||||
using value_type =
|
||||
typename BufferSequence::value_type;
|
||||
|
||||
using const_iterator =
|
||||
typename BufferSequence::const_iterator;
|
||||
|
||||
buffers_ref(buffers_ref const&) = default;
|
||||
|
||||
explicit
|
||||
buffers_ref(BufferSequence const& buffers)
|
||||
: buffers_(buffers)
|
||||
{
|
||||
}
|
||||
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return buffers_.begin();
|
||||
}
|
||||
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return buffers_.end();
|
||||
}
|
||||
};
|
||||
|
||||
// Return a reference to a buffer sequence
|
||||
template<class BufferSequence>
|
||||
buffers_ref<BufferSequence>
|
||||
make_buffers_ref(BufferSequence const& buffers)
|
||||
{
|
||||
return buffers_ref<BufferSequence>(buffers);
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -1,106 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_CI_CHAR_TRAITS_HPP
|
||||
#define BEAST_DETAIL_CI_CHAR_TRAITS_HPP
|
||||
|
||||
#include <boost/range/algorithm/equal.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
char
|
||||
tolower(char c)
|
||||
{
|
||||
static std::array<std::uint8_t, 256> constexpr tab = {{
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
|
||||
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95,
|
||||
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
|
||||
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
|
||||
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
|
||||
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
|
||||
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
|
||||
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
|
||||
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
|
||||
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
|
||||
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
|
||||
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
|
||||
}};
|
||||
return static_cast<char>(tab[static_cast<std::uint8_t>(c)]);
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
inline
|
||||
boost::string_ref
|
||||
string_helper(const char (&s)[N])
|
||||
{
|
||||
return boost::string_ref{s, N-1};
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline
|
||||
T const&
|
||||
string_helper(T const& t)
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
// Case-insensitive less
|
||||
struct ci_less
|
||||
{
|
||||
static bool const is_transparent = true;
|
||||
|
||||
template<class S1, class S2>
|
||||
bool
|
||||
operator()(S1 const& lhs, S2 const& rhs) const noexcept
|
||||
{
|
||||
using std::begin;
|
||||
using std::end;
|
||||
auto const s1 = string_helper(lhs);
|
||||
auto const s2 = string_helper(rhs);
|
||||
return std::lexicographical_compare(
|
||||
begin(s1), end(s1), begin(s2), end(s2),
|
||||
[](char lhs, char rhs)
|
||||
{
|
||||
return tolower(lhs) < tolower(rhs);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Case-insensitive equal
|
||||
struct ci_equal_pred
|
||||
{
|
||||
bool
|
||||
operator()(char c1, char c2) const noexcept
|
||||
{
|
||||
return tolower(c1) == tolower(c2);
|
||||
}
|
||||
};
|
||||
|
||||
// Case-insensitive equal
|
||||
template<class S1, class S2>
|
||||
bool
|
||||
ci_equal(S1 const& lhs, S2 const& rhs)
|
||||
{
|
||||
return boost::range::equal(
|
||||
string_helper(lhs), string_helper(rhs),
|
||||
ci_equal_pred{});
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
20
include/beast/core/detail/config.hpp
Normal file
20
include/beast/core/detail/config.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_CORE_DETAIL_CONFIG_HPP
|
||||
#define BEAST_CORE_DETAIL_CONFIG_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/version.hpp>
|
||||
|
||||
#if BOOST_VERSION >= 106500 || ! defined(BOOST_GCC) || BOOST_GCC < 70000
|
||||
# define BEAST_FALLTHROUGH BOOST_FALLTHROUGH
|
||||
#else
|
||||
# define BEAST_FALLTHROUGH __attribute__((fallthrough))
|
||||
#endif
|
||||
|
||||
#endif
|
||||
95
include/beast/core/detail/cpu_info.hpp
Normal file
95
include/beast/core/detail/cpu_info.hpp
Normal file
@@ -0,0 +1,95 @@
|
||||
//
|
||||
// Copyright (c) 2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_CPU_INFO_HPP
|
||||
#define BEAST_DETAIL_CPU_INFO_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#ifndef BEAST_NO_INTRINSICS
|
||||
# if defined(BOOST_MSVC) || ((defined(BOOST_GCC) || defined(BOOST_CLANG)) && defined(__SSE4_2__))
|
||||
# define BEAST_NO_INTRINSICS 0
|
||||
# else
|
||||
# define BEAST_NO_INTRINSICS 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if ! BEAST_NO_INTRINSICS
|
||||
|
||||
#ifdef BOOST_MSVC
|
||||
#include <intrin.h> // __cpuid
|
||||
#else
|
||||
#include <cpuid.h> // __get_cpuid
|
||||
#endif
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
/* Portions from Boost,
|
||||
Copyright Andrey Semashev 2007 - 2015.
|
||||
*/
|
||||
template<class = void>
|
||||
void
|
||||
cpuid(
|
||||
std::uint32_t id,
|
||||
std::uint32_t& eax,
|
||||
std::uint32_t& ebx,
|
||||
std::uint32_t& ecx,
|
||||
std::uint32_t& edx)
|
||||
{
|
||||
#ifdef BOOST_MSVC
|
||||
int regs[4];
|
||||
__cpuid(regs, id);
|
||||
eax = regs[0];
|
||||
ebx = regs[1];
|
||||
ecx = regs[2];
|
||||
edx = regs[3];
|
||||
#else
|
||||
__get_cpuid(id, &eax, &ebx, &ecx, &edx);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct cpu_info
|
||||
{
|
||||
bool sse42 = false;
|
||||
|
||||
cpu_info();
|
||||
};
|
||||
|
||||
inline
|
||||
cpu_info::
|
||||
cpu_info()
|
||||
{
|
||||
constexpr std::uint32_t SSE42 = 1 << 20;
|
||||
|
||||
std::uint32_t eax = 0;
|
||||
std::uint32_t ebx = 0;
|
||||
std::uint32_t ecx = 0;
|
||||
std::uint32_t edx = 0;
|
||||
|
||||
cpuid(0, eax, ebx, ecx, edx);
|
||||
if(eax >= 1)
|
||||
{
|
||||
cpuid(1, eax, ebx, ecx, edx);
|
||||
sse42 = (ecx & SSE42) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
cpu_info const&
|
||||
get_cpu_info()
|
||||
{
|
||||
static cpu_info const ci;
|
||||
return ci;
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,53 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_GET_LOWEST_LAYER_HPP
|
||||
#define BEAST_DETAIL_GET_LOWEST_LAYER_HPP
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template<class T>
|
||||
class has_lowest_layer
|
||||
{
|
||||
template<class U, class R =
|
||||
typename U::lowest_layer_type>
|
||||
static std::true_type check(int);
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<T>(0));
|
||||
public:
|
||||
static bool constexpr value = type::value;
|
||||
};
|
||||
|
||||
template<class T, bool B>
|
||||
struct maybe_get_lowest_layer
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct maybe_get_lowest_layer<T, true>
|
||||
{
|
||||
using type = typename T::lowest_layer_type;
|
||||
};
|
||||
|
||||
// If T has a nested type lowest_layer_type,
|
||||
// returns that, else returns T.
|
||||
template<class T>
|
||||
struct get_lowest_layer
|
||||
{
|
||||
using type = typename maybe_get_lowest_layer<T,
|
||||
has_lowest_layer<T>::value>::type;
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
41
include/beast/core/detail/in_place_init.hpp
Normal file
41
include/beast/core/detail/in_place_init.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_IN_PLACE_INIT_HPP
|
||||
#define BEAST_DETAIL_IN_PLACE_INIT_HPP
|
||||
|
||||
#include <boost/version.hpp>
|
||||
#include <boost/optional/optional.hpp>
|
||||
|
||||
// Provide boost::in_place_init_t and boost::in_place_init
|
||||
// for Boost versions earlier than 1.63.0.
|
||||
|
||||
#if BOOST_VERSION < 106300
|
||||
|
||||
namespace boost {
|
||||
|
||||
namespace optional_ns {
|
||||
|
||||
// a tag for in-place initialization of contained value
|
||||
struct in_place_init_t
|
||||
{
|
||||
struct init_tag{};
|
||||
explicit in_place_init_t(init_tag){}
|
||||
};
|
||||
const in_place_init_t in_place_init ((in_place_init_t::init_tag()));
|
||||
|
||||
} // namespace optional_ns
|
||||
|
||||
using optional_ns::in_place_init_t;
|
||||
using optional_ns::in_place_init;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_INTEGER_SEQUENCE_H_INCLUDED
|
||||
#define BEAST_DETAIL_INTEGER_SEQUENCE_H_INCLUDED
|
||||
#ifndef BEAST_DETAIL_INTEGER_SEQUENCE_HPP
|
||||
#define BEAST_DETAIL_INTEGER_SEQUENCE_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
@@ -19,8 +20,7 @@ template<class T, T... Ints>
|
||||
struct integer_sequence
|
||||
{
|
||||
using value_type = T;
|
||||
static_assert (std::is_integral<T>::value,
|
||||
"std::integer_sequence can only be instantiated with an integral type" );
|
||||
BOOST_STATIC_ASSERT(std::is_integral<T>::value);
|
||||
|
||||
static std::size_t constexpr static_size = sizeof...(Ints);
|
||||
|
||||
@@ -40,9 +40,9 @@ struct sizeof_workaround
|
||||
static std::size_t constexpr size = sizeof... (Args);
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#ifdef BOOST_MSVC
|
||||
|
||||
// This implementation compiles on MSVC and clang but not gcc
|
||||
// This implementation compiles on real MSVC and clang but not gcc
|
||||
|
||||
template<class T, unsigned long long N, class Seq>
|
||||
struct make_integer_sequence_unchecked;
|
||||
@@ -65,11 +65,8 @@ struct make_integer_sequence_unchecked<
|
||||
template<class T, T N>
|
||||
struct make_integer_sequence_checked
|
||||
{
|
||||
static_assert (std::is_integral<T>::value,
|
||||
"T must be an integral type");
|
||||
|
||||
static_assert (N >= 0,
|
||||
"N must be non-negative");
|
||||
BOOST_STATIC_ASSERT(std::is_integral<T>::value);
|
||||
BOOST_STATIC_ASSERT(N >= 0);
|
||||
|
||||
using type = typename make_integer_sequence_unchecked<
|
||||
T, N, integer_sequence<T>>::type;
|
||||
@@ -117,11 +114,8 @@ struct integer_sequence_helper;
|
||||
template<class T, T N, std::size_t... Ints>
|
||||
struct integer_sequence_helper<T, N, index_tuple<Ints...>>
|
||||
{
|
||||
static_assert (std::is_integral<T>::value,
|
||||
"T must be an integral type");
|
||||
|
||||
static_assert (N >= 0,
|
||||
"N must be non-negative");
|
||||
BOOST_STATIC_ASSERT(std::is_integral<T>::value);
|
||||
BOOST_STATIC_ASSERT(N >= 0);
|
||||
|
||||
using type = integer_sequence<T, static_cast<T> (Ints)...>;
|
||||
};
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_IS_CALL_POSSIBLE_HPP
|
||||
#define BEAST_DETAIL_IS_CALL_POSSIBLE_HPP
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template<class R, class C, class ...A>
|
||||
auto
|
||||
is_call_possible_test(C&& c, int, A&& ...a)
|
||||
-> decltype(std::is_convertible<
|
||||
decltype(c(a...)), R>::value ||
|
||||
std::is_same<R, void>::value,
|
||||
std::true_type());
|
||||
|
||||
template<class R, class C, class ...A>
|
||||
std::false_type
|
||||
is_call_possible_test(C&& c, long, A&& ...a);
|
||||
|
||||
/** Metafunction returns `true` if F callable as R(A...)
|
||||
|
||||
Example:
|
||||
|
||||
@code
|
||||
is_call_possible<T, void(std::string)>
|
||||
@endcode
|
||||
*/
|
||||
/** @{ */
|
||||
template<class C, class F>
|
||||
struct is_call_possible
|
||||
: std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template<class C, class R, class ...A>
|
||||
struct is_call_possible<C, R(A...)>
|
||||
: decltype(is_call_possible_test<R>(
|
||||
std::declval<C>(), 1, std::declval<A>()...))
|
||||
{
|
||||
};
|
||||
/** @} */
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
318
include/beast/core/detail/ostream.hpp
Normal file
318
include/beast/core/detail/ostream.hpp
Normal file
@@ -0,0 +1,318 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_OSTREAM_HPP
|
||||
#define BEAST_DETAIL_OSTREAM_HPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <beast/core/read_size.hpp>
|
||||
#include <memory>
|
||||
#include <iosfwd>
|
||||
#include <streambuf>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template<class Buffers>
|
||||
class buffers_helper
|
||||
{
|
||||
Buffers b_;
|
||||
|
||||
public:
|
||||
explicit
|
||||
buffers_helper(Buffers const& b)
|
||||
: b_(b)
|
||||
{
|
||||
}
|
||||
|
||||
template<class B>
|
||||
friend
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os,
|
||||
buffers_helper<B> const& v);
|
||||
};
|
||||
|
||||
template<class Buffers>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os,
|
||||
buffers_helper<Buffers> const& v)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
for(boost::asio::const_buffer b : v.b_)
|
||||
os.write(buffer_cast<char const*>(b),
|
||||
buffer_size(b));
|
||||
return os;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct basic_streambuf_movable_helper :
|
||||
std::basic_streambuf<char, std::char_traits<char>>
|
||||
{
|
||||
basic_streambuf_movable_helper(
|
||||
basic_streambuf_movable_helper&&) = default;
|
||||
};
|
||||
|
||||
using basic_streambuf_movable =
|
||||
std::is_move_constructible<basic_streambuf_movable_helper>;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class DynamicBuffer,
|
||||
class CharT, class Traits, bool isMovable>
|
||||
class ostream_buffer;
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
class ostream_buffer
|
||||
<DynamicBuffer, CharT, Traits, true>
|
||||
: public std::basic_streambuf<CharT, Traits>
|
||||
{
|
||||
using int_type = typename
|
||||
std::basic_streambuf<CharT, Traits>::int_type;
|
||||
|
||||
using traits_type = typename
|
||||
std::basic_streambuf<CharT, Traits>::traits_type;
|
||||
|
||||
static std::size_t constexpr max_size = 512;
|
||||
|
||||
DynamicBuffer& buf_;
|
||||
|
||||
public:
|
||||
ostream_buffer(ostream_buffer&&) = default;
|
||||
ostream_buffer(ostream_buffer const&) = delete;
|
||||
|
||||
~ostream_buffer() noexcept
|
||||
{
|
||||
sync();
|
||||
}
|
||||
|
||||
explicit
|
||||
ostream_buffer(DynamicBuffer& buf)
|
||||
: buf_(buf)
|
||||
{
|
||||
prepare();
|
||||
}
|
||||
|
||||
int_type
|
||||
overflow(int_type ch) override
|
||||
{
|
||||
if(! Traits::eq_int_type(ch, Traits::eof()))
|
||||
{
|
||||
Traits::assign(*this->pptr(),
|
||||
static_cast<CharT>(ch));
|
||||
flush(1);
|
||||
prepare();
|
||||
return ch;
|
||||
}
|
||||
flush();
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
int
|
||||
sync() override
|
||||
{
|
||||
flush();
|
||||
prepare();
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
prepare()
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
auto mbs = buf_.prepare(
|
||||
read_size_or_throw(buf_, max_size));
|
||||
auto const mb = *mbs.begin();
|
||||
auto const p = buffer_cast<CharT*>(mb);
|
||||
this->setp(p,
|
||||
p + buffer_size(mb) / sizeof(CharT) - 1);
|
||||
}
|
||||
|
||||
void
|
||||
flush(int extra = 0)
|
||||
{
|
||||
buf_.commit(
|
||||
(this->pptr() - this->pbase() + extra) *
|
||||
sizeof(CharT));
|
||||
}
|
||||
};
|
||||
|
||||
// This nonsense is all to work around a glitch in libstdc++
|
||||
// where std::basic_streambuf copy constructor is private:
|
||||
// https://github.com/gcc-mirror/gcc/blob/gcc-4_8-branch/libstdc%2B%2B-v3/include/std/streambuf#L799
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
class ostream_buffer
|
||||
<DynamicBuffer, CharT, Traits, false>
|
||||
: public std::basic_streambuf<CharT, Traits>
|
||||
{
|
||||
using int_type = typename
|
||||
std::basic_streambuf<CharT, Traits>::int_type;
|
||||
|
||||
using traits_type = typename
|
||||
std::basic_streambuf<CharT, Traits>::traits_type;
|
||||
|
||||
static std::size_t constexpr max_size = 512;
|
||||
|
||||
DynamicBuffer& buf_;
|
||||
|
||||
public:
|
||||
ostream_buffer(ostream_buffer&&) = delete;
|
||||
ostream_buffer(ostream_buffer const&) = delete;
|
||||
|
||||
~ostream_buffer() noexcept
|
||||
{
|
||||
sync();
|
||||
}
|
||||
|
||||
explicit
|
||||
ostream_buffer(DynamicBuffer& buf)
|
||||
: buf_(buf)
|
||||
{
|
||||
prepare();
|
||||
}
|
||||
|
||||
int_type
|
||||
overflow(int_type ch) override
|
||||
{
|
||||
if(! Traits::eq_int_type(ch, Traits::eof()))
|
||||
{
|
||||
Traits::assign(*this->pptr(),
|
||||
static_cast<CharT>(ch));
|
||||
flush(1);
|
||||
prepare();
|
||||
return ch;
|
||||
}
|
||||
flush();
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
int
|
||||
sync() override
|
||||
{
|
||||
flush();
|
||||
prepare();
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
prepare()
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
auto mbs = buf_.prepare(
|
||||
read_size_or_throw(buf_, max_size));
|
||||
auto const mb = *mbs.begin();
|
||||
auto const p = buffer_cast<CharT*>(mb);
|
||||
this->setp(p,
|
||||
p + buffer_size(mb) / sizeof(CharT) - 1);
|
||||
}
|
||||
|
||||
void
|
||||
flush(int extra = 0)
|
||||
{
|
||||
buf_.commit(
|
||||
(this->pptr() - this->pbase() + extra) *
|
||||
sizeof(CharT));
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class DynamicBuffer,
|
||||
class CharT, class Traits, bool isMovable>
|
||||
class ostream_helper;
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
class ostream_helper<
|
||||
DynamicBuffer, CharT, Traits, true>
|
||||
: public std::basic_ostream<CharT, Traits>
|
||||
{
|
||||
ostream_buffer<
|
||||
DynamicBuffer, CharT, Traits, true> osb_;
|
||||
|
||||
public:
|
||||
explicit
|
||||
ostream_helper(DynamicBuffer& buf);
|
||||
|
||||
ostream_helper(ostream_helper&& other);
|
||||
};
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
ostream_helper<DynamicBuffer, CharT, Traits, true>::
|
||||
ostream_helper(DynamicBuffer& buf)
|
||||
: std::basic_ostream<CharT, Traits>(
|
||||
&this->osb_)
|
||||
, osb_(buf)
|
||||
{
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
ostream_helper<DynamicBuffer, CharT, Traits, true>::
|
||||
ostream_helper(
|
||||
ostream_helper&& other)
|
||||
: std::basic_ostream<CharT, Traits>(&osb_)
|
||||
, osb_(std::move(other.osb_))
|
||||
{
|
||||
}
|
||||
|
||||
// This work-around is for libstdc++ versions that
|
||||
// don't have a movable std::basic_streambuf
|
||||
|
||||
template<class T>
|
||||
class ostream_helper_base
|
||||
{
|
||||
protected:
|
||||
std::unique_ptr<T> member;
|
||||
|
||||
ostream_helper_base(
|
||||
ostream_helper_base&&) = default;
|
||||
|
||||
explicit
|
||||
ostream_helper_base(T* t)
|
||||
: member(t)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
class ostream_helper<
|
||||
DynamicBuffer, CharT, Traits, false>
|
||||
: private ostream_helper_base<ostream_buffer<
|
||||
DynamicBuffer, CharT, Traits, false>>
|
||||
, public std::basic_ostream<CharT, Traits>
|
||||
{
|
||||
public:
|
||||
explicit
|
||||
ostream_helper(DynamicBuffer& buf)
|
||||
: ostream_helper_base<ostream_buffer<
|
||||
DynamicBuffer, CharT, Traits, false>>(
|
||||
new ostream_buffer<DynamicBuffer,
|
||||
CharT, Traits, false>(buf))
|
||||
, std::basic_ostream<CharT, Traits>(this->member.get())
|
||||
{
|
||||
}
|
||||
|
||||
ostream_helper(ostream_helper&& other)
|
||||
: ostream_helper_base<ostream_buffer<
|
||||
DynamicBuffer, CharT, Traits, false>>(
|
||||
std::move(other))
|
||||
, std::basic_ostream<CharT, Traits>(this->member.get())
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
138
include/beast/core/detail/static_ostream.hpp
Normal file
138
include/beast/core/detail/static_ostream.hpp
Normal file
@@ -0,0 +1,138 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_STATIC_OSTREAM_HPP
|
||||
#define BEAST_DETAIL_STATIC_OSTREAM_HPP
|
||||
|
||||
#include <locale>
|
||||
#include <ostream>
|
||||
#include <streambuf>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
// http://www.mr-edd.co.uk/blog/beginners_guide_streambuf
|
||||
|
||||
class static_ostream_buffer
|
||||
: public std::basic_streambuf<char>
|
||||
{
|
||||
using CharT = char;
|
||||
using Traits = std::char_traits<CharT>;
|
||||
using int_type = typename
|
||||
std::basic_streambuf<CharT, Traits>::int_type;
|
||||
using traits_type = typename
|
||||
std::basic_streambuf<CharT, Traits>::traits_type;
|
||||
|
||||
char* data_;
|
||||
std::size_t size_;
|
||||
std::size_t len_ = 0;
|
||||
std::string s_;
|
||||
|
||||
public:
|
||||
static_ostream_buffer(static_ostream_buffer&&) = delete;
|
||||
static_ostream_buffer(static_ostream_buffer const&) = delete;
|
||||
|
||||
static_ostream_buffer(char* data, std::size_t size)
|
||||
: data_(data)
|
||||
, size_(size)
|
||||
{
|
||||
this->setp(data_, data_ + size - 1);
|
||||
}
|
||||
|
||||
~static_ostream_buffer() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
string_view
|
||||
str() const
|
||||
{
|
||||
if(! s_.empty())
|
||||
return {s_.data(), len_};
|
||||
return {data_, len_};
|
||||
}
|
||||
|
||||
int_type
|
||||
overflow(int_type ch) override
|
||||
{
|
||||
if(! Traits::eq_int_type(ch, Traits::eof()))
|
||||
{
|
||||
Traits::assign(*this->pptr(),
|
||||
static_cast<CharT>(ch));
|
||||
flush(1);
|
||||
prepare();
|
||||
return ch;
|
||||
}
|
||||
flush();
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
int
|
||||
sync() override
|
||||
{
|
||||
flush();
|
||||
prepare();
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
prepare()
|
||||
{
|
||||
static auto const growth_factor = 1.5;
|
||||
|
||||
if(len_ < size_ - 1)
|
||||
{
|
||||
this->setp(
|
||||
data_ + len_, data_ + size_ - 2);
|
||||
return;
|
||||
}
|
||||
if(s_.empty())
|
||||
{
|
||||
s_.resize(static_cast<std::size_t>(
|
||||
growth_factor * len_));
|
||||
Traits::copy(&s_[0], data_, len_);
|
||||
}
|
||||
else
|
||||
{
|
||||
s_.resize(static_cast<std::size_t>(
|
||||
growth_factor * len_));
|
||||
}
|
||||
this->setp(&s_[len_], &s_[len_] +
|
||||
s_.size() - len_ - 1);
|
||||
}
|
||||
|
||||
void
|
||||
flush(int extra = 0)
|
||||
{
|
||||
len_ += static_cast<std::size_t>(
|
||||
this->pptr() - this->pbase() + extra);
|
||||
}
|
||||
};
|
||||
|
||||
class static_ostream : public std::basic_ostream<char>
|
||||
{
|
||||
static_ostream_buffer osb_;
|
||||
|
||||
public:
|
||||
static_ostream(char* data, std::size_t size)
|
||||
: std::basic_ostream<char>(&this->osb_)
|
||||
, osb_(data, size)
|
||||
{
|
||||
imbue(std::locale::classic());
|
||||
}
|
||||
|
||||
string_view
|
||||
str() const
|
||||
{
|
||||
return osb_.str();
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
131
include/beast/core/detail/static_string.hpp
Normal file
131
include/beast/core/detail/static_string.hpp
Normal file
@@ -0,0 +1,131 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_STATIC_STRING_HPP
|
||||
#define BEAST_DETAIL_STATIC_STRING_HPP
|
||||
|
||||
#include <beast/core/string.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
// Because k-ballo said so
|
||||
template<class T>
|
||||
using is_input_iterator =
|
||||
std::integral_constant<bool,
|
||||
! std::is_integral<T>::value>;
|
||||
|
||||
template<class CharT, class Traits>
|
||||
int
|
||||
lexicographical_compare(
|
||||
CharT const* s1, std::size_t n1,
|
||||
CharT const* s2, std::size_t n2)
|
||||
{
|
||||
if(n1 < n2)
|
||||
return Traits::compare(
|
||||
s1, s2, n1) <= 0 ? -1 : 1;
|
||||
if(n1 > n2)
|
||||
return Traits::compare(
|
||||
s1, s2, n2) >= 0 ? 1 : -1;
|
||||
return Traits::compare(s1, s2, n1);
|
||||
}
|
||||
|
||||
template<class CharT, class Traits>
|
||||
inline
|
||||
int
|
||||
lexicographical_compare(
|
||||
basic_string_view<CharT, Traits> s1,
|
||||
CharT const* s2, std::size_t n2)
|
||||
{
|
||||
return lexicographical_compare<CharT, Traits>(
|
||||
s1.data(), s1.size(), s2, n2);
|
||||
}
|
||||
|
||||
template<class CharT, class Traits>
|
||||
inline
|
||||
int
|
||||
lexicographical_compare(
|
||||
basic_string_view<CharT, Traits> s1,
|
||||
basic_string_view<CharT, Traits> s2)
|
||||
{
|
||||
return lexicographical_compare<CharT, Traits>(
|
||||
s1.data(), s1.size(), s2.data(), s2.size());
|
||||
}
|
||||
|
||||
// Maximum number of characters in the decimal
|
||||
// representation of a binary number. This includes
|
||||
// the potential minus sign.
|
||||
//
|
||||
inline
|
||||
std::size_t constexpr
|
||||
max_digits(std::size_t bytes)
|
||||
{
|
||||
return static_cast<std::size_t>(
|
||||
bytes * 2.41) + 1 + 1;
|
||||
}
|
||||
|
||||
template<class CharT, class Integer, class Traits>
|
||||
CharT*
|
||||
raw_to_string(
|
||||
CharT* buf, Integer x, std::true_type)
|
||||
{
|
||||
if(x == 0)
|
||||
{
|
||||
Traits::assign(*--buf, '0');
|
||||
return buf;
|
||||
}
|
||||
if(x < 0)
|
||||
{
|
||||
x = -x;
|
||||
for(;x > 0; x /= 10)
|
||||
Traits::assign(*--buf ,
|
||||
"0123456789"[x % 10]);
|
||||
Traits::assign(*--buf, '-');
|
||||
return buf;
|
||||
}
|
||||
for(;x > 0; x /= 10)
|
||||
Traits::assign(*--buf ,
|
||||
"0123456789"[x % 10]);
|
||||
return buf;
|
||||
}
|
||||
|
||||
template<class CharT, class Integer, class Traits>
|
||||
CharT*
|
||||
raw_to_string(
|
||||
CharT* buf, Integer x, std::false_type)
|
||||
{
|
||||
if(x == 0)
|
||||
{
|
||||
*--buf = '0';
|
||||
return buf;
|
||||
}
|
||||
for(;x > 0; x /= 10)
|
||||
Traits::assign(*--buf ,
|
||||
"0123456789"[x % 10]);
|
||||
return buf;
|
||||
}
|
||||
|
||||
template<
|
||||
class CharT,
|
||||
class Integer,
|
||||
class Traits = std::char_traits<CharT>>
|
||||
CharT*
|
||||
raw_to_string(CharT* last, std::size_t size, Integer i)
|
||||
{
|
||||
boost::ignore_unused(size);
|
||||
BOOST_ASSERT(size >= max_digits(sizeof(Integer)));
|
||||
return raw_to_string<CharT, Integer, Traits>(
|
||||
last, i, std::is_signed<Integer>{});
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -1,134 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_STREAM_CONCEPTS_HPP
|
||||
#define BEAST_DETAIL_STREAM_CONCEPTS_HPP
|
||||
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
// Types that meet the requirements,
|
||||
// for use with std::declval only.
|
||||
struct StreamHandler
|
||||
{
|
||||
StreamHandler(StreamHandler const&) = default;
|
||||
void operator()(error_code ec, std::size_t);
|
||||
};
|
||||
using ReadHandler = StreamHandler;
|
||||
using WriteHandler = StreamHandler;
|
||||
|
||||
template<class T>
|
||||
class has_get_io_service
|
||||
{
|
||||
template<class U, class R = typename std::is_same<
|
||||
decltype(std::declval<U>().get_io_service()),
|
||||
boost::asio::io_service&>>
|
||||
static R check(int);
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
public:
|
||||
using type = decltype(check<T>(0));
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class is_AsyncReadStream
|
||||
{
|
||||
template<class U, class R = decltype(
|
||||
std::declval<U>().async_read_some(
|
||||
std::declval<MutableBufferSequence>(),
|
||||
std::declval<ReadHandler>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type1 = decltype(check<T>(0));
|
||||
public:
|
||||
using type = std::integral_constant<bool,
|
||||
type1::value &&
|
||||
has_get_io_service<T>::type::value>;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class is_AsyncWriteStream
|
||||
{
|
||||
template<class U, class R = decltype(
|
||||
std::declval<U>().async_write_some(
|
||||
std::declval<ConstBufferSequence>(),
|
||||
std::declval<WriteHandler>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type1 = decltype(check<T>(0));
|
||||
public:
|
||||
using type = std::integral_constant<bool,
|
||||
type1::value &&
|
||||
has_get_io_service<T>::type::value>;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class is_SyncReadStream
|
||||
{
|
||||
template<class U, class R = std::is_same<decltype(
|
||||
std::declval<U>().read_some(
|
||||
std::declval<MutableBufferSequence>())),
|
||||
std::size_t>>
|
||||
static R check1(int);
|
||||
template<class>
|
||||
static std::false_type check1(...);
|
||||
using type1 = decltype(check1<T>(0));
|
||||
|
||||
template<class U, class R = std::is_same<decltype(
|
||||
std::declval<U>().read_some(
|
||||
std::declval<MutableBufferSequence>(),
|
||||
std::declval<error_code&>())), std::size_t>>
|
||||
static R check2(int);
|
||||
template<class>
|
||||
static std::false_type check2(...);
|
||||
using type2 = decltype(check2<T>(0));
|
||||
|
||||
public:
|
||||
using type = std::integral_constant<bool,
|
||||
type1::value && type2::value>;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class is_SyncWriteStream
|
||||
{
|
||||
template<class U, class R = std::is_same<decltype(
|
||||
std::declval<U>().write_some(
|
||||
std::declval<ConstBufferSequence>())),
|
||||
std::size_t>>
|
||||
static R check1(int);
|
||||
template<class>
|
||||
static std::false_type check1(...);
|
||||
using type1 = decltype(check1<T>(0));
|
||||
|
||||
template<class U, class R = std::is_same<decltype(
|
||||
std::declval<U>().write_some(
|
||||
std::declval<ConstBufferSequence>(),
|
||||
std::declval<error_code&>())), std::size_t>>
|
||||
static R check2(int);
|
||||
template<class>
|
||||
static std::false_type check2(...);
|
||||
using type2 = decltype(check2<T>(0));
|
||||
|
||||
public:
|
||||
using type = std::integral_constant<bool,
|
||||
type1::value && type2::value>;
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -1,93 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_SYNC_OSTREAM_HPP
|
||||
#define BEAST_DETAIL_SYNC_OSTREAM_HPP
|
||||
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <ostream>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
/** A SyncWriteStream which outputs to a `std::ostream`
|
||||
|
||||
Objects of this type meet the requirements of @b SyncWriteStream.
|
||||
*/
|
||||
class sync_ostream
|
||||
{
|
||||
std::ostream& os_;
|
||||
|
||||
public:
|
||||
/** Construct the stream.
|
||||
|
||||
@param os The associated `std::ostream`. All buffers
|
||||
written will be passed to the associated output stream.
|
||||
*/
|
||||
sync_ostream(std::ostream& os)
|
||||
: os_(os)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
write_some(ConstBufferSequence const& buffers);
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
write_some(ConstBufferSequence const& buffers,
|
||||
error_code& ec);
|
||||
};
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
sync_ostream::
|
||||
write_some(ConstBufferSequence const& buffers)
|
||||
{
|
||||
static_assert(
|
||||
is_ConstBufferSequence<ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
error_code ec;
|
||||
auto const n = write_some(buffers, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
return n;
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
sync_ostream::
|
||||
write_some(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(
|
||||
is_ConstBufferSequence<ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
std::size_t n = 0;
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
for(auto const& buffer : buffers)
|
||||
{
|
||||
os_.write(buffer_cast<char const*>(buffer),
|
||||
buffer_size(buffer));
|
||||
if(os_.fail())
|
||||
{
|
||||
ec = errc::make_error_code(
|
||||
errc::no_stream_resources);
|
||||
break;
|
||||
}
|
||||
n += buffer_size(buffer);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -8,14 +8,46 @@
|
||||
#ifndef BEAST_DETAIL_TYPE_TRAITS_HPP
|
||||
#define BEAST_DETAIL_TYPE_TRAITS_HPP
|
||||
|
||||
#include <beast/core/error.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
// A few workarounds to keep things working
|
||||
|
||||
namespace boost {
|
||||
namespace asio {
|
||||
|
||||
// for has_get_io_service
|
||||
class io_service;
|
||||
|
||||
// for is_dynamic_buffer
|
||||
template<class Allocator>
|
||||
class basic_streambuf;
|
||||
|
||||
namespace detail {
|
||||
|
||||
// for is_buffer_sequence
|
||||
template<class Buffer, class Buffers>
|
||||
class consuming_buffers;
|
||||
|
||||
} // detail
|
||||
|
||||
} // asio
|
||||
} // boost
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
//
|
||||
// utilities
|
||||
//
|
||||
|
||||
template<class... Ts>
|
||||
struct make_void
|
||||
{
|
||||
@@ -25,18 +57,10 @@ struct make_void
|
||||
template<class... Ts>
|
||||
using void_t = typename make_void<Ts...>::type;
|
||||
|
||||
template<class... Ts>
|
||||
template<class T>
|
||||
inline
|
||||
void
|
||||
ignore_unused(Ts const& ...)
|
||||
{
|
||||
}
|
||||
|
||||
template<class... Ts>
|
||||
inline
|
||||
void
|
||||
ignore_unused()
|
||||
{}
|
||||
accept_rv(T){}
|
||||
|
||||
template<class U>
|
||||
std::size_t constexpr
|
||||
@@ -80,17 +104,159 @@ struct repeat_tuple<0, T>
|
||||
using type = std::tuple<>;
|
||||
};
|
||||
|
||||
template<class Exception>
|
||||
Exception
|
||||
make_exception(char const* reason, char const* file, int line)
|
||||
template<class R, class C, class ...A>
|
||||
auto
|
||||
is_invocable_test(C&& c, int, A&& ...a)
|
||||
-> decltype(std::is_convertible<
|
||||
decltype(c(a...)), R>::value ||
|
||||
std::is_same<R, void>::value,
|
||||
std::true_type());
|
||||
|
||||
template<class R, class C, class ...A>
|
||||
std::false_type
|
||||
is_invocable_test(C&& c, long, A&& ...a);
|
||||
|
||||
/** Metafunction returns `true` if F callable as R(A...)
|
||||
|
||||
Example:
|
||||
|
||||
@code
|
||||
is_invocable<T, void(std::string)>
|
||||
@endcode
|
||||
*/
|
||||
/** @{ */
|
||||
template<class C, class F>
|
||||
struct is_invocable : std::false_type
|
||||
{
|
||||
char const* n = file;
|
||||
for(auto p = file; *p; ++p)
|
||||
if(*p == '\\' || *p == '/')
|
||||
n = p + 1;
|
||||
return Exception{std::string(reason) + " (" +
|
||||
n + ":" + std::to_string(line) + ")"};
|
||||
}
|
||||
};
|
||||
|
||||
template<class C, class R, class ...A>
|
||||
struct is_invocable<C, R(A...)>
|
||||
: decltype(is_invocable_test<R>(
|
||||
std::declval<C>(), 1, std::declval<A>()...))
|
||||
{
|
||||
};
|
||||
/** @} */
|
||||
|
||||
// for span
|
||||
template<class T, class E, class = void>
|
||||
struct is_contiguous_container: std::false_type {};
|
||||
|
||||
template<class T, class E>
|
||||
struct is_contiguous_container<T, E, void_t<
|
||||
decltype(
|
||||
std::declval<std::size_t&>() = std::declval<T const&>().size(),
|
||||
std::declval<E*&>() = std::declval<T&>().data(),
|
||||
(void)0),
|
||||
typename std::enable_if<
|
||||
std::is_same<
|
||||
typename std::remove_cv<E>::type,
|
||||
typename std::remove_cv<
|
||||
typename std::remove_pointer<
|
||||
decltype(std::declval<T&>().data())
|
||||
>::type
|
||||
>::type
|
||||
>::value
|
||||
>::type>>: std::true_type
|
||||
{};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// buffer concepts
|
||||
//
|
||||
|
||||
// Types that meet the requirements,
|
||||
// for use with std::declval only.
|
||||
template<class BufferType>
|
||||
struct BufferSequence
|
||||
{
|
||||
using value_type = BufferType;
|
||||
using const_iterator = BufferType const*;
|
||||
~BufferSequence();
|
||||
BufferSequence(BufferSequence const&) = default;
|
||||
const_iterator begin() const noexcept;
|
||||
const_iterator end() const noexcept;
|
||||
};
|
||||
using ConstBufferSequence =
|
||||
BufferSequence<boost::asio::const_buffer>;
|
||||
using MutableBufferSequence =
|
||||
BufferSequence<boost::asio::mutable_buffer>;
|
||||
|
||||
template<class T, class B, class = void>
|
||||
struct is_buffer_sequence : std::false_type {};
|
||||
|
||||
template<class T, class B>
|
||||
struct is_buffer_sequence<T, B, void_t<decltype(
|
||||
std::declval<typename T::value_type>(),
|
||||
std::declval<typename T::const_iterator&>() =
|
||||
std::declval<T const&>().begin(),
|
||||
std::declval<typename T::const_iterator&>() =
|
||||
std::declval<T const&>().end(),
|
||||
(void)0)>> : std::integral_constant<bool,
|
||||
std::is_convertible<typename T::value_type, B>::value &&
|
||||
#if 0
|
||||
std::is_base_of<std::bidirectional_iterator_tag,
|
||||
typename std::iterator_traits<
|
||||
typename T::const_iterator>::iterator_category>::value
|
||||
#else
|
||||
// workaround:
|
||||
// boost::asio::detail::consuming_buffers::const_iterator
|
||||
// is not bidirectional
|
||||
std::is_base_of<std::forward_iterator_tag,
|
||||
typename std::iterator_traits<
|
||||
typename T::const_iterator>::iterator_category>::value
|
||||
#endif
|
||||
>
|
||||
{
|
||||
};
|
||||
|
||||
#if 0
|
||||
// workaround:
|
||||
// boost::asio::detail::consuming_buffers::const_iterator
|
||||
// is not bidirectional
|
||||
template<class Buffer, class Buffers, class B>
|
||||
struct is_buffer_sequence<
|
||||
boost::asio::detail::consuming_buffers<Buffer, Buffers>>
|
||||
: std::true_type
|
||||
{
|
||||
};
|
||||
#endif
|
||||
|
||||
template<class B1, class... Bn>
|
||||
struct is_all_const_buffer_sequence
|
||||
: std::integral_constant<bool,
|
||||
is_buffer_sequence<B1, boost::asio::const_buffer>::value &&
|
||||
is_all_const_buffer_sequence<Bn...>::value>
|
||||
{
|
||||
};
|
||||
|
||||
template<class B1>
|
||||
struct is_all_const_buffer_sequence<B1>
|
||||
: is_buffer_sequence<B1, boost::asio::const_buffer>
|
||||
{
|
||||
};
|
||||
|
||||
template<class... Bn>
|
||||
struct common_buffers_type
|
||||
{
|
||||
using type = typename std::conditional<
|
||||
std::is_convertible<std::tuple<Bn...>,
|
||||
typename repeat_tuple<sizeof...(Bn),
|
||||
boost::asio::mutable_buffer>::type>::value,
|
||||
boost::asio::mutable_buffer,
|
||||
boost::asio::const_buffer>::type;
|
||||
};
|
||||
|
||||
// Types that meet the requirements,
|
||||
// for use with std::declval only.
|
||||
struct StreamHandler
|
||||
{
|
||||
StreamHandler(StreamHandler const&) = default;
|
||||
void operator()(error_code ec, std::size_t);
|
||||
};
|
||||
using ReadHandler = StreamHandler;
|
||||
using WriteHandler = StreamHandler;
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_WRITE_DYNABUF_HPP
|
||||
#define BEAST_DETAIL_WRITE_DYNABUF_HPP
|
||||
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
// detects string literals.
|
||||
template<class T>
|
||||
struct is_string_literal : std::integral_constant<bool,
|
||||
! std::is_same<T, typename std::remove_extent<T>::type>::value &&
|
||||
std::is_same<char, typename std::remove_extent<T>::type>::value>
|
||||
{
|
||||
};
|
||||
|
||||
// `true` if a call to boost::asio::buffer(T const&) is possible
|
||||
// note: we exclude string literals because boost::asio::buffer()
|
||||
// will include the null terminator, which we don't want.
|
||||
template<class T>
|
||||
class is_BufferConvertible
|
||||
{
|
||||
template<class U, class R = decltype(
|
||||
boost::asio::buffer(std::declval<U const&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<T>(0));
|
||||
public:
|
||||
static bool const value = type::value &&
|
||||
! is_string_literal<T>::value;
|
||||
};
|
||||
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
write_dynabuf(DynamicBuffer& dynabuf,
|
||||
boost::asio::const_buffer const& buffer)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
dynabuf.commit(buffer_copy(
|
||||
dynabuf.prepare(buffer_size(buffer)),
|
||||
buffer));
|
||||
}
|
||||
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
write_dynabuf(DynamicBuffer& dynabuf,
|
||||
boost::asio::mutable_buffer const& buffer)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
dynabuf.commit(buffer_copy(
|
||||
dynabuf.prepare(buffer_size(buffer)),
|
||||
buffer));
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class T>
|
||||
typename std::enable_if<
|
||||
is_BufferConvertible<T>::value &&
|
||||
! std::is_convertible<T, boost::asio::const_buffer>::value &&
|
||||
! std::is_convertible<T, boost::asio::mutable_buffer>::value
|
||||
>::type
|
||||
write_dynabuf(DynamicBuffer& dynabuf, T const& t)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
auto const buffers = boost::asio::buffer(t);
|
||||
dynabuf.commit(buffer_copy(
|
||||
dynabuf.prepare(buffer_size(buffers)),
|
||||
buffers));
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class Buffers>
|
||||
typename std::enable_if<
|
||||
is_ConstBufferSequence<Buffers>::value &&
|
||||
! is_BufferConvertible<Buffers>::value &&
|
||||
! std::is_convertible<Buffers, boost::asio::const_buffer>::value &&
|
||||
! std::is_convertible<Buffers, boost::asio::mutable_buffer>::value
|
||||
>::type
|
||||
write_dynabuf(DynamicBuffer& dynabuf, Buffers const& buffers)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
dynabuf.commit(buffer_copy(
|
||||
dynabuf.prepare(buffer_size(buffers)),
|
||||
buffers));
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, std::size_t N>
|
||||
void
|
||||
write_dynabuf(DynamicBuffer& dynabuf, const char (&s)[N])
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
dynabuf.commit(buffer_copy(
|
||||
dynabuf.prepare(N - 1),
|
||||
boost::asio::buffer(s, N - 1)));
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class T>
|
||||
typename std::enable_if<
|
||||
! is_string_literal<T>::value &&
|
||||
! is_ConstBufferSequence<T>::value &&
|
||||
! is_BufferConvertible<T>::value &&
|
||||
! std::is_convertible<T, boost::asio::const_buffer>::value &&
|
||||
! std::is_convertible<T, boost::asio::mutable_buffer>::value
|
||||
>::type
|
||||
write_dynabuf(DynamicBuffer& dynabuf, T const& t)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
auto const s = boost::lexical_cast<std::string>(t);
|
||||
dynabuf.commit(buffer_copy(
|
||||
dynabuf.prepare(s.size()), buffer(s)));
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class T0, class T1, class... TN>
|
||||
void
|
||||
write_dynabuf(DynamicBuffer& dynabuf,
|
||||
T0 const& t0, T1 const& t1, TN const&... tn)
|
||||
{
|
||||
write_dynabuf(dynabuf, t0);
|
||||
write_dynabuf(dynabuf, t1, tn...);
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
122
include/beast/core/drain_buffer.hpp
Normal file
122
include/beast/core/drain_buffer.hpp
Normal file
@@ -0,0 +1,122 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DRAIN_BUFFER_HPP
|
||||
#define BEAST_DRAIN_BUFFER_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** A @b DynamicBuffer which does not retain its input sequence.
|
||||
|
||||
This object implements a dynamic buffer with a fixed size
|
||||
output area, not dynamically allocated, and whose input
|
||||
sequence is always length zero. Bytes committed from the
|
||||
output area to the input area are always discarded. This
|
||||
is useful for calling interfaces that require a dynamic
|
||||
buffer for storage, but where the caller does not want
|
||||
to retain the data.
|
||||
*/
|
||||
class drain_buffer
|
||||
{
|
||||
char buf_[512];
|
||||
std::size_t n_ = 0;
|
||||
|
||||
public:
|
||||
/// The type used to represent the input sequence as a list of buffers.
|
||||
using const_buffers_type = boost::asio::null_buffers;
|
||||
|
||||
/// The type used to represent the output sequence as a list of buffers.
|
||||
using mutable_buffers_type = boost::asio::mutable_buffers_1;
|
||||
|
||||
/// Constructor
|
||||
drain_buffer() = default;
|
||||
|
||||
/// Copy constructor
|
||||
drain_buffer(drain_buffer const&)
|
||||
{
|
||||
// Previously returned ranges are invalidated
|
||||
}
|
||||
|
||||
/// Copy assignment
|
||||
drain_buffer&
|
||||
operator=(drain_buffer const&)
|
||||
{
|
||||
n_ = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Return the size of the input sequence.
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Return the maximum sum of the input and output sequence sizes.
|
||||
std::size_t
|
||||
max_size() const
|
||||
{
|
||||
return sizeof(buf_);
|
||||
}
|
||||
|
||||
/// Return the maximum sum of input and output sizes that can be held without an allocation.
|
||||
std::size_t
|
||||
capacity() const
|
||||
{
|
||||
return max_size();
|
||||
}
|
||||
|
||||
/** Get a list of buffers that represent the input sequence.
|
||||
|
||||
@note These buffers remain valid across subsequent calls to `prepare`.
|
||||
*/
|
||||
const_buffers_type
|
||||
data() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
/** Get a list of buffers that represent the output sequence, with the given size.
|
||||
|
||||
@throws std::length_error if the size would exceed the buffer limit
|
||||
*/
|
||||
mutable_buffers_type
|
||||
prepare(std::size_t n)
|
||||
{
|
||||
if(n > sizeof(buf_))
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"buffer overflow"});
|
||||
n_ = n;
|
||||
return {buf_, n_};
|
||||
}
|
||||
|
||||
/** Move bytes from the output sequence to the input sequence.
|
||||
|
||||
This call always discards the output sequence.
|
||||
The size of the input sequence will remain at zero.
|
||||
*/
|
||||
void
|
||||
commit(std::size_t)
|
||||
{
|
||||
}
|
||||
|
||||
/** Remove bytes from the input sequence.
|
||||
|
||||
This call has no effect.
|
||||
*/
|
||||
void
|
||||
consume(std::size_t) const
|
||||
{
|
||||
}
|
||||
};
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -23,8 +23,16 @@ using system_error = boost::system::system_error;
|
||||
/// The type of error category used by the library
|
||||
using error_category = boost::system::error_category;
|
||||
|
||||
/// A function to return the generic error category used by the library
|
||||
#if BEAST_DOXYGEN
|
||||
error_category const&
|
||||
generic_category();
|
||||
#else
|
||||
using boost::system::generic_category;
|
||||
#endif
|
||||
|
||||
/// A function to return the system error category used by the library
|
||||
#if GENERATING_DOCS
|
||||
#if BEAST_DOXYGEN
|
||||
error_category const&
|
||||
system_category();
|
||||
#else
|
||||
@@ -35,7 +43,7 @@ using boost::system::system_category;
|
||||
using error_condition = boost::system::error_condition;
|
||||
|
||||
/// The set of constants used for cross-platform error codes
|
||||
#if GENERATING_DOCS
|
||||
#if BEAST_DOXYGEN
|
||||
enum errc{};
|
||||
#else
|
||||
namespace errc = boost::system::errc;
|
||||
|
||||
41
include/beast/core/file.hpp
Normal file
41
include/beast/core/file.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_CORE_FILE_HPP
|
||||
#define BEAST_CORE_FILE_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/file_base.hpp>
|
||||
#include <beast/core/file_posix.hpp>
|
||||
#include <beast/core/file_stdio.hpp>
|
||||
#include <beast/core/file_win32.hpp>
|
||||
#include <boost/config.hpp>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** An implementation of File.
|
||||
|
||||
This alias is set to the best available implementation
|
||||
of @b File given the platform and build settings.
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
struct file : file_stdio
|
||||
{
|
||||
};
|
||||
#else
|
||||
#if BEAST_USE_WIN32_FILE
|
||||
using file = file_win32;
|
||||
#elif BEAST_USE_POSIX_FILE
|
||||
using file = file_posix;
|
||||
#else
|
||||
using file = file_stdio;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
88
include/beast/core/file_base.hpp
Normal file
88
include/beast/core/file_base.hpp
Normal file
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_CORE_FILE_BASE_HPP
|
||||
#define BEAST_CORE_FILE_BASE_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/string.hpp>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/// The type of file path used by the library
|
||||
using file_path = string_view;
|
||||
|
||||
/** File open modes
|
||||
|
||||
These modes are used when opening files using
|
||||
instances of the @b File concept.
|
||||
|
||||
@see file_stdio
|
||||
*/
|
||||
enum class file_mode
|
||||
{
|
||||
/// Random reading
|
||||
read,
|
||||
|
||||
/// Sequential reading
|
||||
scan,
|
||||
|
||||
/** Random writing to a new or truncated file
|
||||
|
||||
@li If the file does not exist, it is created.
|
||||
|
||||
@li If the file exists, it is truncated to
|
||||
zero size upon opening.
|
||||
*/
|
||||
write,
|
||||
|
||||
/** Random writing to new file only
|
||||
|
||||
If the file exists, an error is generated.
|
||||
*/
|
||||
write_new,
|
||||
|
||||
/** Random writing to existing file
|
||||
|
||||
If the file does not exist, an error is generated.
|
||||
*/
|
||||
write_existing,
|
||||
|
||||
/** Appending to a new or truncated file
|
||||
|
||||
The current file position shall be set to the end of
|
||||
the file prior to each write.
|
||||
|
||||
@li If the file does not exist, it is created.
|
||||
|
||||
@li If the file exists, it is truncated to
|
||||
zero size upon opening.
|
||||
*/
|
||||
append,
|
||||
|
||||
/** Appending to a new file only
|
||||
|
||||
The current file position shall be set to the end of
|
||||
the file prior to each write.
|
||||
|
||||
If the file exists, an error is generated.
|
||||
*/
|
||||
append_new,
|
||||
|
||||
/** Appending to an existing file
|
||||
|
||||
The current file position shall be set to the end of
|
||||
the file prior to each write.
|
||||
|
||||
If the file does not exist, an error is generated.
|
||||
*/
|
||||
append_existing
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
171
include/beast/core/file_posix.hpp
Normal file
171
include/beast/core/file_posix.hpp
Normal file
@@ -0,0 +1,171 @@
|
||||
//
|
||||
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_CORE_FILE_POSIX_HPP
|
||||
#define BEAST_CORE_FILE_POSIX_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#if ! defined(BEAST_NO_POSIX_FILE)
|
||||
# if ! defined(__APPLE__) && ! defined(__linux__)
|
||||
# define BEAST_NO_POSIX_FILE
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if ! defined(BEAST_USE_POSIX_FILE)
|
||||
# if ! defined(BEAST_NO_POSIX_FILE)
|
||||
# define BEAST_USE_POSIX_FILE 1
|
||||
# else
|
||||
# define BEAST_USE_POSIX_FILE 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if BEAST_USE_POSIX_FILE
|
||||
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/file_base.hpp>
|
||||
#include <cstdint>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** An implementation of File for POSIX systems.
|
||||
|
||||
This class implements a @b File using POSIX interfaces.
|
||||
*/
|
||||
class file_posix
|
||||
{
|
||||
int fd_ = -1;
|
||||
|
||||
public:
|
||||
/** The type of the underlying file handle.
|
||||
|
||||
This is platform-specific.
|
||||
*/
|
||||
using native_handle_type = int;
|
||||
|
||||
/** Destructor
|
||||
|
||||
If the file is open it is first closed.
|
||||
*/
|
||||
~file_posix();
|
||||
|
||||
/** Constructor
|
||||
|
||||
There is no open file initially.
|
||||
*/
|
||||
file_posix() = default;
|
||||
|
||||
/** Constructor
|
||||
|
||||
The moved-from object behaves as if default constructed.
|
||||
*/
|
||||
file_posix(file_posix&& other);
|
||||
|
||||
/** Assignment
|
||||
|
||||
The moved-from object behaves as if default constructed.
|
||||
*/
|
||||
file_posix& operator=(file_posix&& other);
|
||||
|
||||
/// Returns the native handle associated with the file.
|
||||
native_handle_type
|
||||
native_handle() const
|
||||
{
|
||||
return fd_;
|
||||
}
|
||||
|
||||
/** Set the native handle associated with the file.
|
||||
|
||||
If the file is open it is first closed.
|
||||
|
||||
@param fd The native file handle to assign.
|
||||
*/
|
||||
void
|
||||
native_handle(native_handle_type fd);
|
||||
|
||||
/// Returns `true` if the file is open
|
||||
bool
|
||||
is_open() const
|
||||
{
|
||||
return fd_ != -1;
|
||||
}
|
||||
|
||||
/** Close the file if open
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
void
|
||||
close(error_code& ec);
|
||||
|
||||
/** Open a file at the given path with the specified mode
|
||||
|
||||
@param path The utf-8 encoded path to the file
|
||||
|
||||
@param mode The file mode to use
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
void
|
||||
open(char const* path, file_mode mode, error_code& ec);
|
||||
|
||||
/** Return the size of the open file
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
|
||||
@return The size in bytes
|
||||
*/
|
||||
std::uint64_t
|
||||
size(error_code& ec) const;
|
||||
|
||||
/** Return the current position in the open file
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
|
||||
@return The offset in bytes from the beginning of the file
|
||||
*/
|
||||
std::uint64_t
|
||||
pos(error_code& ec) const;
|
||||
|
||||
/** Adjust the current position in the open file
|
||||
|
||||
@param offset The offset in bytes from the beginning of the file
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
void
|
||||
seek(std::uint64_t offset, error_code& ec);
|
||||
|
||||
/** Read from the open file
|
||||
|
||||
@param buffer The buffer for storing the result of the read
|
||||
|
||||
@param n The number of bytes to read
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
std::size_t
|
||||
read(void* buffer, std::size_t n, error_code& ec) const;
|
||||
|
||||
/** Write to the open file
|
||||
|
||||
@param buffer The buffer holding the data to write
|
||||
|
||||
@param n The number of bytes to write
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
std::size_t
|
||||
write(void const* buffer, std::size_t n, error_code& ec);
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/core/impl/file_posix.ipp>
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
154
include/beast/core/file_stdio.hpp
Normal file
154
include/beast/core/file_stdio.hpp
Normal file
@@ -0,0 +1,154 @@
|
||||
//
|
||||
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_CORE_FILE_STDIO_HPP
|
||||
#define BEAST_CORE_FILE_STDIO_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/file_base.hpp>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** An implementation of File which uses cstdio.
|
||||
|
||||
This class implements a file using the interfaces present
|
||||
in the C++ Standard Library, in `<stdio>`.
|
||||
*/
|
||||
class file_stdio
|
||||
{
|
||||
FILE* f_ = nullptr;
|
||||
|
||||
public:
|
||||
/** The type of the underlying file handle.
|
||||
|
||||
This is platform-specific.
|
||||
*/
|
||||
using native_handle_type = FILE*;
|
||||
|
||||
/** Destructor
|
||||
|
||||
If the file is open it is first closed.
|
||||
*/
|
||||
~file_stdio();
|
||||
|
||||
/** Constructor
|
||||
|
||||
There is no open file initially.
|
||||
*/
|
||||
file_stdio() = default;
|
||||
|
||||
/** Constructor
|
||||
|
||||
The moved-from object behaves as if default constructed.
|
||||
*/
|
||||
file_stdio(file_stdio&& other);
|
||||
|
||||
/** Assignment
|
||||
|
||||
The moved-from object behaves as if default constructed.
|
||||
*/
|
||||
file_stdio& operator=(file_stdio&& other);
|
||||
|
||||
/// Returns the native handle associated with the file.
|
||||
FILE*
|
||||
native_handle() const
|
||||
{
|
||||
return f_;
|
||||
}
|
||||
|
||||
/** Set the native handle associated with the file.
|
||||
|
||||
If the file is open it is first closed.
|
||||
|
||||
@param f The native file handle to assign.
|
||||
*/
|
||||
void
|
||||
native_handle(FILE* f);
|
||||
|
||||
/// Returns `true` if the file is open
|
||||
bool
|
||||
is_open() const
|
||||
{
|
||||
return f_ != nullptr;
|
||||
}
|
||||
|
||||
/** Close the file if open
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
void
|
||||
close(error_code& ec);
|
||||
|
||||
/** Open a file at the given path with the specified mode
|
||||
|
||||
@param path The utf-8 encoded path to the file
|
||||
|
||||
@param mode The file mode to use
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
void
|
||||
open(char const* path, file_mode mode, error_code& ec);
|
||||
|
||||
/** Return the size of the open file
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
|
||||
@return The size in bytes
|
||||
*/
|
||||
std::uint64_t
|
||||
size(error_code& ec) const;
|
||||
|
||||
/** Return the current position in the open file
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
|
||||
@return The offset in bytes from the beginning of the file
|
||||
*/
|
||||
std::uint64_t
|
||||
pos(error_code& ec) const;
|
||||
|
||||
/** Adjust the current position in the open file
|
||||
|
||||
@param offset The offset in bytes from the beginning of the file
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
void
|
||||
seek(std::uint64_t offset, error_code& ec);
|
||||
|
||||
/** Read from the open file
|
||||
|
||||
@param buffer The buffer for storing the result of the read
|
||||
|
||||
@param n The number of bytes to read
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
std::size_t
|
||||
read(void* buffer, std::size_t n, error_code& ec) const;
|
||||
|
||||
/** Write to the open file
|
||||
|
||||
@param buffer The buffer holding the data to write
|
||||
|
||||
@param n The number of bytes to write
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
std::size_t
|
||||
write(void const* buffer, std::size_t n, error_code& ec);
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/core/impl/file_stdio.ipp>
|
||||
|
||||
#endif
|
||||
173
include/beast/core/file_win32.hpp
Normal file
173
include/beast/core/file_win32.hpp
Normal file
@@ -0,0 +1,173 @@
|
||||
//
|
||||
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_CORE_FILE_WIN32_HPP
|
||||
#define BEAST_CORE_FILE_WIN32_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#if ! defined(BEAST_USE_WIN32_FILE)
|
||||
# ifdef BOOST_MSVC
|
||||
# define BEAST_USE_WIN32_FILE 1
|
||||
# else
|
||||
# define BEAST_USE_WIN32_FILE 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if BEAST_USE_WIN32_FILE
|
||||
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/file_base.hpp>
|
||||
#include <boost/detail/winapi/basic_types.hpp>
|
||||
#include <boost/detail/winapi/handles.hpp>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** An implementation of File for Win32.
|
||||
|
||||
This class implements a @b File using Win32 native interfaces.
|
||||
*/
|
||||
class file_win32
|
||||
{
|
||||
boost::detail::winapi::HANDLE_ h_ =
|
||||
boost::detail::winapi::INVALID_HANDLE_VALUE_;
|
||||
|
||||
public:
|
||||
/** The type of the underlying file handle.
|
||||
|
||||
This is platform-specific.
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
using native_handle_type = HANDLE;
|
||||
#else
|
||||
using native_handle_type = boost::detail::winapi::HANDLE_;
|
||||
#endif
|
||||
|
||||
/** Destructor
|
||||
|
||||
If the file is open it is first closed.
|
||||
*/
|
||||
~file_win32();
|
||||
|
||||
/** Constructor
|
||||
|
||||
There is no open file initially.
|
||||
*/
|
||||
file_win32() = default;
|
||||
|
||||
/** Constructor
|
||||
|
||||
The moved-from object behaves as if default constructed.
|
||||
*/
|
||||
file_win32(file_win32&& other);
|
||||
|
||||
/** Assignment
|
||||
|
||||
The moved-from object behaves as if default constructed.
|
||||
*/
|
||||
file_win32& operator=(file_win32&& other);
|
||||
|
||||
/// Returns the native handle associated with the file.
|
||||
native_handle_type
|
||||
native_handle()
|
||||
{
|
||||
return h_;
|
||||
}
|
||||
|
||||
/** Set the native handle associated with the file.
|
||||
|
||||
If the file is open it is first closed.
|
||||
|
||||
@param h The native file handle to assign.
|
||||
*/
|
||||
void
|
||||
native_handle(native_handle_type h);
|
||||
|
||||
/// Returns `true` if the file is open
|
||||
bool
|
||||
is_open() const
|
||||
{
|
||||
return h_ != boost::detail::winapi::INVALID_HANDLE_VALUE_;
|
||||
}
|
||||
|
||||
/** Close the file if open
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
void
|
||||
close(error_code& ec);
|
||||
|
||||
/** Open a file at the given path with the specified mode
|
||||
|
||||
@param path The utf-8 encoded path to the file
|
||||
|
||||
@param mode The file mode to use
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
void
|
||||
open(char const* path, file_mode mode, error_code& ec);
|
||||
|
||||
/** Return the size of the open file
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
|
||||
@return The size in bytes
|
||||
*/
|
||||
std::uint64_t
|
||||
size(error_code& ec) const;
|
||||
|
||||
/** Return the current position in the open file
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
|
||||
@return The offset in bytes from the beginning of the file
|
||||
*/
|
||||
std::uint64_t
|
||||
pos(error_code& ec);
|
||||
|
||||
/** Adjust the current position in the open file
|
||||
|
||||
@param offset The offset in bytes from the beginning of the file
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
void
|
||||
seek(std::uint64_t offset, error_code& ec);
|
||||
|
||||
/** Read from the open file
|
||||
|
||||
@param buffer The buffer for storing the result of the read
|
||||
|
||||
@param n The number of bytes to read
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
std::size_t
|
||||
read(void* buffer, std::size_t n, error_code& ec);
|
||||
|
||||
/** Write to the open file
|
||||
|
||||
@param buffer The buffer holding the data to write
|
||||
|
||||
@param n The number of bytes to write
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
std::size_t
|
||||
write(void const* buffer, std::size_t n, error_code& ec);
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/core/impl/file_win32.ipp>
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
341
include/beast/core/flat_buffer.hpp
Normal file
341
include/beast/core/flat_buffer.hpp
Normal file
@@ -0,0 +1,341 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_FLAT_BUFFER_HPP
|
||||
#define BEAST_FLAT_BUFFER_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/detail/empty_base_optimization.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** A linear dynamic buffer.
|
||||
|
||||
Objects of this type meet the requirements of @b DynamicBuffer
|
||||
and offer additional invariants:
|
||||
|
||||
@li Buffer sequences returned by @ref data and @ref prepare
|
||||
will always be of length one.
|
||||
|
||||
@li A configurable maximum buffer size may be set upon
|
||||
construction. Attempts to exceed the buffer size will throw
|
||||
`std::length_error`.
|
||||
|
||||
Upon construction, a maximum size for the buffer may be
|
||||
specified. If this limit is exceeded, the `std::length_error`
|
||||
exception will be thrown.
|
||||
|
||||
@note This class is designed for use with algorithms that
|
||||
take dynamic buffers as parameters, and are optimized
|
||||
for the case where the input sequence or output sequence
|
||||
is stored in a single contiguous buffer.
|
||||
*/
|
||||
template<class Allocator>
|
||||
class basic_flat_buffer
|
||||
#if ! BEAST_DOXYGEN
|
||||
: private detail::empty_base_optimization<
|
||||
typename std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<char>>
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
#if BEAST_DOXYGEN
|
||||
/// The type of allocator used.
|
||||
using allocator_type = Allocator;
|
||||
#else
|
||||
using allocator_type = typename
|
||||
std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<char>;
|
||||
#endif
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
min_size = 512
|
||||
};
|
||||
|
||||
template<class OtherAlloc>
|
||||
friend class basic_flat_buffer;
|
||||
|
||||
using alloc_traits =
|
||||
std::allocator_traits<allocator_type>;
|
||||
|
||||
static
|
||||
inline
|
||||
std::size_t
|
||||
dist(char const* first, char const* last)
|
||||
{
|
||||
return static_cast<std::size_t>(last - first);
|
||||
}
|
||||
|
||||
char* begin_;
|
||||
char* in_;
|
||||
char* out_;
|
||||
char* last_;
|
||||
char* end_;
|
||||
std::size_t max_;
|
||||
|
||||
public:
|
||||
/// The type used to represent the input sequence as a list of buffers.
|
||||
using const_buffers_type = boost::asio::const_buffers_1;
|
||||
|
||||
/// The type used to represent the output sequence as a list of buffers.
|
||||
using mutable_buffers_type = boost::asio::mutable_buffers_1;
|
||||
|
||||
/// Destructor
|
||||
~basic_flat_buffer();
|
||||
|
||||
/** Constructor
|
||||
|
||||
Upon construction, capacity will be zero.
|
||||
*/
|
||||
basic_flat_buffer();
|
||||
|
||||
/** Constructor
|
||||
|
||||
Upon construction, capacity will be zero.
|
||||
|
||||
@param limit The setting for @ref max_size.
|
||||
*/
|
||||
explicit
|
||||
basic_flat_buffer(std::size_t limit);
|
||||
|
||||
/** Constructor
|
||||
|
||||
Upon construction, capacity will be zero.
|
||||
|
||||
@param alloc The allocator to construct with.
|
||||
*/
|
||||
explicit
|
||||
basic_flat_buffer(Allocator const& alloc);
|
||||
|
||||
/** Constructor
|
||||
|
||||
Upon construction, capacity will be zero.
|
||||
|
||||
@param limit The setting for @ref max_size.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
basic_flat_buffer(
|
||||
std::size_t limit, Allocator const& alloc);
|
||||
|
||||
/** Move constructor
|
||||
|
||||
After the move, `*this` will have an empty output sequence.
|
||||
|
||||
@param other The object to move from. After the move,
|
||||
The object's state will be as if constructed using
|
||||
its current allocator and limit.
|
||||
*/
|
||||
basic_flat_buffer(basic_flat_buffer&& other);
|
||||
|
||||
/** Move constructor
|
||||
|
||||
After the move, `*this` will have an empty output sequence.
|
||||
|
||||
@param other The object to move from. After the move,
|
||||
The object's state will be as if constructed using
|
||||
its current allocator and limit.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
basic_flat_buffer(
|
||||
basic_flat_buffer&& other, Allocator const& alloc);
|
||||
|
||||
/** Copy constructor
|
||||
|
||||
@param other The object to copy from.
|
||||
*/
|
||||
basic_flat_buffer(basic_flat_buffer const& other);
|
||||
|
||||
/** Copy constructor
|
||||
|
||||
@param other The object to copy from.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
basic_flat_buffer(basic_flat_buffer const& other,
|
||||
Allocator const& alloc);
|
||||
|
||||
/** Copy constructor
|
||||
|
||||
@param other The object to copy from.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_flat_buffer(
|
||||
basic_flat_buffer<OtherAlloc> const& other);
|
||||
|
||||
/** Copy constructor
|
||||
|
||||
@param other The object to copy from.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_flat_buffer(
|
||||
basic_flat_buffer<OtherAlloc> const& other,
|
||||
Allocator const& alloc);
|
||||
|
||||
/** Move assignment
|
||||
|
||||
After the move, `*this` will have an empty output sequence.
|
||||
|
||||
@param other The object to move from. After the move,
|
||||
The object's state will be as if constructed using
|
||||
its current allocator and limit.
|
||||
*/
|
||||
basic_flat_buffer&
|
||||
operator=(basic_flat_buffer&& other);
|
||||
|
||||
/** Copy assignment
|
||||
|
||||
After the copy, `*this` will have an empty output sequence.
|
||||
|
||||
@param other The object to copy from.
|
||||
*/
|
||||
basic_flat_buffer&
|
||||
operator=(basic_flat_buffer const& other);
|
||||
|
||||
/** Copy assignment
|
||||
|
||||
After the copy, `*this` will have an empty output sequence.
|
||||
|
||||
@param other The object to copy from.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_flat_buffer&
|
||||
operator=(basic_flat_buffer<OtherAlloc> const& other);
|
||||
|
||||
/// Returns a copy of the associated allocator.
|
||||
allocator_type
|
||||
get_allocator() const
|
||||
{
|
||||
return this->member();
|
||||
}
|
||||
|
||||
/// Returns the size of the input sequence.
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
return dist(in_, out_);
|
||||
}
|
||||
|
||||
/// Return the maximum sum of the input and output sequence sizes.
|
||||
std::size_t
|
||||
max_size() const
|
||||
{
|
||||
return max_;
|
||||
}
|
||||
|
||||
/// Return the maximum sum of input and output sizes that can be held without an allocation.
|
||||
std::size_t
|
||||
capacity() const
|
||||
{
|
||||
return dist(begin_, end_);
|
||||
}
|
||||
|
||||
/// Get a list of buffers that represent the input sequence.
|
||||
const_buffers_type
|
||||
data() const
|
||||
{
|
||||
return {in_, dist(in_, out_)};
|
||||
}
|
||||
|
||||
/** Get a list of buffers that represent the output sequence, with the given size.
|
||||
|
||||
@throws std::length_error if `size() + n` exceeds `max_size()`.
|
||||
|
||||
@note All previous buffers sequences obtained from
|
||||
calls to @ref data or @ref prepare are invalidated.
|
||||
*/
|
||||
mutable_buffers_type
|
||||
prepare(std::size_t n);
|
||||
|
||||
/** Move bytes from the output sequence to the input sequence.
|
||||
|
||||
@param n The number of bytes to move. If this is larger than
|
||||
the number of bytes in the output sequences, then the entire
|
||||
output sequences is moved.
|
||||
|
||||
@note All previous buffers sequences obtained from
|
||||
calls to @ref data or @ref prepare are invalidated.
|
||||
*/
|
||||
void
|
||||
commit(std::size_t n)
|
||||
{
|
||||
out_ += (std::min)(n, dist(out_, last_));
|
||||
}
|
||||
|
||||
/** Remove bytes from the input sequence.
|
||||
|
||||
If `n` is greater than the number of bytes in the input
|
||||
sequence, all bytes in the input sequence are removed.
|
||||
|
||||
@note All previous buffers sequences obtained from
|
||||
calls to @ref data or @ref prepare are invalidated.
|
||||
*/
|
||||
void
|
||||
consume(std::size_t n);
|
||||
|
||||
/** Reallocate the buffer to fit the input sequence.
|
||||
|
||||
@note All previous buffers sequences obtained from
|
||||
calls to @ref data or @ref prepare are invalidated.
|
||||
*/
|
||||
void
|
||||
shrink_to_fit();
|
||||
|
||||
/// Exchange two flat buffers
|
||||
template<class Alloc>
|
||||
friend
|
||||
void
|
||||
swap(
|
||||
basic_flat_buffer<Alloc>& lhs,
|
||||
basic_flat_buffer<Alloc>& rhs);
|
||||
|
||||
private:
|
||||
void
|
||||
reset();
|
||||
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
copy_from(DynamicBuffer const& other);
|
||||
|
||||
void
|
||||
move_assign(basic_flat_buffer&, std::true_type);
|
||||
|
||||
void
|
||||
move_assign(basic_flat_buffer&, std::false_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_flat_buffer const&, std::true_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_flat_buffer const&, std::false_type);
|
||||
|
||||
void
|
||||
swap(basic_flat_buffer&);
|
||||
|
||||
void
|
||||
swap(basic_flat_buffer&, std::true_type);
|
||||
|
||||
void
|
||||
swap(basic_flat_buffer&, std::false_type);
|
||||
};
|
||||
|
||||
using flat_buffer =
|
||||
basic_flat_buffer<std::allocator<char>>;
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/core/impl/flat_buffer.ipp>
|
||||
|
||||
#endif
|
||||
@@ -9,7 +9,9 @@
|
||||
#define BEAST_HANDLER_ALLOC_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
@@ -35,7 +37,7 @@ namespace beast {
|
||||
the handler is invoked or undefined behavior results. This behavior
|
||||
is described as the "deallocate before invocation" Asio guarantee.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
#if BEAST_DOXYGEN
|
||||
template<class T, class Handler>
|
||||
class handler_alloc;
|
||||
#else
|
||||
@@ -56,6 +58,12 @@ private:
|
||||
public:
|
||||
using value_type = T;
|
||||
using is_always_equal = std::true_type;
|
||||
using pointer = T*;
|
||||
using reference = T&;
|
||||
using const_pointer = T const*;
|
||||
using const_reference = T const&;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
template<class U>
|
||||
struct rebind
|
||||
@@ -89,38 +97,46 @@ public:
|
||||
}
|
||||
|
||||
value_type*
|
||||
allocate(std::ptrdiff_t n)
|
||||
allocate(size_type n)
|
||||
{
|
||||
auto const size = n * sizeof(T);
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return static_cast<value_type*>(
|
||||
beast_asio_helpers::allocate(
|
||||
size, h_));
|
||||
asio_handler_allocate(size, std::addressof(h_)));
|
||||
}
|
||||
|
||||
void
|
||||
deallocate(value_type* p, std::ptrdiff_t n)
|
||||
deallocate(value_type* p, size_type n)
|
||||
{
|
||||
auto const size = n * sizeof(T);
|
||||
beast_asio_helpers::deallocate(
|
||||
p, size, h_);
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(p, size, std::addressof(h_));
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Work-around for MSVC not using allocator_traits
|
||||
// in the implementation of shared_ptr
|
||||
//
|
||||
//#if BOOST_WORKAROUND(BOOST_GCC, < 60000) // Works, but too coarse
|
||||
|
||||
#if defined(BOOST_LIBSTDCXX_VERSION) && BOOST_LIBSTDCXX_VERSION < 60000
|
||||
template<class U, class... Args>
|
||||
void
|
||||
destroy(T* t)
|
||||
construct(U* ptr, Args&&... args)
|
||||
{
|
||||
t->~T();
|
||||
::new((void*)ptr) U(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class U>
|
||||
void
|
||||
destroy(U* ptr)
|
||||
{
|
||||
ptr->~U();
|
||||
}
|
||||
#endif
|
||||
|
||||
template<class U>
|
||||
friend
|
||||
bool
|
||||
operator==(handler_alloc const& lhs,
|
||||
handler_alloc<U, Handler> const& rhs)
|
||||
operator==(
|
||||
handler_alloc const&,
|
||||
handler_alloc<U, Handler> const&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -128,7 +144,8 @@ public:
|
||||
template<class U>
|
||||
friend
|
||||
bool
|
||||
operator!=(handler_alloc const& lhs,
|
||||
operator!=(
|
||||
handler_alloc const& lhs,
|
||||
handler_alloc<U, Handler> const& rhs)
|
||||
{
|
||||
return ! (lhs == rhs);
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HANDLER_CONCEPTS_HPP
|
||||
#define BEAST_HANDLER_CONCEPTS_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/detail/is_call_possible.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/// Determine if `T` meets the requirements of @b `CompletionHandler`.
|
||||
template<class T, class Signature>
|
||||
#if GENERATING_DOCS
|
||||
using is_CompletionHandler = std::integral_constant<bool, ...>;
|
||||
#else
|
||||
using is_CompletionHandler = std::integral_constant<bool,
|
||||
std::is_copy_constructible<typename std::decay<T>::type>::value &&
|
||||
detail::is_call_possible<T, Signature>::value>;
|
||||
#endif
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -1,105 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HANDLER_HELPERS_HPP
|
||||
#define BEAST_HANDLER_HELPERS_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <memory>
|
||||
|
||||
/* Calls to:
|
||||
|
||||
* asio_handler_allocate
|
||||
* asio_handler_deallocate
|
||||
* asio_handler_invoke
|
||||
* asio_handler_is_continuation
|
||||
|
||||
must be made from a namespace that does not
|
||||
contain overloads of this function. The beast_asio_helpers
|
||||
namespace is defined here for that purpose.
|
||||
*/
|
||||
|
||||
namespace beast_asio_helpers {
|
||||
|
||||
/// Allocation function for handlers.
|
||||
template <class Handler>
|
||||
inline
|
||||
void*
|
||||
allocate(std::size_t s, Handler& handler)
|
||||
{
|
||||
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
|
||||
return ::operator new(s);
|
||||
#else
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(s, std::addressof(handler));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Deallocation function for handlers.
|
||||
template<class Handler>
|
||||
inline
|
||||
void
|
||||
deallocate(void* p, std::size_t s, Handler& handler)
|
||||
{
|
||||
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
|
||||
::operator delete(p);
|
||||
#else
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(p, s, std::addressof(handler));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Invoke function for handlers.
|
||||
template<class Function, class Handler>
|
||||
inline
|
||||
void
|
||||
invoke(Function& function, Handler& handler)
|
||||
{
|
||||
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
|
||||
Function tmp(function);
|
||||
tmp();
|
||||
#else
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(function, std::addressof(handler));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Invoke function for handlers.
|
||||
template<class Function, class Handler>
|
||||
inline
|
||||
void
|
||||
invoke(Function const& function, Handler& handler)
|
||||
{
|
||||
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
|
||||
Function tmp(function);
|
||||
tmp();
|
||||
#else
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(function, std::addressof(handler));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Returns true if handler represents a continuation of the asynchronous operation
|
||||
template<class Handler>
|
||||
inline
|
||||
bool
|
||||
is_continuation(Handler& handler)
|
||||
{
|
||||
#if !defined(BOOST_ASIO_HAS_HANDLER_HOOKS)
|
||||
return false;
|
||||
#else
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return asio_handler_is_continuation(std::addressof(handler));
|
||||
#endif
|
||||
}
|
||||
|
||||
} // beast_asio_helpers
|
||||
|
||||
#endif
|
||||
@@ -191,6 +191,11 @@ public:
|
||||
deallocation-before-invocation Asio guarantee. All
|
||||
instances of @ref handler_ptr which refer to the
|
||||
same owned object will be reset, including this instance.
|
||||
|
||||
@note Care must be taken when the arguments are themselves
|
||||
stored in the owned object. Such arguments must first be
|
||||
moved to the stack or elsewhere, and then passed, or else
|
||||
undefined behavior will result.
|
||||
*/
|
||||
template<class... Args>
|
||||
void
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_BUFFER_CAT_HPP
|
||||
#define BEAST_DETAIL_BUFFER_CAT_HPP
|
||||
#ifndef BEAST_IMPL_BUFFER_CAT_IPP
|
||||
#define BEAST_IMPL_BUFFER_CAT_IPP
|
||||
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <new>
|
||||
@@ -19,57 +20,22 @@
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template<class... Bn>
|
||||
struct common_buffers_type
|
||||
class buffer_cat_view<Bn...>::const_iterator
|
||||
{
|
||||
using type = typename std::conditional<
|
||||
std::is_convertible<std::tuple<Bn...>,
|
||||
typename repeat_tuple<sizeof...(Bn),
|
||||
boost::asio::mutable_buffer>::type>::value,
|
||||
boost::asio::mutable_buffer,
|
||||
boost::asio::const_buffer>::type;
|
||||
};
|
||||
#if 0
|
||||
static_assert(
|
||||
detail::is_all_const_buffer_sequence<Bn...>::value,
|
||||
"BufferSequence requirements not met");
|
||||
#endif
|
||||
|
||||
template<class... Bn>
|
||||
class buffer_cat_helper
|
||||
{
|
||||
std::tuple<Bn...> bn_;
|
||||
|
||||
public:
|
||||
using value_type = typename
|
||||
common_buffers_type<Bn...>::type;
|
||||
|
||||
class const_iterator;
|
||||
|
||||
buffer_cat_helper(buffer_cat_helper&&) = default;
|
||||
buffer_cat_helper(buffer_cat_helper const&) = default;
|
||||
buffer_cat_helper& operator=(buffer_cat_helper&&) = delete;
|
||||
buffer_cat_helper& operator=(buffer_cat_helper const&) = delete;
|
||||
|
||||
explicit
|
||||
buffer_cat_helper(Bn const&... bn)
|
||||
: bn_(bn...)
|
||||
{
|
||||
}
|
||||
|
||||
const_iterator
|
||||
begin() const;
|
||||
|
||||
const_iterator
|
||||
end() const;
|
||||
};
|
||||
|
||||
template<class... Bn>
|
||||
class buffer_cat_helper<Bn...>::const_iterator
|
||||
{
|
||||
std::size_t n_;
|
||||
std::tuple<Bn...> const* bn_;
|
||||
std::array<std::uint8_t,
|
||||
max_sizeof<typename Bn::const_iterator...>()> buf_;
|
||||
std::array<char, detail::max_sizeof<
|
||||
typename Bn::const_iterator...>()> buf_;
|
||||
|
||||
friend class buffer_cat_helper<Bn...>;
|
||||
friend class buffer_cat_view<Bn...>;
|
||||
|
||||
template<std::size_t I>
|
||||
using C = std::integral_constant<std::size_t, I>;
|
||||
@@ -84,8 +50,7 @@ class buffer_cat_helper<Bn...>::const_iterator
|
||||
{
|
||||
// type-pun
|
||||
return *reinterpret_cast<
|
||||
iter_t<I>*>(static_cast<void*>(
|
||||
buf_.data()));
|
||||
iter_t<I>*>(static_cast<void*>(buf_.data()));
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
@@ -100,7 +65,7 @@ class buffer_cat_helper<Bn...>::const_iterator
|
||||
|
||||
public:
|
||||
using value_type = typename
|
||||
common_buffers_type<Bn...>::type;
|
||||
detail::common_buffers_type<Bn...>::type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
@@ -120,7 +85,7 @@ public:
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
return ! (*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
@@ -133,23 +98,13 @@ public:
|
||||
operator++();
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
operator++(int);
|
||||
|
||||
const_iterator&
|
||||
operator--();
|
||||
|
||||
const_iterator
|
||||
operator--(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
operator--(int);
|
||||
|
||||
private:
|
||||
const_iterator(
|
||||
@@ -166,17 +121,48 @@ private:
|
||||
void
|
||||
construct(C<I> const&)
|
||||
{
|
||||
if(std::get<I>(*bn_).begin() !=
|
||||
std::get<I>(*bn_).end())
|
||||
if(boost::asio::buffer_size(
|
||||
std::get<I>(*bn_)) != 0)
|
||||
{
|
||||
n_ = I;
|
||||
new(buf_.data()) iter_t<I>{
|
||||
new(&buf_[0]) iter_t<I>{
|
||||
std::get<I>(*bn_).begin()};
|
||||
return;
|
||||
}
|
||||
construct(C<I+1>{});
|
||||
}
|
||||
|
||||
void
|
||||
rconstruct(C<0> const&)
|
||||
{
|
||||
auto constexpr I = 0;
|
||||
if(boost::asio::buffer_size(
|
||||
std::get<I>(*bn_)) != 0)
|
||||
{
|
||||
n_ = I;
|
||||
new(&buf_[0]) iter_t<I>{
|
||||
std::get<I>(*bn_).end()};
|
||||
return;
|
||||
}
|
||||
BOOST_THROW_EXCEPTION(std::logic_error{
|
||||
"invalid iterator"});
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
void
|
||||
rconstruct(C<I> const&)
|
||||
{
|
||||
if(boost::asio::buffer_size(
|
||||
std::get<I>(*bn_)) != 0)
|
||||
{
|
||||
n_ = I;
|
||||
new(&buf_[0]) iter_t<I>{
|
||||
std::get<I>(*bn_).end()};
|
||||
return;
|
||||
}
|
||||
rconstruct(C<I-1>{});
|
||||
}
|
||||
|
||||
void
|
||||
destroy(C<sizeof...(Bn)> const&)
|
||||
{
|
||||
@@ -209,7 +195,7 @@ private:
|
||||
{
|
||||
if(n_ == I)
|
||||
{
|
||||
new(buf_.data()) iter_t<I>{
|
||||
new(&buf_[0]) iter_t<I>{
|
||||
std::move(other.iter<I>())};
|
||||
return;
|
||||
}
|
||||
@@ -229,7 +215,7 @@ private:
|
||||
{
|
||||
if(n_ == I)
|
||||
{
|
||||
new(buf_.data()) iter_t<I>{
|
||||
new(&buf_[0]) iter_t<I>{
|
||||
other.iter<I>()};
|
||||
return;
|
||||
}
|
||||
@@ -257,8 +243,8 @@ private:
|
||||
reference
|
||||
dereference(C<sizeof...(Bn)> const&) const
|
||||
{
|
||||
throw detail::make_exception<std::logic_error>(
|
||||
"invalid iterator", __FILE__, __LINE__);
|
||||
BOOST_THROW_EXCEPTION(std::logic_error{
|
||||
"invalid iterator"});
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
@@ -274,8 +260,8 @@ private:
|
||||
void
|
||||
increment(C<sizeof...(Bn)> const&)
|
||||
{
|
||||
throw detail::make_exception<std::logic_error>(
|
||||
"invalid iterator", __FILE__, __LINE__);
|
||||
BOOST_THROW_EXCEPTION(std::logic_error{
|
||||
"invalid iterator"});
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
@@ -299,27 +285,10 @@ private:
|
||||
{
|
||||
auto constexpr I = sizeof...(Bn);
|
||||
if(n_ == I)
|
||||
{
|
||||
--n_;
|
||||
new(buf_.data()) iter_t<I-1>{
|
||||
std::get<I-1>(*bn_).end()};
|
||||
}
|
||||
rconstruct(C<I-1>{});
|
||||
decrement(C<I-1>{});
|
||||
}
|
||||
|
||||
void
|
||||
decrement(C<0> const&)
|
||||
{
|
||||
auto constexpr I = 0;
|
||||
if(iter<I>() != std::get<I>(*bn_).begin())
|
||||
{
|
||||
--iter<I>();
|
||||
return;
|
||||
}
|
||||
throw detail::make_exception<std::logic_error>(
|
||||
"invalid iterator", __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
void
|
||||
decrement(C<I> const&)
|
||||
@@ -334,24 +303,36 @@ private:
|
||||
--n_;
|
||||
using Iter = iter_t<I>;
|
||||
iter<I>().~Iter();
|
||||
new(buf_.data()) iter_t<I-1>{
|
||||
std::get<I-1>(*bn_).end()};
|
||||
rconstruct(C<I-1>{});
|
||||
}
|
||||
decrement(C<I-1>{});
|
||||
}
|
||||
|
||||
void
|
||||
decrement(C<0> const&)
|
||||
{
|
||||
auto constexpr I = 0;
|
||||
if(iter<I>() != std::get<I>(*bn_).begin())
|
||||
{
|
||||
--iter<I>();
|
||||
return;
|
||||
}
|
||||
BOOST_THROW_EXCEPTION(std::logic_error{
|
||||
"invalid iterator"});
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class... Bn>
|
||||
buffer_cat_helper<Bn...>::
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::~const_iterator()
|
||||
{
|
||||
destroy(C<0>{});
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
buffer_cat_helper<Bn...>::
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::const_iterator()
|
||||
: n_(sizeof...(Bn))
|
||||
, bn_(nullptr)
|
||||
@@ -359,7 +340,7 @@ const_iterator::const_iterator()
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
buffer_cat_helper<Bn...>::
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::const_iterator(
|
||||
std::tuple<Bn...> const& bn, bool at_end)
|
||||
: bn_(&bn)
|
||||
@@ -371,7 +352,7 @@ const_iterator::const_iterator(
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
buffer_cat_helper<Bn...>::
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::const_iterator(const_iterator&& other)
|
||||
: n_(other.n_)
|
||||
, bn_(other.bn_)
|
||||
@@ -380,7 +361,7 @@ const_iterator::const_iterator(const_iterator&& other)
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
buffer_cat_helper<Bn...>::
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::const_iterator(const_iterator const& other)
|
||||
: n_(other.n_)
|
||||
, bn_(other.bn_)
|
||||
@@ -390,7 +371,7 @@ const_iterator::const_iterator(const_iterator const& other)
|
||||
|
||||
template<class... Bn>
|
||||
auto
|
||||
buffer_cat_helper<Bn...>::
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::operator=(const_iterator&& other) ->
|
||||
const_iterator&
|
||||
{
|
||||
@@ -399,13 +380,14 @@ const_iterator::operator=(const_iterator&& other) ->
|
||||
destroy(C<0>{});
|
||||
n_ = other.n_;
|
||||
bn_ = other.bn_;
|
||||
// VFALCO What about exceptions?
|
||||
move(std::move(other), C<0>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
auto
|
||||
buffer_cat_helper<Bn...>::
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::operator=(const_iterator const& other) ->
|
||||
const_iterator&
|
||||
{
|
||||
@@ -414,13 +396,14 @@ const_iterator&
|
||||
destroy(C<0>{});
|
||||
n_ = other.n_;
|
||||
bn_ = other.bn_;
|
||||
// VFALCO What about exceptions?
|
||||
copy(other, C<0>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
bool
|
||||
buffer_cat_helper<Bn...>::
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::operator==(const_iterator const& other) const
|
||||
{
|
||||
if(bn_ != other.bn_)
|
||||
@@ -432,7 +415,7 @@ const_iterator::operator==(const_iterator const& other) const
|
||||
|
||||
template<class... Bn>
|
||||
auto
|
||||
buffer_cat_helper<Bn...>::
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::operator*() const ->
|
||||
reference
|
||||
{
|
||||
@@ -441,7 +424,7 @@ const_iterator::operator*() const ->
|
||||
|
||||
template<class... Bn>
|
||||
auto
|
||||
buffer_cat_helper<Bn...>::
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::operator++() ->
|
||||
const_iterator&
|
||||
{
|
||||
@@ -451,7 +434,18 @@ const_iterator::operator++() ->
|
||||
|
||||
template<class... Bn>
|
||||
auto
|
||||
buffer_cat_helper<Bn...>::
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::operator++(int) ->
|
||||
const_iterator
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
auto
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::operator--() ->
|
||||
const_iterator&
|
||||
{
|
||||
@@ -459,10 +453,31 @@ const_iterator::operator--() ->
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class... Bn>
|
||||
auto
|
||||
buffer_cat_view<Bn...>::
|
||||
const_iterator::operator--(int) ->
|
||||
const_iterator
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class... Bn>
|
||||
buffer_cat_view<Bn...>::
|
||||
buffer_cat_view(Bn const&... bn)
|
||||
: bn_(bn...)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
template<class... Bn>
|
||||
inline
|
||||
auto
|
||||
buffer_cat_helper<Bn...>::begin() const ->
|
||||
buffer_cat_view<Bn...>::begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{bn_, false};
|
||||
@@ -471,13 +486,12 @@ buffer_cat_helper<Bn...>::begin() const ->
|
||||
template<class... Bn>
|
||||
inline
|
||||
auto
|
||||
buffer_cat_helper<Bn...>::end() const ->
|
||||
buffer_cat_view<Bn...>::end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{bn_, true};
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -5,11 +5,9 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_DETAIL_PREPARED_BUFFERS_HPP
|
||||
#define BEAST_DETAIL_PREPARED_BUFFERS_HPP
|
||||
#ifndef BEAST_IMPL_BUFFER_PREFIX_IPP
|
||||
#define BEAST_IMPL_BUFFER_PREFIX_IPP
|
||||
|
||||
#include <beast/core/prepare_buffer.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
@@ -18,102 +16,40 @@
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/** A buffer sequence adapter that shortens the sequence size.
|
||||
|
||||
The class adapts a buffer sequence to efficiently represent
|
||||
a shorter subset of the original list of buffers starting
|
||||
with the first byte of the original sequence.
|
||||
|
||||
@tparam BufferSequence The buffer sequence to adapt.
|
||||
*/
|
||||
template<class BufferSequence>
|
||||
class prepared_buffers
|
||||
inline
|
||||
boost::asio::const_buffer
|
||||
buffer_prefix(std::size_t n,
|
||||
boost::asio::const_buffer buffer)
|
||||
{
|
||||
using iter_type =
|
||||
typename BufferSequence::const_iterator;
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
return { buffer_cast<void const*>(buffer),
|
||||
(std::min)(n, buffer_size(buffer)) };
|
||||
}
|
||||
|
||||
BufferSequence bs_;
|
||||
iter_type back_;
|
||||
iter_type end_;
|
||||
std::size_t size_;
|
||||
inline
|
||||
boost::asio::mutable_buffer
|
||||
buffer_prefix(std::size_t n,
|
||||
boost::asio::mutable_buffer buffer)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
return { buffer_cast<void*>(buffer),
|
||||
(std::min)(n, buffer_size(buffer)) };
|
||||
}
|
||||
|
||||
template<class Deduced>
|
||||
prepared_buffers(Deduced&& other,
|
||||
std::size_t nback, std::size_t nend)
|
||||
: bs_(std::forward<Deduced>(other).bs_)
|
||||
, back_(std::next(bs_.begin(), nback))
|
||||
, end_(std::next(bs_.begin(), nend))
|
||||
, size_(other.size_)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
setup(std::size_t n);
|
||||
|
||||
public:
|
||||
/// The type for each element in the list of buffers.
|
||||
using value_type = typename std::conditional<
|
||||
std::is_convertible<typename
|
||||
std::iterator_traits<iter_type>::value_type,
|
||||
boost::asio::mutable_buffer>::value,
|
||||
boost::asio::mutable_buffer,
|
||||
boost::asio::const_buffer>::type;
|
||||
|
||||
#if GENERATING_DOCS
|
||||
/// A bidirectional iterator type that may be used to read elements.
|
||||
using const_iterator = implementation_defined;
|
||||
|
||||
#else
|
||||
class const_iterator;
|
||||
|
||||
#endif
|
||||
|
||||
/// Move constructor.
|
||||
prepared_buffers(prepared_buffers&&);
|
||||
|
||||
/// Copy constructor.
|
||||
prepared_buffers(prepared_buffers const&);
|
||||
|
||||
/// Move assignment.
|
||||
prepared_buffers& operator=(prepared_buffers&&);
|
||||
|
||||
/// Copy assignment.
|
||||
prepared_buffers& operator=(prepared_buffers const&);
|
||||
|
||||
/** Construct a shortened buffer sequence.
|
||||
|
||||
@param n The maximum number of bytes in the wrapped
|
||||
sequence. If this is larger than the size of passed,
|
||||
buffers, the resulting sequence will represent the
|
||||
entire input sequence.
|
||||
|
||||
@param buffers The buffer sequence to adapt. A copy of
|
||||
the sequence will be made, but ownership of the underlying
|
||||
memory is not transferred.
|
||||
*/
|
||||
prepared_buffers(std::size_t n, BufferSequence const& buffers);
|
||||
|
||||
/// Get a bidirectional iterator to the first element.
|
||||
const_iterator
|
||||
begin() const;
|
||||
|
||||
/// Get a bidirectional iterator to one past the last element.
|
||||
const_iterator
|
||||
end() const;
|
||||
};
|
||||
} // detail
|
||||
|
||||
template<class BufferSequence>
|
||||
class prepared_buffers<BufferSequence>::const_iterator
|
||||
class buffer_prefix_view<BufferSequence>::const_iterator
|
||||
{
|
||||
friend class prepared_buffers<BufferSequence>;
|
||||
friend class buffer_prefix_view<BufferSequence>;
|
||||
|
||||
using iter_type =
|
||||
typename BufferSequence::const_iterator;
|
||||
|
||||
prepared_buffers const* b_ = nullptr;
|
||||
typename BufferSequence::const_iterator it_;
|
||||
buffer_prefix_view const* b_ = nullptr;
|
||||
iter_type it_;
|
||||
|
||||
public:
|
||||
using value_type = typename std::conditional<
|
||||
@@ -150,7 +86,7 @@ public:
|
||||
operator*() const
|
||||
{
|
||||
if(it_ == b_->back_)
|
||||
return prepare_buffer(b_->size_, *it_);
|
||||
return detail::buffer_prefix(b_->size_, *it_);
|
||||
return *it_;
|
||||
}
|
||||
|
||||
@@ -188,7 +124,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
const_iterator(prepared_buffers const& b,
|
||||
const_iterator(buffer_prefix_view const& b,
|
||||
bool at_end)
|
||||
: b_(&b)
|
||||
, it_(at_end ? b.end_ : b.bs_.begin())
|
||||
@@ -198,7 +134,7 @@ private:
|
||||
|
||||
template<class BufferSequence>
|
||||
void
|
||||
prepared_buffers<BufferSequence>::
|
||||
buffer_prefix_view<BufferSequence>::
|
||||
setup(std::size_t n)
|
||||
{
|
||||
for(end_ = bs_.begin(); end_ != bs_.end(); ++end_)
|
||||
@@ -218,7 +154,8 @@ setup(std::size_t n)
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
prepared_buffers<BufferSequence>::const_iterator::
|
||||
buffer_prefix_view<BufferSequence>::
|
||||
const_iterator::
|
||||
const_iterator(const_iterator&& other)
|
||||
: b_(other.b_)
|
||||
, it_(std::move(other.it_))
|
||||
@@ -226,7 +163,8 @@ const_iterator(const_iterator&& other)
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
prepared_buffers<BufferSequence>::const_iterator::
|
||||
buffer_prefix_view<BufferSequence>::
|
||||
const_iterator::
|
||||
const_iterator(const_iterator const& other)
|
||||
: b_(other.b_)
|
||||
, it_(other.it_)
|
||||
@@ -235,7 +173,8 @@ const_iterator(const_iterator const& other)
|
||||
|
||||
template<class BufferSequence>
|
||||
auto
|
||||
prepared_buffers<BufferSequence>::const_iterator::
|
||||
buffer_prefix_view<BufferSequence>::
|
||||
const_iterator::
|
||||
operator=(const_iterator&& other) ->
|
||||
const_iterator&
|
||||
{
|
||||
@@ -246,7 +185,8 @@ operator=(const_iterator&& other) ->
|
||||
|
||||
template<class BufferSequence>
|
||||
auto
|
||||
prepared_buffers<BufferSequence>::const_iterator::
|
||||
buffer_prefix_view<BufferSequence>::
|
||||
const_iterator::
|
||||
operator=(const_iterator const& other) ->
|
||||
const_iterator&
|
||||
{
|
||||
@@ -258,18 +198,18 @@ operator=(const_iterator const& other) ->
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
prepared_buffers<BufferSequence>::
|
||||
prepared_buffers(prepared_buffers&& other)
|
||||
: prepared_buffers(std::move(other),
|
||||
buffer_prefix_view<BufferSequence>::
|
||||
buffer_prefix_view(buffer_prefix_view&& other)
|
||||
: buffer_prefix_view(std::move(other),
|
||||
std::distance<iter_type>(other.bs_.begin(), other.back_),
|
||||
std::distance<iter_type>(other.bs_.begin(), other.end_))
|
||||
{
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
prepared_buffers<BufferSequence>::
|
||||
prepared_buffers(prepared_buffers const& other)
|
||||
: prepared_buffers(other,
|
||||
buffer_prefix_view<BufferSequence>::
|
||||
buffer_prefix_view(buffer_prefix_view const& other)
|
||||
: buffer_prefix_view(other,
|
||||
std::distance<iter_type>(other.bs_.begin(), other.back_),
|
||||
std::distance<iter_type>(other.bs_.begin(), other.end_))
|
||||
{
|
||||
@@ -277,9 +217,9 @@ prepared_buffers(prepared_buffers const& other)
|
||||
|
||||
template<class BufferSequence>
|
||||
auto
|
||||
prepared_buffers<BufferSequence>::
|
||||
operator=(prepared_buffers&& other) ->
|
||||
prepared_buffers&
|
||||
buffer_prefix_view<BufferSequence>::
|
||||
operator=(buffer_prefix_view&& other) ->
|
||||
buffer_prefix_view&
|
||||
{
|
||||
auto const nback = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.back_);
|
||||
@@ -294,9 +234,9 @@ operator=(prepared_buffers&& other) ->
|
||||
|
||||
template<class BufferSequence>
|
||||
auto
|
||||
prepared_buffers<BufferSequence>::
|
||||
operator=(prepared_buffers const& other) ->
|
||||
prepared_buffers&
|
||||
buffer_prefix_view<BufferSequence>::
|
||||
operator=(buffer_prefix_view const& other) ->
|
||||
buffer_prefix_view&
|
||||
{
|
||||
auto const nback = std::distance<iter_type>(
|
||||
other.bs_.begin(), other.back_);
|
||||
@@ -310,17 +250,27 @@ operator=(prepared_buffers const& other) ->
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
prepared_buffers<BufferSequence>::
|
||||
prepared_buffers(std::size_t n, BufferSequence const& bs)
|
||||
buffer_prefix_view<BufferSequence>::
|
||||
buffer_prefix_view(std::size_t n, BufferSequence const& bs)
|
||||
: bs_(bs)
|
||||
{
|
||||
setup(n);
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
template<class... Args>
|
||||
buffer_prefix_view<BufferSequence>::
|
||||
buffer_prefix_view(std::size_t n,
|
||||
boost::in_place_init_t, Args&&... args)
|
||||
: bs_(std::forward<Args>(args)...)
|
||||
{
|
||||
setup(n);
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
inline
|
||||
auto
|
||||
prepared_buffers<BufferSequence>::begin() const ->
|
||||
buffer_prefix_view<BufferSequence>::begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, false};
|
||||
@@ -329,13 +279,12 @@ prepared_buffers<BufferSequence>::begin() const ->
|
||||
template<class BufferSequence>
|
||||
inline
|
||||
auto
|
||||
prepared_buffers<BufferSequence>::end() const ->
|
||||
buffer_prefix_view<BufferSequence>::end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, true};
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -5,38 +5,30 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_IMPL_DYNABUF_READSTREAM_HPP
|
||||
#define BEAST_IMPL_DYNABUF_READSTREAM_HPP
|
||||
#ifndef BEAST_IMPL_BUFFERED_READ_STREAM_IPP
|
||||
#define BEAST_IMPL_BUFFERED_READ_STREAM_IPP
|
||||
|
||||
#include <beast/core/bind_handler.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/handler_concepts.hpp>
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <beast/core/type_traits.hpp>
|
||||
#include <beast/core/detail/config.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
namespace beast {
|
||||
|
||||
template<class Stream, class DynamicBuffer>
|
||||
template<class MutableBufferSequence, class Handler>
|
||||
class dynabuf_readstream<
|
||||
class buffered_read_stream<
|
||||
Stream, DynamicBuffer>::read_some_op
|
||||
{
|
||||
// VFALCO What about bool cont for is_continuation?
|
||||
struct data
|
||||
{
|
||||
dynabuf_readstream& srs;
|
||||
MutableBufferSequence bs;
|
||||
int state = 0;
|
||||
|
||||
data(Handler&, dynabuf_readstream& srs_,
|
||||
MutableBufferSequence const& bs_)
|
||||
: srs(srs_)
|
||||
, bs(bs_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
int step_ = 0;
|
||||
buffered_read_stream& s_;
|
||||
MutableBufferSequence b_;
|
||||
Handler h_;
|
||||
|
||||
public:
|
||||
read_some_op(read_some_op&&) = default;
|
||||
@@ -44,11 +36,12 @@ public:
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
read_some_op(DeducedHandler&& h,
|
||||
dynabuf_readstream& srs, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
srs, std::forward<Args>(args)...)
|
||||
buffered_read_stream& s,
|
||||
MutableBufferSequence const& b)
|
||||
: s_(s)
|
||||
, b_(b)
|
||||
, h_(std::forward<DeducedHandler>(h))
|
||||
{
|
||||
(*this)(error_code{}, 0);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -59,99 +52,92 @@ public:
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_some_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_some_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, op->d_.handler());
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_some_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
is_continuation(op->d_.handler());
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return asio_handler_is_continuation(
|
||||
std::addressof(op->h_));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_some_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, op->d_.handler());
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(f, std::addressof(op->h_));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer>
|
||||
template<class MutableBufferSequence, class Handler>
|
||||
void
|
||||
dynabuf_readstream<Stream, DynamicBuffer>::
|
||||
buffered_read_stream<Stream, DynamicBuffer>::
|
||||
read_some_op<MutableBufferSequence, Handler>::operator()(
|
||||
error_code const& ec, std::size_t bytes_transferred)
|
||||
{
|
||||
auto& d = *d_;
|
||||
while(! ec && d.state != 99)
|
||||
switch(step_)
|
||||
{
|
||||
switch(d.state)
|
||||
case 0:
|
||||
if(s_.sb_.size() == 0)
|
||||
{
|
||||
case 0:
|
||||
if(d.srs.sb_.size() == 0)
|
||||
if(s_.capacity_ == 0)
|
||||
{
|
||||
d.state =
|
||||
d.srs.capacity_ > 0 ? 2 : 1;
|
||||
break;
|
||||
// read (unbuffered)
|
||||
step_ = 1;
|
||||
return s_.next_layer_.async_read_some(
|
||||
b_, std::move(*this));
|
||||
}
|
||||
d.state = 4;
|
||||
d.srs.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
return;
|
||||
|
||||
case 1:
|
||||
// read (unbuffered)
|
||||
d.state = 99;
|
||||
d.srs.next_layer_.async_read_some(
|
||||
d.bs, std::move(*this));
|
||||
return;
|
||||
|
||||
case 2:
|
||||
// read
|
||||
d.state = 3;
|
||||
d.srs.next_layer_.async_read_some(
|
||||
d.srs.sb_.prepare(d.srs.capacity_),
|
||||
step_ = 2;
|
||||
return s_.next_layer_.async_read_some(
|
||||
s_.sb_.prepare(s_.capacity_),
|
||||
std::move(*this));
|
||||
return;
|
||||
|
||||
// got data
|
||||
case 3:
|
||||
d.state = 4;
|
||||
d.srs.sb_.commit(bytes_transferred);
|
||||
break;
|
||||
|
||||
// copy
|
||||
case 4:
|
||||
bytes_transferred =
|
||||
boost::asio::buffer_copy(
|
||||
d.bs, d.srs.sb_.data());
|
||||
d.srs.sb_.consume(bytes_transferred);
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
step_ = 3;
|
||||
s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
return;
|
||||
|
||||
case 1:
|
||||
// upcall
|
||||
break;
|
||||
|
||||
case 2:
|
||||
s_.sb_.commit(bytes_transferred);
|
||||
BEAST_FALLTHROUGH;
|
||||
|
||||
case 3:
|
||||
bytes_transferred =
|
||||
boost::asio::buffer_copy(b_, s_.sb_.data());
|
||||
s_.sb_.consume(bytes_transferred);
|
||||
break;
|
||||
}
|
||||
d_.invoke(ec, bytes_transferred);
|
||||
h_(ec, bytes_transferred);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream, class DynamicBuffer>
|
||||
template<class... Args>
|
||||
dynabuf_readstream<Stream, DynamicBuffer>::
|
||||
dynabuf_readstream(Args&&... args)
|
||||
buffered_read_stream<Stream, DynamicBuffer>::
|
||||
buffered_read_stream(Args&&... args)
|
||||
: next_layer_(std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
@@ -159,18 +145,17 @@ dynabuf_readstream(Args&&... args)
|
||||
template<class Stream, class DynamicBuffer>
|
||||
template<class ConstBufferSequence, class WriteHandler>
|
||||
auto
|
||||
dynabuf_readstream<Stream, DynamicBuffer>::
|
||||
buffered_read_stream<Stream, DynamicBuffer>::
|
||||
async_write_some(ConstBufferSequence const& buffers,
|
||||
WriteHandler&& handler) ->
|
||||
typename async_completion<
|
||||
WriteHandler, void(error_code)>::result_type
|
||||
WriteHandler&& handler) ->
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
{
|
||||
static_assert(is_AsyncWriteStream<next_layer_type>::value,
|
||||
static_assert(is_async_write_stream<next_layer_type>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
static_assert(is_ConstBufferSequence<
|
||||
static_assert(is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
static_assert(is_CompletionHandler<WriteHandler,
|
||||
static_assert(is_completion_handler<WriteHandler,
|
||||
void(error_code, std::size_t)>::value,
|
||||
"WriteHandler requirements not met");
|
||||
return next_layer_.async_write_some(buffers,
|
||||
@@ -180,32 +165,32 @@ async_write_some(ConstBufferSequence const& buffers,
|
||||
template<class Stream, class DynamicBuffer>
|
||||
template<class MutableBufferSequence>
|
||||
std::size_t
|
||||
dynabuf_readstream<Stream, DynamicBuffer>::
|
||||
buffered_read_stream<Stream, DynamicBuffer>::
|
||||
read_some(
|
||||
MutableBufferSequence const& buffers)
|
||||
{
|
||||
static_assert(is_SyncReadStream<next_layer_type>::value,
|
||||
static_assert(is_sync_read_stream<next_layer_type>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_MutableBufferSequence<
|
||||
static_assert(is_mutable_buffer_sequence<
|
||||
MutableBufferSequence>::value,
|
||||
"MutableBufferSequence requirements not met");
|
||||
error_code ec;
|
||||
auto n = read_some(buffers, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return n;
|
||||
}
|
||||
|
||||
template<class Stream, class DynamicBuffer>
|
||||
template<class MutableBufferSequence>
|
||||
std::size_t
|
||||
dynabuf_readstream<Stream, DynamicBuffer>::
|
||||
buffered_read_stream<Stream, DynamicBuffer>::
|
||||
read_some(MutableBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_SyncReadStream<next_layer_type>::value,
|
||||
static_assert(is_sync_read_stream<next_layer_type>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_MutableBufferSequence<
|
||||
static_assert(is_mutable_buffer_sequence<
|
||||
MutableBufferSequence>::value,
|
||||
"MutableBufferSequence requirements not met");
|
||||
using boost::asio::buffer_size;
|
||||
@@ -219,6 +204,10 @@ read_some(MutableBufferSequence const& buffers,
|
||||
if(ec)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
auto bytes_transferred =
|
||||
buffer_copy(buffers, sb_.data());
|
||||
sb_.consume(bytes_transferred);
|
||||
@@ -228,25 +217,23 @@ read_some(MutableBufferSequence const& buffers,
|
||||
template<class Stream, class DynamicBuffer>
|
||||
template<class MutableBufferSequence, class ReadHandler>
|
||||
auto
|
||||
dynabuf_readstream<Stream, DynamicBuffer>::
|
||||
async_read_some(
|
||||
MutableBufferSequence const& buffers,
|
||||
buffered_read_stream<Stream, DynamicBuffer>::
|
||||
async_read_some(MutableBufferSequence const& buffers,
|
||||
ReadHandler&& handler) ->
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
async_return_type<ReadHandler, void(error_code)>
|
||||
{
|
||||
static_assert(is_AsyncReadStream<next_layer_type>::value,
|
||||
static_assert(is_async_read_stream<next_layer_type>::value,
|
||||
"Stream requirements not met");
|
||||
static_assert(is_MutableBufferSequence<
|
||||
static_assert(is_mutable_buffer_sequence<
|
||||
MutableBufferSequence>::value,
|
||||
"MutableBufferSequence requirements not met");
|
||||
beast::async_completion<
|
||||
ReadHandler, void(error_code, std::size_t)
|
||||
> completion{handler};
|
||||
read_some_op<MutableBufferSequence,
|
||||
decltype(completion.handler)>{
|
||||
completion.handler, *this, buffers};
|
||||
return completion.result.get();
|
||||
async_completion<ReadHandler,
|
||||
void(error_code, std::size_t)> init{handler};
|
||||
read_some_op<MutableBufferSequence, handler_type<
|
||||
ReadHandler, void(error_code, std::size_t)>>{
|
||||
init.completion_handler, *this, buffers}(
|
||||
error_code{}, 0);
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
} // beast
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
@@ -414,8 +415,8 @@ buffers_adapter<MutableBufferSequence>::prepare(std::size_t n) ->
|
||||
}
|
||||
}
|
||||
if(n > 0)
|
||||
throw detail::make_exception<std::length_error>(
|
||||
"no space", __FILE__, __LINE__);
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"buffer overflow"});
|
||||
return mutable_buffers_type{*this};
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
#ifndef BEAST_IMPL_CONSUMING_BUFFERS_IPP
|
||||
#define BEAST_IMPL_CONSUMING_BUFFERS_IPP
|
||||
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <beast/core/type_traits.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
@@ -18,13 +17,13 @@
|
||||
|
||||
namespace beast {
|
||||
|
||||
template<class BufferSequence>
|
||||
class consuming_buffers<BufferSequence>::const_iterator
|
||||
template<class Buffers>
|
||||
class consuming_buffers<Buffers>::const_iterator
|
||||
{
|
||||
friend class consuming_buffers<BufferSequence>;
|
||||
friend class consuming_buffers<Buffers>;
|
||||
|
||||
using iter_type =
|
||||
typename BufferSequence::const_iterator;
|
||||
typename Buffers::const_iterator;
|
||||
|
||||
iter_type it_;
|
||||
consuming_buffers const* b_ = nullptr;
|
||||
@@ -110,8 +109,17 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
template<class BufferSequence>
|
||||
consuming_buffers<BufferSequence>::
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Buffers>
|
||||
consuming_buffers<Buffers>::
|
||||
consuming_buffers()
|
||||
: begin_(bs_.begin())
|
||||
{
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
consuming_buffers<Buffers>::
|
||||
consuming_buffers(consuming_buffers&& other)
|
||||
: consuming_buffers(std::move(other),
|
||||
std::distance<iter_type>(
|
||||
@@ -119,8 +127,8 @@ consuming_buffers(consuming_buffers&& other)
|
||||
{
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
consuming_buffers<BufferSequence>::
|
||||
template<class Buffers>
|
||||
consuming_buffers<Buffers>::
|
||||
consuming_buffers(consuming_buffers const& other)
|
||||
: consuming_buffers(other,
|
||||
std::distance<iter_type>(
|
||||
@@ -128,9 +136,35 @@ consuming_buffers(consuming_buffers const& other)
|
||||
{
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
template<class Buffers>
|
||||
consuming_buffers<Buffers>::
|
||||
consuming_buffers(Buffers const& bs)
|
||||
: bs_(bs)
|
||||
, begin_(bs_.begin())
|
||||
{
|
||||
static_assert(
|
||||
is_const_buffer_sequence<Buffers>::value||
|
||||
is_mutable_buffer_sequence<Buffers>::value,
|
||||
"BufferSequence requirements not met");
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
template<class... Args>
|
||||
consuming_buffers<Buffers>::
|
||||
consuming_buffers(boost::in_place_init_t, Args&&... args)
|
||||
: bs_(std::forward<Args>(args)...)
|
||||
, begin_(bs_.begin())
|
||||
{
|
||||
static_assert(sizeof...(Args) > 0,
|
||||
"Missing constructor arguments");
|
||||
static_assert(
|
||||
std::is_constructible<Buffers, Args...>::value,
|
||||
"Buffers not constructible from arguments");
|
||||
}
|
||||
|
||||
template<class Buffers>
|
||||
auto
|
||||
consuming_buffers<BufferSequence>::
|
||||
consuming_buffers<Buffers>::
|
||||
operator=(consuming_buffers&& other) ->
|
||||
consuming_buffers&
|
||||
{
|
||||
@@ -142,9 +176,9 @@ operator=(consuming_buffers&& other) ->
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
template<class Buffers>
|
||||
auto
|
||||
consuming_buffers<BufferSequence>::
|
||||
consuming_buffers<Buffers>::
|
||||
operator=(consuming_buffers const& other) ->
|
||||
consuming_buffers&
|
||||
{
|
||||
@@ -156,40 +190,29 @@ operator=(consuming_buffers const& other) ->
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
consuming_buffers<BufferSequence>::
|
||||
consuming_buffers(BufferSequence const& bs)
|
||||
: bs_(bs)
|
||||
, begin_(bs_.begin())
|
||||
{
|
||||
static_assert(
|
||||
is_BufferSequence<BufferSequence, value_type>::value,
|
||||
"BufferSequence requirements not met");
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
template<class Buffers>
|
||||
inline
|
||||
auto
|
||||
consuming_buffers<BufferSequence>::
|
||||
consuming_buffers<Buffers>::
|
||||
begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, begin_};
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
template<class Buffers>
|
||||
inline
|
||||
auto
|
||||
consuming_buffers<BufferSequence>::
|
||||
consuming_buffers<Buffers>::
|
||||
end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, bs_.end()};
|
||||
}
|
||||
|
||||
template<class BufferSequence>
|
||||
template<class Buffers>
|
||||
void
|
||||
consuming_buffers<BufferSequence>::
|
||||
consuming_buffers<Buffers>::
|
||||
consume(std::size_t n)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
|
||||
331
include/beast/core/impl/file_posix.ipp
Normal file
331
include/beast/core/impl/file_posix.ipp
Normal file
@@ -0,0 +1,331 @@
|
||||
//
|
||||
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_CORE_IMPL_FILE_POSIX_IPP
|
||||
#define BEAST_CORE_IMPL_FILE_POSIX_IPP
|
||||
|
||||
#include <limits>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
|
||||
namespace beast {
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
int
|
||||
file_posix_close(int fd)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
if(! ::close(fd))
|
||||
break;
|
||||
int const ev = errno;
|
||||
if(errno != EINTR)
|
||||
return ev;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
inline
|
||||
file_posix::
|
||||
~file_posix()
|
||||
{
|
||||
if(fd_ != -1)
|
||||
detail::file_posix_close(fd_);
|
||||
}
|
||||
|
||||
inline
|
||||
file_posix::
|
||||
file_posix(file_posix&& other)
|
||||
: fd_(other.fd_)
|
||||
{
|
||||
other.fd_ = -1;
|
||||
}
|
||||
|
||||
inline
|
||||
file_posix&
|
||||
file_posix::
|
||||
operator=(file_posix&& other)
|
||||
{
|
||||
if(&other == this)
|
||||
return *this;
|
||||
if(fd_ != -1)
|
||||
detail::file_posix_close(fd_);
|
||||
fd_ = other.fd_;
|
||||
other.fd_ = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_posix::
|
||||
native_handle(native_handle_type fd)
|
||||
{
|
||||
if(fd_ != -1)
|
||||
detail::file_posix_close(fd_);
|
||||
fd_ = fd;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_posix::
|
||||
close(error_code& ec)
|
||||
{
|
||||
if(fd_ != -1)
|
||||
{
|
||||
auto const ev =
|
||||
detail::file_posix_close(fd_);
|
||||
if(ev)
|
||||
ec.assign(ev, generic_category());
|
||||
else
|
||||
ec.assign(0, ec.category());
|
||||
fd_ = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_posix::
|
||||
open(char const* path, file_mode mode, error_code& ec)
|
||||
{
|
||||
if(fd_ != -1)
|
||||
{
|
||||
auto const ev =
|
||||
detail::file_posix_close(fd_);
|
||||
if(ev)
|
||||
ec.assign(ev, generic_category());
|
||||
else
|
||||
ec.assign(0, ec.category());
|
||||
fd_ = -1;
|
||||
}
|
||||
int f = 0;
|
||||
#ifndef __APPLE__
|
||||
int advise = 0;
|
||||
#endif
|
||||
switch(mode)
|
||||
{
|
||||
default:
|
||||
case file_mode::read:
|
||||
f = O_RDONLY;
|
||||
#ifndef __APPLE__
|
||||
advise = POSIX_FADV_RANDOM;
|
||||
#endif
|
||||
break;
|
||||
case file_mode::scan:
|
||||
f = O_RDONLY;
|
||||
#ifndef __APPLE__
|
||||
advise = POSIX_FADV_SEQUENTIAL;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case file_mode::write:
|
||||
f = O_RDWR | O_CREAT | O_TRUNC;
|
||||
#ifndef __APPLE__
|
||||
advise = POSIX_FADV_RANDOM;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case file_mode::write_new:
|
||||
f = O_RDWR | O_CREAT | O_EXCL;
|
||||
#ifndef __APPLE__
|
||||
advise = POSIX_FADV_RANDOM;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case file_mode::write_existing:
|
||||
f = O_RDWR | O_EXCL;
|
||||
#ifndef __APPLE__
|
||||
advise = POSIX_FADV_RANDOM;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case file_mode::append:
|
||||
f = O_RDWR | O_CREAT | O_TRUNC;
|
||||
#ifndef __APPLE__
|
||||
advise = POSIX_FADV_SEQUENTIAL;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case file_mode::append_new:
|
||||
f = O_RDWR | O_CREAT | O_EXCL;
|
||||
#ifndef __APPLE__
|
||||
advise = POSIX_FADV_SEQUENTIAL;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case file_mode::append_existing:
|
||||
f = O_RDWR | O_EXCL;
|
||||
#ifndef __APPLE__
|
||||
advise = POSIX_FADV_SEQUENTIAL;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
for(;;)
|
||||
{
|
||||
fd_ = ::open(path, f, 0644);
|
||||
if(fd_ != -1)
|
||||
break;
|
||||
auto const ev = errno;
|
||||
if(ev != EINTR)
|
||||
{
|
||||
ec.assign(ev, generic_category());
|
||||
return;
|
||||
}
|
||||
}
|
||||
#ifndef __APPLE__
|
||||
if(::posix_fadvise(fd_, 0, 0, advise))
|
||||
{
|
||||
auto const ev = errno;
|
||||
detail::file_posix_close(fd_);
|
||||
fd_ = -1;
|
||||
ec.assign(ev, generic_category());
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
inline
|
||||
std::uint64_t
|
||||
file_posix::
|
||||
size(error_code& ec) const
|
||||
{
|
||||
if(fd_ == -1)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
struct stat st;
|
||||
if(::fstat(fd_, &st) != 0)
|
||||
{
|
||||
ec.assign(errno, generic_category());
|
||||
return 0;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return st.st_size;
|
||||
}
|
||||
|
||||
inline
|
||||
std::uint64_t
|
||||
file_posix::
|
||||
pos(error_code& ec) const
|
||||
{
|
||||
if(fd_ == -1)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
auto const result = ::lseek(fd_, 0, SEEK_CUR);
|
||||
if(result == (off_t)-1)
|
||||
{
|
||||
ec.assign(errno, generic_category());
|
||||
return 0;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return result;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_posix::
|
||||
seek(std::uint64_t offset, error_code& ec)
|
||||
{
|
||||
if(fd_ == -1)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return;
|
||||
}
|
||||
auto const result = ::lseek(fd_, offset, SEEK_SET);
|
||||
if(result == static_cast<off_t>(-1))
|
||||
{
|
||||
ec.assign(errno, generic_category());
|
||||
return;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
inline
|
||||
std::size_t
|
||||
file_posix::
|
||||
read(void* buffer, std::size_t n, error_code& ec) const
|
||||
{
|
||||
if(fd_ == -1)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
std::size_t nread = 0;
|
||||
while(n > 0)
|
||||
{
|
||||
auto const amount = static_cast<ssize_t>((std::min)(
|
||||
n, static_cast<std::size_t>(SSIZE_MAX)));
|
||||
auto const result = ::read(fd_, buffer, amount);
|
||||
if(result == -1)
|
||||
{
|
||||
auto const ev = errno;
|
||||
if(ev == EINTR)
|
||||
continue;
|
||||
ec.assign(ev, generic_category());
|
||||
return nread;
|
||||
}
|
||||
if(result == 0)
|
||||
{
|
||||
// short read
|
||||
return nread;
|
||||
}
|
||||
n -= result;
|
||||
nread += result;
|
||||
buffer = reinterpret_cast<char*>(buffer) + result;
|
||||
}
|
||||
return nread;
|
||||
}
|
||||
|
||||
inline
|
||||
std::size_t
|
||||
file_posix::
|
||||
write(void const* buffer, std::size_t n, error_code& ec)
|
||||
{
|
||||
if(fd_ == -1)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
std::size_t nwritten = 0;
|
||||
while(n > 0)
|
||||
{
|
||||
auto const amount = static_cast<ssize_t>((std::min)(
|
||||
n, static_cast<std::size_t>(SSIZE_MAX)));
|
||||
auto const result = ::write(fd_, buffer, amount);
|
||||
if(result == -1)
|
||||
{
|
||||
auto const ev = errno;
|
||||
if(ev == EINTR)
|
||||
continue;
|
||||
ec.assign(ev, generic_category());
|
||||
return nwritten;
|
||||
}
|
||||
n -= result;
|
||||
nwritten += result;
|
||||
buffer = reinterpret_cast<char const*>(buffer) + result;
|
||||
}
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
225
include/beast/core/impl/file_stdio.ipp
Normal file
225
include/beast/core/impl/file_stdio.ipp
Normal file
@@ -0,0 +1,225 @@
|
||||
//
|
||||
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_CORE_IMPL_FILE_STDIO_IPP
|
||||
#define BEAST_CORE_IMPL_FILE_STDIO_IPP
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace beast {
|
||||
|
||||
inline
|
||||
file_stdio::
|
||||
~file_stdio()
|
||||
{
|
||||
if(f_)
|
||||
fclose(f_);
|
||||
}
|
||||
|
||||
inline
|
||||
file_stdio::
|
||||
file_stdio(file_stdio&& other)
|
||||
: f_(other.f_)
|
||||
{
|
||||
other.f_ = nullptr;
|
||||
}
|
||||
|
||||
inline
|
||||
file_stdio&
|
||||
file_stdio::
|
||||
operator=(file_stdio&& other)
|
||||
{
|
||||
if(&other == this)
|
||||
return *this;
|
||||
if(f_)
|
||||
fclose(f_);
|
||||
f_ = other.f_;
|
||||
other.f_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_stdio::
|
||||
native_handle(FILE* f)
|
||||
{
|
||||
if(f_)
|
||||
fclose(f_);
|
||||
f_ = f;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_stdio::
|
||||
close(error_code& ec)
|
||||
{
|
||||
if(f_)
|
||||
{
|
||||
int failed = fclose(f_);
|
||||
f_ = nullptr;
|
||||
if(failed)
|
||||
{
|
||||
ec.assign(errno, generic_category());
|
||||
return;
|
||||
}
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_stdio::
|
||||
open(char const* path, file_mode mode, error_code& ec)
|
||||
{
|
||||
if(f_)
|
||||
{
|
||||
fclose(f_);
|
||||
f_ = nullptr;
|
||||
}
|
||||
char const* s;
|
||||
switch(mode)
|
||||
{
|
||||
default:
|
||||
case file_mode::read: s = "rb"; break;
|
||||
case file_mode::scan: s = "rb"; break;
|
||||
case file_mode::write: s = "wb"; break;
|
||||
case file_mode::write_new: s = "wbx"; break;
|
||||
case file_mode::write_existing: s = "wb"; break;
|
||||
case file_mode::append: s = "ab"; break;
|
||||
case file_mode::append_new: s = "abx"; break;
|
||||
case file_mode::append_existing: s = "ab"; break;
|
||||
}
|
||||
f_ = std::fopen(path, s);
|
||||
if(! f_)
|
||||
{
|
||||
ec.assign(errno, generic_category());
|
||||
return;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
inline
|
||||
std::uint64_t
|
||||
file_stdio::
|
||||
size(error_code& ec) const
|
||||
{
|
||||
if(! f_)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
long pos = std::ftell(f_);
|
||||
if(pos == -1L)
|
||||
{
|
||||
ec.assign(errno, generic_category());
|
||||
return 0;
|
||||
}
|
||||
int result = std::fseek(f_, 0, SEEK_END);
|
||||
if(result != 0)
|
||||
{
|
||||
ec.assign(errno, generic_category());
|
||||
return 0;
|
||||
}
|
||||
long size = std::ftell(f_);
|
||||
if(size == -1L)
|
||||
{
|
||||
ec.assign(errno, generic_category());
|
||||
std::fseek(f_, pos, SEEK_SET);
|
||||
return 0;
|
||||
}
|
||||
result = std::fseek(f_, pos, SEEK_SET);
|
||||
if(result != 0)
|
||||
ec.assign(errno, generic_category());
|
||||
else
|
||||
ec.assign(0, ec.category());
|
||||
return size;
|
||||
}
|
||||
|
||||
inline
|
||||
std::uint64_t
|
||||
file_stdio::
|
||||
pos(error_code& ec) const
|
||||
{
|
||||
if(! f_)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
long pos = std::ftell(f_);
|
||||
if(pos == -1L)
|
||||
{
|
||||
ec.assign(errno, generic_category());
|
||||
return 0;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return pos;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_stdio::
|
||||
seek(std::uint64_t offset, error_code& ec)
|
||||
{
|
||||
if(! f_)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return;
|
||||
}
|
||||
if(offset > (std::numeric_limits<long>::max)())
|
||||
{
|
||||
ec = make_error_code(errc::invalid_seek);
|
||||
return;
|
||||
}
|
||||
int result = std::fseek(f_,
|
||||
static_cast<long>(offset), SEEK_SET);
|
||||
if(result != 0)
|
||||
ec.assign(errno, generic_category());
|
||||
else
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
inline
|
||||
std::size_t
|
||||
file_stdio::
|
||||
read(void* buffer, std::size_t n, error_code& ec) const
|
||||
{
|
||||
if(! f_)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
auto nread = std::fread(buffer, 1, n, f_);
|
||||
if(std::ferror(f_))
|
||||
{
|
||||
ec.assign(errno, generic_category());
|
||||
return 0;
|
||||
}
|
||||
return nread;
|
||||
}
|
||||
|
||||
inline
|
||||
std::size_t
|
||||
file_stdio::
|
||||
write(void const* buffer, std::size_t n, error_code& ec)
|
||||
{
|
||||
if(! f_)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
auto nwritten = std::fwrite(buffer, 1, n, f_);
|
||||
if(std::ferror(f_))
|
||||
{
|
||||
ec.assign(errno, generic_category());
|
||||
return 0;
|
||||
}
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
356
include/beast/core/impl/file_win32.ipp
Normal file
356
include/beast/core/impl/file_win32.ipp
Normal file
@@ -0,0 +1,356 @@
|
||||
//
|
||||
// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_CORE_IMPL_FILE_WIN32_IPP
|
||||
#define BEAST_CORE_IMPL_FILE_WIN32_IPP
|
||||
|
||||
#include <boost/detail/winapi/access_rights.hpp>
|
||||
#include <boost/detail/winapi/error_codes.hpp>
|
||||
#include <boost/detail/winapi/file_management.hpp>
|
||||
#include <boost/detail/winapi/get_last_error.hpp>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// VFALCO Can't seem to get boost/detail/winapi to work with
|
||||
// this so use the non-Ex version for now.
|
||||
inline
|
||||
boost::detail::winapi::BOOL_
|
||||
set_file_pointer_ex(
|
||||
boost::detail::winapi::HANDLE_ hFile,
|
||||
boost::detail::winapi::LARGE_INTEGER_ lpDistanceToMove,
|
||||
boost::detail::winapi::PLARGE_INTEGER_ lpNewFilePointer,
|
||||
boost::detail::winapi::DWORD_ dwMoveMethod)
|
||||
{
|
||||
auto dwHighPart = lpDistanceToMove.u.HighPart;
|
||||
auto dwLowPart = boost::detail::winapi::SetFilePointer(
|
||||
hFile,
|
||||
lpDistanceToMove.u.LowPart,
|
||||
&dwHighPart,
|
||||
dwMoveMethod);
|
||||
if(dwLowPart == boost::detail::winapi::INVALID_SET_FILE_POINTER_)
|
||||
return 0;
|
||||
if(lpNewFilePointer)
|
||||
{
|
||||
lpNewFilePointer->u.LowPart = dwLowPart;
|
||||
lpNewFilePointer->u.HighPart = dwHighPart;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
inline
|
||||
file_win32::
|
||||
~file_win32()
|
||||
{
|
||||
if(h_ != boost::detail::winapi::INVALID_HANDLE_VALUE_)
|
||||
boost::detail::winapi::CloseHandle(h_);
|
||||
}
|
||||
|
||||
inline
|
||||
file_win32::
|
||||
file_win32(file_win32&& other)
|
||||
: h_(other.h_)
|
||||
{
|
||||
other.h_ = boost::detail::winapi::INVALID_HANDLE_VALUE_;
|
||||
}
|
||||
|
||||
inline
|
||||
file_win32&
|
||||
file_win32::
|
||||
operator=(file_win32&& other)
|
||||
{
|
||||
if(&other == this)
|
||||
return *this;
|
||||
if(h_)
|
||||
boost::detail::winapi::CloseHandle(h_);
|
||||
h_ = other.h_;
|
||||
other.h_ = boost::detail::winapi::INVALID_HANDLE_VALUE_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_win32::
|
||||
native_handle(native_handle_type h)
|
||||
{
|
||||
if(h_ != boost::detail::winapi::INVALID_HANDLE_VALUE_)
|
||||
boost::detail::winapi::CloseHandle(h_);
|
||||
h_ = h;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_win32::
|
||||
close(error_code& ec)
|
||||
{
|
||||
if(h_ != boost::detail::winapi::INVALID_HANDLE_VALUE_)
|
||||
{
|
||||
if(! boost::detail::winapi::CloseHandle(h_))
|
||||
ec.assign(boost::detail::winapi::GetLastError(),
|
||||
system_category());
|
||||
else
|
||||
ec.assign(0, ec.category());
|
||||
h_ = boost::detail::winapi::INVALID_HANDLE_VALUE_;
|
||||
}
|
||||
else
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_win32::
|
||||
open(char const* path, file_mode mode, error_code& ec)
|
||||
{
|
||||
if(h_ != boost::detail::winapi::INVALID_HANDLE_VALUE_)
|
||||
{
|
||||
boost::detail::winapi::CloseHandle(h_);
|
||||
h_ = boost::detail::winapi::INVALID_HANDLE_VALUE_;
|
||||
}
|
||||
boost::detail::winapi::DWORD_ dw1 = 0;
|
||||
boost::detail::winapi::DWORD_ dw2 = 0;
|
||||
boost::detail::winapi::DWORD_ dw3 = 0;
|
||||
/*
|
||||
| When the file...
|
||||
This argument: | Exists Does not exist
|
||||
-------------------------+------------------------------------------------------
|
||||
CREATE_ALWAYS | Truncates Creates
|
||||
CREATE_NEW +-----------+ Fails Creates
|
||||
OPEN_ALWAYS ===| does this |===> Opens Creates
|
||||
OPEN_EXISTING +-----------+ Opens Fails
|
||||
TRUNCATE_EXISTING | Truncates Fails
|
||||
*/
|
||||
switch(mode)
|
||||
{
|
||||
default:
|
||||
case file_mode::read:
|
||||
dw1 = boost::detail::winapi::GENERIC_READ_;
|
||||
dw2 = boost::detail::winapi::OPEN_EXISTING_;
|
||||
dw3 = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
|
||||
break;
|
||||
|
||||
case file_mode::scan:
|
||||
dw1 = boost::detail::winapi::GENERIC_READ_;
|
||||
dw2 = boost::detail::winapi::OPEN_EXISTING_;
|
||||
dw3 = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
|
||||
break;
|
||||
|
||||
case file_mode::write:
|
||||
dw1 = boost::detail::winapi::GENERIC_READ_ |
|
||||
boost::detail::winapi::GENERIC_WRITE_;
|
||||
dw2 = boost::detail::winapi::CREATE_ALWAYS_;
|
||||
dw3 = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
|
||||
break;
|
||||
|
||||
case file_mode::write_new:
|
||||
dw1 = boost::detail::winapi::GENERIC_READ_ |
|
||||
boost::detail::winapi::GENERIC_WRITE_;
|
||||
dw2 = boost::detail::winapi::CREATE_NEW_;
|
||||
dw3 = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
|
||||
break;
|
||||
|
||||
case file_mode::write_existing:
|
||||
dw1 = boost::detail::winapi::GENERIC_READ_ |
|
||||
boost::detail::winapi::GENERIC_WRITE_;
|
||||
dw2 = boost::detail::winapi::OPEN_EXISTING_;
|
||||
dw3 = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
|
||||
break;
|
||||
|
||||
case file_mode::append:
|
||||
dw1 = boost::detail::winapi::GENERIC_READ_ |
|
||||
boost::detail::winapi::GENERIC_WRITE_;
|
||||
dw2 = boost::detail::winapi::CREATE_ALWAYS_;
|
||||
dw3 = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
|
||||
break;
|
||||
|
||||
case file_mode::append_new:
|
||||
dw1 = boost::detail::winapi::GENERIC_READ_ |
|
||||
boost::detail::winapi::GENERIC_WRITE_;
|
||||
dw2 = boost::detail::winapi::CREATE_NEW_;
|
||||
dw3 = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
|
||||
break;
|
||||
|
||||
case file_mode::append_existing:
|
||||
dw1 = boost::detail::winapi::GENERIC_READ_ |
|
||||
boost::detail::winapi::GENERIC_WRITE_;
|
||||
dw2 = boost::detail::winapi::OPEN_EXISTING_;
|
||||
dw3 = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
|
||||
break;
|
||||
}
|
||||
h_ = ::CreateFileA(
|
||||
path,
|
||||
dw1,
|
||||
0,
|
||||
NULL,
|
||||
dw2,
|
||||
dw3,
|
||||
NULL);
|
||||
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
|
||||
ec.assign(boost::detail::winapi::GetLastError(),
|
||||
system_category());
|
||||
else
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
inline
|
||||
std::uint64_t
|
||||
file_win32::
|
||||
size(error_code& ec) const
|
||||
{
|
||||
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
boost::detail::winapi::LARGE_INTEGER_ fileSize;
|
||||
if(! boost::detail::winapi::GetFileSizeEx(h_, &fileSize))
|
||||
{
|
||||
ec.assign(boost::detail::winapi::GetLastError(),
|
||||
system_category());
|
||||
return 0;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return fileSize.QuadPart;
|
||||
}
|
||||
|
||||
inline
|
||||
std::uint64_t
|
||||
file_win32::
|
||||
pos(error_code& ec)
|
||||
{
|
||||
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
boost::detail::winapi::LARGE_INTEGER_ in;
|
||||
boost::detail::winapi::LARGE_INTEGER_ out;
|
||||
in.QuadPart = 0;
|
||||
if(! detail::set_file_pointer_ex(h_, in, &out,
|
||||
boost::detail::winapi::FILE_CURRENT_))
|
||||
{
|
||||
ec.assign(boost::detail::winapi::GetLastError(),
|
||||
system_category());
|
||||
return 0;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return out.QuadPart;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
file_win32::
|
||||
seek(std::uint64_t offset, error_code& ec)
|
||||
{
|
||||
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return;
|
||||
}
|
||||
boost::detail::winapi::LARGE_INTEGER_ in;
|
||||
in.QuadPart = offset;
|
||||
if(! detail::set_file_pointer_ex(h_, in, 0,
|
||||
boost::detail::winapi::FILE_BEGIN_))
|
||||
{
|
||||
ec.assign(boost::detail::winapi::GetLastError(),
|
||||
system_category());
|
||||
return;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
inline
|
||||
std::size_t
|
||||
file_win32::
|
||||
read(void* buffer, std::size_t n, error_code& ec)
|
||||
{
|
||||
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
std::size_t nread = 0;
|
||||
while(n > 0)
|
||||
{
|
||||
boost::detail::winapi::DWORD_ amount;
|
||||
if(n > (std::numeric_limits<
|
||||
boost::detail::winapi::DWORD_>::max)())
|
||||
amount = (std::numeric_limits<
|
||||
boost::detail::winapi::DWORD_>::max)();
|
||||
else
|
||||
amount = static_cast<
|
||||
boost::detail::winapi::DWORD_>(n);
|
||||
boost::detail::winapi::DWORD_ bytesRead;
|
||||
if(! ::ReadFile(h_, buffer, amount, &bytesRead, 0))
|
||||
{
|
||||
auto const dwError = ::GetLastError();
|
||||
if(dwError != boost::detail::winapi::ERROR_HANDLE_EOF_)
|
||||
ec.assign(::GetLastError(), system_category());
|
||||
else
|
||||
ec.assign(0, ec.category());
|
||||
return nread;
|
||||
}
|
||||
if(bytesRead == 0)
|
||||
return nread;
|
||||
n -= bytesRead;
|
||||
nread += bytesRead;
|
||||
buffer = reinterpret_cast<char*>(buffer) + bytesRead;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return nread;
|
||||
}
|
||||
|
||||
inline
|
||||
std::size_t
|
||||
file_win32::
|
||||
write(void const* buffer, std::size_t n, error_code& ec)
|
||||
{
|
||||
if(h_ == boost::detail::winapi::INVALID_HANDLE_VALUE_)
|
||||
{
|
||||
ec.assign(errc::invalid_argument, generic_category());
|
||||
return 0;
|
||||
}
|
||||
std::size_t nwritten = 0;
|
||||
while(n > 0)
|
||||
{
|
||||
boost::detail::winapi::DWORD_ amount;
|
||||
if(n > (std::numeric_limits<
|
||||
boost::detail::winapi::DWORD_>::max)())
|
||||
amount = (std::numeric_limits<
|
||||
boost::detail::winapi::DWORD_>::max)();
|
||||
else
|
||||
amount = static_cast<
|
||||
boost::detail::winapi::DWORD_>(n);
|
||||
boost::detail::winapi::DWORD_ bytesWritten;
|
||||
if(! ::WriteFile(h_, buffer, amount, &bytesWritten, 0))
|
||||
{
|
||||
auto const dwError = ::GetLastError();
|
||||
if(dwError != boost::detail::winapi::ERROR_HANDLE_EOF_)
|
||||
ec.assign(::GetLastError(), system_category());
|
||||
else
|
||||
ec.assign(0, ec.category());
|
||||
return nwritten;
|
||||
}
|
||||
if(bytesWritten == 0)
|
||||
return nwritten;
|
||||
n -= bytesWritten;
|
||||
nwritten += bytesWritten;
|
||||
buffer = reinterpret_cast<char const*>(buffer) + bytesWritten;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
471
include/beast/core/impl/flat_buffer.ipp
Normal file
471
include/beast/core/impl/flat_buffer.ipp
Normal file
@@ -0,0 +1,471 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_IMPL_FLAT_BUFFER_HPP
|
||||
#define BEAST_IMPL_FLAT_BUFFER_HPP
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/* Memory is laid out thusly:
|
||||
|
||||
begin_ ..|.. in_ ..|.. out_ ..|.. last_ ..|.. end_
|
||||
*/
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_buffer<Allocator>::
|
||||
~basic_flat_buffer()
|
||||
{
|
||||
if(begin_)
|
||||
alloc_traits::deallocate(
|
||||
this->member(), begin_, dist(begin_, end_));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_buffer<Allocator>::
|
||||
basic_flat_buffer()
|
||||
: begin_(nullptr)
|
||||
, in_(nullptr)
|
||||
, out_(nullptr)
|
||||
, last_(nullptr)
|
||||
, end_(nullptr)
|
||||
, max_((std::numeric_limits<std::size_t>::max)())
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_buffer<Allocator>::
|
||||
basic_flat_buffer(std::size_t limit)
|
||||
: begin_(nullptr)
|
||||
, in_(nullptr)
|
||||
, out_(nullptr)
|
||||
, last_(nullptr)
|
||||
, end_(nullptr)
|
||||
, max_(limit)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_buffer<Allocator>::
|
||||
basic_flat_buffer(Allocator const& alloc)
|
||||
: detail::empty_base_optimization<allocator_type>(alloc)
|
||||
, begin_(nullptr)
|
||||
, in_(nullptr)
|
||||
, out_(nullptr)
|
||||
, last_(nullptr)
|
||||
, end_(nullptr)
|
||||
, max_((std::numeric_limits<std::size_t>::max)())
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_buffer<Allocator>::
|
||||
basic_flat_buffer(std::size_t limit, Allocator const& alloc)
|
||||
: detail::empty_base_optimization<allocator_type>(alloc)
|
||||
, begin_(nullptr)
|
||||
, in_(nullptr)
|
||||
, out_(nullptr)
|
||||
, last_(nullptr)
|
||||
, end_(nullptr)
|
||||
, max_(limit)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_buffer<Allocator>::
|
||||
basic_flat_buffer(basic_flat_buffer&& other)
|
||||
: detail::empty_base_optimization<allocator_type>(
|
||||
std::move(other.member()))
|
||||
, begin_(other.begin_)
|
||||
, in_(other.in_)
|
||||
, out_(other.out_)
|
||||
, last_(out_)
|
||||
, end_(other.end_)
|
||||
, max_(other.max_)
|
||||
{
|
||||
other.begin_ = nullptr;
|
||||
other.in_ = nullptr;
|
||||
other.out_ = nullptr;
|
||||
other.last_ = nullptr;
|
||||
other.end_ = nullptr;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_buffer<Allocator>::
|
||||
basic_flat_buffer(basic_flat_buffer&& other,
|
||||
Allocator const& alloc)
|
||||
: detail::empty_base_optimization<allocator_type>(alloc)
|
||||
{
|
||||
if(this->member() != other.member())
|
||||
{
|
||||
begin_ = nullptr;
|
||||
in_ = nullptr;
|
||||
out_ = nullptr;
|
||||
last_ = nullptr;
|
||||
end_ = nullptr;
|
||||
max_ = other.max_;
|
||||
copy_from(other);
|
||||
other.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
begin_ = other.begin_;
|
||||
in_ = other.in_;
|
||||
out_ = other.out_;
|
||||
last_ = out_;
|
||||
end_ = other.end_;
|
||||
max_ = other.max_;
|
||||
other.begin_ = nullptr;
|
||||
other.in_ = nullptr;
|
||||
other.out_ = nullptr;
|
||||
other.last_ = nullptr;
|
||||
other.end_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_buffer<Allocator>::
|
||||
basic_flat_buffer(basic_flat_buffer const& other)
|
||||
: detail::empty_base_optimization<allocator_type>(
|
||||
alloc_traits::select_on_container_copy_construction(
|
||||
other.member()))
|
||||
, begin_(nullptr)
|
||||
, in_(nullptr)
|
||||
, out_(nullptr)
|
||||
, last_(nullptr)
|
||||
, end_(nullptr)
|
||||
, max_(other.max_)
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_flat_buffer<Allocator>::
|
||||
basic_flat_buffer(basic_flat_buffer const& other,
|
||||
Allocator const& alloc)
|
||||
: detail::empty_base_optimization<allocator_type>(alloc)
|
||||
, begin_(nullptr)
|
||||
, in_(nullptr)
|
||||
, out_(nullptr)
|
||||
, last_(nullptr)
|
||||
, end_(nullptr)
|
||||
, max_(other.max_)
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
basic_flat_buffer<Allocator>::
|
||||
basic_flat_buffer(
|
||||
basic_flat_buffer<OtherAlloc> const& other)
|
||||
: begin_(nullptr)
|
||||
, in_(nullptr)
|
||||
, out_(nullptr)
|
||||
, last_(nullptr)
|
||||
, end_(nullptr)
|
||||
, max_(other.max_)
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
basic_flat_buffer<Allocator>::
|
||||
basic_flat_buffer(basic_flat_buffer<OtherAlloc> const& other,
|
||||
Allocator const& alloc)
|
||||
: detail::empty_base_optimization<allocator_type>(alloc)
|
||||
, begin_(nullptr)
|
||||
, in_(nullptr)
|
||||
, out_(nullptr)
|
||||
, last_(nullptr)
|
||||
, end_(nullptr)
|
||||
, max_(other.max_)
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_flat_buffer<Allocator>::
|
||||
operator=(basic_flat_buffer&& other) ->
|
||||
basic_flat_buffer&
|
||||
{
|
||||
if(this != &other)
|
||||
move_assign(other,
|
||||
typename alloc_traits::propagate_on_container_move_assignment{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_flat_buffer<Allocator>::
|
||||
operator=(basic_flat_buffer const& other) ->
|
||||
basic_flat_buffer&
|
||||
{
|
||||
if(this != &other)
|
||||
copy_assign(other,
|
||||
typename alloc_traits::propagate_on_container_copy_assignment{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
auto
|
||||
basic_flat_buffer<Allocator>::
|
||||
operator=(basic_flat_buffer<OtherAlloc> const& other) ->
|
||||
basic_flat_buffer&
|
||||
{
|
||||
reset();
|
||||
max_ = other.max_;
|
||||
copy_from(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_flat_buffer<Allocator>::
|
||||
prepare(std::size_t n) ->
|
||||
mutable_buffers_type
|
||||
{
|
||||
if(n <= dist(out_, end_))
|
||||
{
|
||||
// existing capacity is sufficient
|
||||
last_ = out_ + n;
|
||||
return{out_, n};
|
||||
}
|
||||
auto const len = size();
|
||||
if(n <= capacity() - len)
|
||||
{
|
||||
// after a memmove,
|
||||
// existing capacity is sufficient
|
||||
if(len > 0)
|
||||
std::memmove(begin_, in_, len);
|
||||
in_ = begin_;
|
||||
out_ = in_ + len;
|
||||
last_ = out_ + n;
|
||||
return {out_, n};
|
||||
}
|
||||
// enforce maximum capacity
|
||||
if(n > max_ - len)
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"basic_flat_buffer overflow"});
|
||||
// allocate a new buffer
|
||||
auto const new_size = std::min<std::size_t>(
|
||||
max_,
|
||||
std::max<std::size_t>(2 * len, len + n));
|
||||
auto const p = alloc_traits::allocate(
|
||||
this->member(), new_size);
|
||||
if(begin_)
|
||||
{
|
||||
BOOST_ASSERT(p);
|
||||
BOOST_ASSERT(in_);
|
||||
std::memcpy(p, in_, len);
|
||||
alloc_traits::deallocate(
|
||||
this->member(), begin_, capacity());
|
||||
}
|
||||
begin_ = p;
|
||||
in_ = begin_;
|
||||
out_ = in_ + len;
|
||||
last_ = out_ + n;
|
||||
end_ = begin_ + new_size;
|
||||
return {out_, n};
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_flat_buffer<Allocator>::
|
||||
consume(std::size_t n)
|
||||
{
|
||||
if(n >= dist(in_, out_))
|
||||
{
|
||||
in_ = begin_;
|
||||
out_ = begin_;
|
||||
return;
|
||||
}
|
||||
in_ += n;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_flat_buffer<Allocator>::
|
||||
shrink_to_fit()
|
||||
{
|
||||
auto const len = size();
|
||||
if(len == capacity())
|
||||
return;
|
||||
char* p;
|
||||
if(len > 0)
|
||||
{
|
||||
BOOST_ASSERT(begin_);
|
||||
BOOST_ASSERT(in_);
|
||||
p = alloc_traits::allocate(
|
||||
this->member(), len);
|
||||
std::memcpy(p, in_, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
p = nullptr;
|
||||
}
|
||||
alloc_traits::deallocate(
|
||||
this->member(), begin_, dist(begin_, end_));
|
||||
begin_ = p;
|
||||
in_ = begin_;
|
||||
out_ = begin_ + len;
|
||||
last_ = out_;
|
||||
end_ = out_;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_flat_buffer<Allocator>::
|
||||
reset()
|
||||
{
|
||||
consume(size());
|
||||
shrink_to_fit();
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class DynamicBuffer>
|
||||
inline
|
||||
void
|
||||
basic_flat_buffer<Allocator>::
|
||||
copy_from(DynamicBuffer const& buffer)
|
||||
{
|
||||
if(buffer.size() == 0)
|
||||
return;
|
||||
using boost::asio::buffer_copy;
|
||||
commit(buffer_copy(
|
||||
prepare(buffer.size()), buffer.data()));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_flat_buffer<Allocator>::
|
||||
move_assign(basic_flat_buffer& other, std::true_type)
|
||||
{
|
||||
reset();
|
||||
this->member() = std::move(other.member());
|
||||
begin_ = other.begin_;
|
||||
in_ = other.in_;
|
||||
out_ = other.out_;
|
||||
last_ = out_;
|
||||
end_ = other.end_;
|
||||
max_ = other.max_;
|
||||
other.begin_ = nullptr;
|
||||
other.in_ = nullptr;
|
||||
other.out_ = nullptr;
|
||||
other.last_ = nullptr;
|
||||
other.end_ = nullptr;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_flat_buffer<Allocator>::
|
||||
move_assign(basic_flat_buffer& other, std::false_type)
|
||||
{
|
||||
reset();
|
||||
if(this->member() != other.member())
|
||||
{
|
||||
copy_from(other);
|
||||
other.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
move_assign(other, std::true_type{});
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_flat_buffer<Allocator>::
|
||||
copy_assign(basic_flat_buffer const& other, std::true_type)
|
||||
{
|
||||
reset();
|
||||
max_ = other.max_;
|
||||
this->member() = other.member();
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_flat_buffer<Allocator>::
|
||||
copy_assign(basic_flat_buffer const& other, std::false_type)
|
||||
{
|
||||
reset();
|
||||
max_ = other.max_;
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_flat_buffer<Allocator>::
|
||||
swap(basic_flat_buffer& other)
|
||||
{
|
||||
swap(other, typename
|
||||
alloc_traits::propagate_on_container_swap{});
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_flat_buffer<Allocator>::
|
||||
swap(basic_flat_buffer& other, std::true_type)
|
||||
{
|
||||
using std::swap;
|
||||
swap(this->member(), other.member());
|
||||
swap(max_, other.max_);
|
||||
swap(begin_, other.begin_);
|
||||
swap(in_, other.in_);
|
||||
swap(out_, other.out_);
|
||||
last_ = this->out_;
|
||||
other.last_ = other.out_;
|
||||
swap(end_, other.end_);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_flat_buffer<Allocator>::
|
||||
swap(basic_flat_buffer& other, std::false_type)
|
||||
{
|
||||
BOOST_ASSERT(this->member() == other.member());
|
||||
using std::swap;
|
||||
swap(max_, other.max_);
|
||||
swap(begin_, other.begin_);
|
||||
swap(in_, other.in_);
|
||||
swap(out_, other.out_);
|
||||
last_ = this->out_;
|
||||
other.last_ = other.out_;
|
||||
swap(end_, other.end_);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
swap(
|
||||
basic_flat_buffer<Allocator>& lhs,
|
||||
basic_flat_buffer<Allocator>& rhs)
|
||||
{
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -8,8 +8,9 @@
|
||||
#ifndef BEAST_IMPL_HANDLER_PTR_HPP
|
||||
#define BEAST_IMPL_HANDLER_PTR_HPP
|
||||
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <boost/asio/detail/handler_alloc_helpers.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <memory>
|
||||
|
||||
@@ -23,9 +24,10 @@ P(DeducedHandler&& h, Args&&... args)
|
||||
: n(1)
|
||||
, handler(std::forward<DeducedHandler>(h))
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
t = reinterpret_cast<T*>(
|
||||
beast_asio_helpers::
|
||||
allocate(sizeof(T), handler));
|
||||
asio_handler_allocate(
|
||||
sizeof(T), std::addressof(handler)));
|
||||
try
|
||||
{
|
||||
t = new(t) T{handler,
|
||||
@@ -33,8 +35,9 @@ P(DeducedHandler&& h, Args&&... args)
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
beast_asio_helpers::
|
||||
deallocate(t, sizeof(T), handler);
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
t, sizeof(T), std::addressof(handler));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -50,8 +53,9 @@ handler_ptr<T, Handler>::
|
||||
if(p_->t)
|
||||
{
|
||||
p_->t->~T();
|
||||
beast_asio_helpers::
|
||||
deallocate(p_->t, sizeof(T), p_->handler);
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p_->t, sizeof(T), std::addressof(p_->handler));
|
||||
}
|
||||
delete p_;
|
||||
}
|
||||
@@ -80,8 +84,7 @@ handler_ptr(Handler&& handler, Args&&... args)
|
||||
: p_(new P{std::move(handler),
|
||||
std::forward<Args>(args)...})
|
||||
{
|
||||
static_assert(! std::is_array<T>::value,
|
||||
"T must not be an array type");
|
||||
BOOST_STATIC_ASSERT(! std::is_array<T>::value);
|
||||
}
|
||||
|
||||
template<class T, class Handler>
|
||||
@@ -90,8 +93,7 @@ handler_ptr<T, Handler>::
|
||||
handler_ptr(Handler const& handler, Args&&... args)
|
||||
: p_(new P{handler, std::forward<Args>(args)...})
|
||||
{
|
||||
static_assert(! std::is_array<T>::value,
|
||||
"T must not be an array type");
|
||||
BOOST_STATIC_ASSERT(! std::is_array<T>::value);
|
||||
}
|
||||
|
||||
template<class T, class Handler>
|
||||
@@ -103,8 +105,9 @@ release_handler() ->
|
||||
BOOST_ASSERT(p_);
|
||||
BOOST_ASSERT(p_->t);
|
||||
p_->t->~T();
|
||||
beast_asio_helpers::
|
||||
deallocate(p_->t, sizeof(T), p_->handler);
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p_->t, sizeof(T), std::addressof(p_->handler));
|
||||
p_->t = nullptr;
|
||||
return std::move(p_->handler);
|
||||
}
|
||||
@@ -118,8 +121,9 @@ invoke(Args&&... args)
|
||||
BOOST_ASSERT(p_);
|
||||
BOOST_ASSERT(p_->t);
|
||||
p_->t->~T();
|
||||
beast_asio_helpers::
|
||||
deallocate(p_->t, sizeof(T), p_->handler);
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p_->t, sizeof(T), std::addressof(p_->handler));
|
||||
p_->t = nullptr;
|
||||
p_->handler(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_IMPL_STREAMBUF_IPP
|
||||
#define BEAST_IMPL_STREAMBUF_IPP
|
||||
#ifndef BEAST_IMPL_MULTI_BUFFER_IPP
|
||||
#define BEAST_IMPL_MULTI_BUFFER_IPP
|
||||
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <beast/core/detail/write_dynabuf.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <sstream>
|
||||
@@ -84,7 +84,7 @@ namespace beast {
|
||||
*/
|
||||
|
||||
template<class Allocator>
|
||||
class basic_streambuf<Allocator>::element
|
||||
class basic_multi_buffer<Allocator>::element
|
||||
: public boost::intrusive::list_base_hook<
|
||||
boost::intrusive::link_mode<
|
||||
boost::intrusive::normal_link>>
|
||||
@@ -118,14 +118,14 @@ public:
|
||||
};
|
||||
|
||||
template<class Allocator>
|
||||
class basic_streambuf<Allocator>::const_buffers_type
|
||||
class basic_multi_buffer<Allocator>::const_buffers_type
|
||||
{
|
||||
basic_streambuf const* sb_;
|
||||
basic_multi_buffer const* b_;
|
||||
|
||||
friend class basic_streambuf;
|
||||
friend class basic_multi_buffer;
|
||||
|
||||
explicit
|
||||
const_buffers_type(basic_streambuf const& sb);
|
||||
const_buffers_type(basic_multi_buffer const& b);
|
||||
|
||||
public:
|
||||
// Why?
|
||||
@@ -142,17 +142,24 @@ public:
|
||||
|
||||
const_iterator
|
||||
end() const;
|
||||
|
||||
friend
|
||||
std::size_t
|
||||
buffer_size(const_buffers_type const& buffers)
|
||||
{
|
||||
return buffers.b_->size();
|
||||
}
|
||||
};
|
||||
|
||||
template<class Allocator>
|
||||
class basic_streambuf<Allocator>::mutable_buffers_type
|
||||
class basic_multi_buffer<Allocator>::mutable_buffers_type
|
||||
{
|
||||
basic_streambuf const* sb_;
|
||||
basic_multi_buffer const* b_;
|
||||
|
||||
friend class basic_streambuf;
|
||||
friend class basic_multi_buffer;
|
||||
|
||||
explicit
|
||||
mutable_buffers_type(basic_streambuf const& sb);
|
||||
mutable_buffers_type(basic_multi_buffer const& b);
|
||||
|
||||
public:
|
||||
using value_type = mutable_buffer;
|
||||
@@ -173,9 +180,9 @@ public:
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
class basic_streambuf<Allocator>::const_buffers_type::const_iterator
|
||||
class basic_multi_buffer<Allocator>::const_buffers_type::const_iterator
|
||||
{
|
||||
basic_streambuf const* sb_ = nullptr;
|
||||
basic_multi_buffer const* b_ = nullptr;
|
||||
typename list_type::const_iterator it_;
|
||||
|
||||
public:
|
||||
@@ -193,9 +200,9 @@ public:
|
||||
const_iterator& operator=(const_iterator&& other) = default;
|
||||
const_iterator& operator=(const_iterator const& other) = default;
|
||||
|
||||
const_iterator(basic_streambuf const& sb,
|
||||
const_iterator(basic_multi_buffer const& b,
|
||||
typename list_type::const_iterator const& it)
|
||||
: sb_(&sb)
|
||||
: b_(&b)
|
||||
, it_(it)
|
||||
{
|
||||
}
|
||||
@@ -203,7 +210,7 @@ public:
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return sb_ == other.sb_ && it_ == other.it_;
|
||||
return b_ == other.b_ && it_ == other.it_;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -217,9 +224,9 @@ public:
|
||||
{
|
||||
auto const& e = *it_;
|
||||
return value_type{e.data(),
|
||||
(sb_->out_ == sb_->list_.end() ||
|
||||
&e != &*sb_->out_) ? e.size() : sb_->out_pos_} +
|
||||
(&e == &*sb_->list_.begin() ? sb_->in_pos_ : 0);
|
||||
(b_->out_ == b_->list_.end() ||
|
||||
&e != &*b_->out_) ? e.size() : b_->out_pos_} +
|
||||
(&e == &*b_->list_.begin() ? b_->in_pos_ : 0);
|
||||
}
|
||||
|
||||
pointer
|
||||
@@ -257,36 +264,42 @@ public:
|
||||
};
|
||||
|
||||
template<class Allocator>
|
||||
basic_streambuf<Allocator>::const_buffers_type::const_buffers_type(
|
||||
basic_streambuf const& sb)
|
||||
: sb_(&sb)
|
||||
basic_multi_buffer<Allocator>::
|
||||
const_buffers_type::
|
||||
const_buffers_type(
|
||||
basic_multi_buffer const& b)
|
||||
: b_(&b)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::const_buffers_type::begin() const ->
|
||||
basic_multi_buffer<Allocator>::
|
||||
const_buffers_type::
|
||||
begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*sb_, sb_->list_.begin()};
|
||||
return const_iterator{*b_, b_->list_.begin()};
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::const_buffers_type::end() const ->
|
||||
basic_multi_buffer<Allocator>::
|
||||
const_buffers_type::
|
||||
end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*sb_, sb_->out_ ==
|
||||
sb_->list_.end() ? sb_->list_.end() :
|
||||
std::next(sb_->out_)};
|
||||
return const_iterator{*b_, b_->out_ ==
|
||||
b_->list_.end() ? b_->list_.end() :
|
||||
std::next(b_->out_)};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
class basic_streambuf<Allocator>::mutable_buffers_type::const_iterator
|
||||
class basic_multi_buffer<Allocator>::mutable_buffers_type::const_iterator
|
||||
{
|
||||
basic_streambuf const* sb_ = nullptr;
|
||||
basic_multi_buffer const* b_ = nullptr;
|
||||
typename list_type::const_iterator it_;
|
||||
|
||||
public:
|
||||
@@ -304,9 +317,9 @@ public:
|
||||
const_iterator& operator=(const_iterator&& other) = default;
|
||||
const_iterator& operator=(const_iterator const& other) = default;
|
||||
|
||||
const_iterator(basic_streambuf const& sb,
|
||||
const_iterator(basic_multi_buffer const& b,
|
||||
typename list_type::const_iterator const& it)
|
||||
: sb_(&sb)
|
||||
: b_(&b)
|
||||
, it_(it)
|
||||
{
|
||||
}
|
||||
@@ -314,7 +327,7 @@ public:
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return sb_ == other.sb_ && it_ == other.it_;
|
||||
return b_ == other.b_ && it_ == other.it_;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -328,9 +341,9 @@ public:
|
||||
{
|
||||
auto const& e = *it_;
|
||||
return value_type{e.data(),
|
||||
&e == &*std::prev(sb_->list_.end()) ?
|
||||
sb_->out_end_ : e.size()} +
|
||||
(&e == &*sb_->out_ ? sb_->out_pos_ : 0);
|
||||
&e == &*std::prev(b_->list_.end()) ?
|
||||
b_->out_end_ : e.size()} +
|
||||
(&e == &*b_->out_ ? b_->out_pos_ : 0);
|
||||
}
|
||||
|
||||
pointer
|
||||
@@ -368,42 +381,84 @@ public:
|
||||
};
|
||||
|
||||
template<class Allocator>
|
||||
basic_streambuf<Allocator>::mutable_buffers_type::mutable_buffers_type(
|
||||
basic_streambuf const& sb)
|
||||
: sb_(&sb)
|
||||
basic_multi_buffer<Allocator>::
|
||||
mutable_buffers_type::
|
||||
mutable_buffers_type(
|
||||
basic_multi_buffer const& b)
|
||||
: b_(&b)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::mutable_buffers_type::begin() const ->
|
||||
basic_multi_buffer<Allocator>::
|
||||
mutable_buffers_type::
|
||||
begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*sb_, sb_->out_};
|
||||
return const_iterator{*b_, b_->out_};
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::mutable_buffers_type::end() const ->
|
||||
basic_multi_buffer<Allocator>::
|
||||
mutable_buffers_type::
|
||||
end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*sb_, sb_->list_.end()};
|
||||
return const_iterator{*b_, b_->list_.end()};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
basic_streambuf<Allocator>::~basic_streambuf()
|
||||
basic_multi_buffer<Allocator>::
|
||||
~basic_multi_buffer()
|
||||
{
|
||||
delete_list();
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_streambuf<Allocator>::
|
||||
basic_streambuf(basic_streambuf&& other)
|
||||
basic_multi_buffer<Allocator>::
|
||||
basic_multi_buffer()
|
||||
: out_(list_.end())
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_multi_buffer<Allocator>::
|
||||
basic_multi_buffer(std::size_t limit)
|
||||
: max_(limit)
|
||||
, out_(list_.end())
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_multi_buffer<Allocator>::
|
||||
basic_multi_buffer(Allocator const& alloc)
|
||||
: detail::empty_base_optimization<
|
||||
allocator_type>(alloc)
|
||||
, out_(list_.end())
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_multi_buffer<Allocator>::
|
||||
basic_multi_buffer(std::size_t limit,
|
||||
Allocator const& alloc)
|
||||
: detail::empty_base_optimization<
|
||||
allocator_type>(alloc)
|
||||
, max_(limit)
|
||||
, out_(list_.end())
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_multi_buffer<Allocator>::
|
||||
basic_multi_buffer(basic_multi_buffer&& other)
|
||||
: detail::empty_base_optimization<allocator_type>(
|
||||
std::move(other.member()))
|
||||
, alloc_size_(other.alloc_size_)
|
||||
, max_(other.max_)
|
||||
, in_size_(other.in_size_)
|
||||
, in_pos_(other.in_pos_)
|
||||
, out_pos_(other.out_pos_)
|
||||
@@ -421,116 +476,127 @@ basic_streambuf(basic_streambuf&& other)
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_streambuf<Allocator>::
|
||||
basic_streambuf(basic_streambuf&& other,
|
||||
allocator_type const& alloc)
|
||||
: basic_streambuf(other.alloc_size_, alloc)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
if(this->member() != other.member())
|
||||
commit(buffer_copy(prepare(other.size()), other.data()));
|
||||
else
|
||||
move_assign(other, std::true_type{});
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::operator=(
|
||||
basic_streambuf&& other) -> basic_streambuf&
|
||||
{
|
||||
if(this == &other)
|
||||
return *this;
|
||||
// VFALCO If any memory allocated we could use it first?
|
||||
clear();
|
||||
alloc_size_ = other.alloc_size_;
|
||||
move_assign(other, std::integral_constant<bool,
|
||||
alloc_traits::propagate_on_container_move_assignment::value>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_streambuf<Allocator>::
|
||||
basic_streambuf(basic_streambuf const& other)
|
||||
: basic_streambuf(other.alloc_size_,
|
||||
alloc_traits::select_on_container_copy_construction(other.member()))
|
||||
{
|
||||
commit(boost::asio::buffer_copy(prepare(other.size()), other.data()));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_streambuf<Allocator>::
|
||||
basic_streambuf(basic_streambuf const& other,
|
||||
allocator_type const& alloc)
|
||||
: basic_streambuf(other.alloc_size_, alloc)
|
||||
{
|
||||
commit(boost::asio::buffer_copy(prepare(other.size()), other.data()));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::operator=(
|
||||
basic_streambuf const& other) ->
|
||||
basic_streambuf&
|
||||
{
|
||||
if(this == &other)
|
||||
return *this;
|
||||
using boost::asio::buffer_copy;
|
||||
clear();
|
||||
copy_assign(other, std::integral_constant<bool,
|
||||
alloc_traits::propagate_on_container_copy_assignment::value>{});
|
||||
commit(buffer_copy(prepare(other.size()), other.data()));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
basic_streambuf<Allocator>::basic_streambuf(
|
||||
basic_streambuf<OtherAlloc> const& other)
|
||||
: basic_streambuf(other.alloc_size_)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
commit(buffer_copy(prepare(other.size()), other.data()));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
basic_streambuf<Allocator>::basic_streambuf(
|
||||
basic_streambuf<OtherAlloc> const& other,
|
||||
allocator_type const& alloc)
|
||||
: basic_streambuf(other.alloc_size_, alloc)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
commit(buffer_copy(prepare(other.size()), other.data()));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
auto
|
||||
basic_streambuf<Allocator>::operator=(
|
||||
basic_streambuf<OtherAlloc> const& other) ->
|
||||
basic_streambuf&
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
clear();
|
||||
commit(buffer_copy(prepare(other.size()), other.data()));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_streambuf<Allocator>::basic_streambuf(
|
||||
std::size_t alloc_size, Allocator const& alloc)
|
||||
basic_multi_buffer<Allocator>::
|
||||
basic_multi_buffer(basic_multi_buffer&& other,
|
||||
Allocator const& alloc)
|
||||
: detail::empty_base_optimization<allocator_type>(alloc)
|
||||
, out_(list_.end())
|
||||
, alloc_size_(alloc_size)
|
||||
, max_(other.max_)
|
||||
{
|
||||
if(alloc_size <= 0)
|
||||
throw detail::make_exception<std::invalid_argument>(
|
||||
"invalid alloc_size", __FILE__, __LINE__);
|
||||
if(this->member() != other.member())
|
||||
{
|
||||
out_ = list_.end();
|
||||
copy_from(other);
|
||||
other.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const at_end =
|
||||
other.out_ == other.list_.end();
|
||||
list_ = std::move(other.list_);
|
||||
out_ = at_end ? list_.end() : other.out_;
|
||||
in_size_ = other.in_size_;
|
||||
in_pos_ = other.in_pos_;
|
||||
out_pos_ = other.out_pos_;
|
||||
out_end_ = other.out_end_;
|
||||
other.in_size_ = 0;
|
||||
other.out_ = other.list_.end();
|
||||
other.in_pos_ = 0;
|
||||
other.out_pos_ = 0;
|
||||
other.out_end_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_multi_buffer<Allocator>::
|
||||
basic_multi_buffer(basic_multi_buffer const& other)
|
||||
: detail::empty_base_optimization<allocator_type>(
|
||||
alloc_traits::select_on_container_copy_construction(other.member()))
|
||||
, max_(other.max_)
|
||||
, out_(list_.end())
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_multi_buffer<Allocator>::
|
||||
basic_multi_buffer(basic_multi_buffer const& other,
|
||||
Allocator const& alloc)
|
||||
: detail::empty_base_optimization<allocator_type>(alloc)
|
||||
, max_(other.max_)
|
||||
, out_(list_.end())
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
basic_multi_buffer<Allocator>::
|
||||
basic_multi_buffer(
|
||||
basic_multi_buffer<OtherAlloc> const& other)
|
||||
: out_(list_.end())
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
basic_multi_buffer<Allocator>::
|
||||
basic_multi_buffer(
|
||||
basic_multi_buffer<OtherAlloc> const& other,
|
||||
allocator_type const& alloc)
|
||||
: detail::empty_base_optimization<allocator_type>(alloc)
|
||||
, max_(other.max_)
|
||||
, out_(list_.end())
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_multi_buffer<Allocator>::
|
||||
operator=(basic_multi_buffer&& other) ->
|
||||
basic_multi_buffer&
|
||||
{
|
||||
if(this == &other)
|
||||
return *this;
|
||||
reset();
|
||||
max_ = other.max_;
|
||||
move_assign(other, typename
|
||||
alloc_traits::propagate_on_container_move_assignment{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_multi_buffer<Allocator>::
|
||||
operator=(basic_multi_buffer const& other) ->
|
||||
basic_multi_buffer&
|
||||
{
|
||||
if(this == &other)
|
||||
return *this;
|
||||
copy_assign(other, typename
|
||||
alloc_traits::propagate_on_container_copy_assignment{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
auto
|
||||
basic_multi_buffer<Allocator>::
|
||||
operator=(
|
||||
basic_multi_buffer<OtherAlloc> const& other) ->
|
||||
basic_multi_buffer&
|
||||
{
|
||||
reset();
|
||||
max_ = other.max_;
|
||||
copy_from(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
std::size_t
|
||||
basic_streambuf<Allocator>::capacity() const
|
||||
basic_multi_buffer<Allocator>::
|
||||
capacity() const
|
||||
{
|
||||
auto pos = out_;
|
||||
if(pos == list_.end())
|
||||
@@ -543,7 +609,7 @@ basic_streambuf<Allocator>::capacity() const
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::
|
||||
basic_multi_buffer<Allocator>::
|
||||
data() const ->
|
||||
const_buffers_type
|
||||
{
|
||||
@@ -552,18 +618,27 @@ data() const ->
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_streambuf<Allocator>::prepare(size_type n) ->
|
||||
basic_multi_buffer<Allocator>::
|
||||
prepare(size_type n) ->
|
||||
mutable_buffers_type
|
||||
{
|
||||
if(in_size_ + n > max_)
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"dynamic buffer overflow"});
|
||||
list_type reuse;
|
||||
std::size_t total = in_size_;
|
||||
// put all empty buffers on reuse list
|
||||
if(out_ != list_.end())
|
||||
{
|
||||
total += out_->size() - out_pos_;
|
||||
if(out_ != list_.iterator_to(list_.back()))
|
||||
{
|
||||
out_end_ = out_->size();
|
||||
reuse.splice(reuse.end(), list_,
|
||||
std::next(out_), list_.end());
|
||||
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
|
||||
debug_check();
|
||||
#endif
|
||||
}
|
||||
auto const avail = out_->size() - out_pos_;
|
||||
if(n > avail)
|
||||
@@ -576,13 +651,17 @@ basic_streambuf<Allocator>::prepare(size_type n) ->
|
||||
out_end_ = out_pos_ + n;
|
||||
n = 0;
|
||||
}
|
||||
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
|
||||
debug_check();
|
||||
#endif
|
||||
}
|
||||
// get space from reuse buffers
|
||||
while(n > 0 && ! reuse.empty())
|
||||
{
|
||||
auto& e = reuse.front();
|
||||
reuse.erase(reuse.iterator_to(e));
|
||||
list_.push_back(e);
|
||||
total += e.size();
|
||||
if(n > e.size())
|
||||
{
|
||||
out_end_ = e.size();
|
||||
@@ -593,11 +672,28 @@ basic_streambuf<Allocator>::prepare(size_type n) ->
|
||||
out_end_ = n;
|
||||
n = 0;
|
||||
}
|
||||
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
|
||||
debug_check();
|
||||
#endif
|
||||
}
|
||||
while(n > 0)
|
||||
BOOST_ASSERT(total <= max_);
|
||||
for(auto it = reuse.begin(); it != reuse.end();)
|
||||
{
|
||||
auto const size = std::max(alloc_size_, n);
|
||||
auto& e = *it++;
|
||||
reuse.erase(list_.iterator_to(e));
|
||||
delete_element(e);
|
||||
}
|
||||
if(n > 0)
|
||||
{
|
||||
static auto const growth_factor = 2.0f;
|
||||
auto const size =
|
||||
std::min<std::size_t>(
|
||||
max_ - total,
|
||||
std::max<std::size_t>({
|
||||
static_cast<std::size_t>(
|
||||
in_size_ * growth_factor - in_size_),
|
||||
512,
|
||||
n}));
|
||||
auto& e = *reinterpret_cast<element*>(static_cast<
|
||||
void*>(alloc_traits::allocate(this->member(),
|
||||
sizeof(element) + size)));
|
||||
@@ -605,33 +701,18 @@ basic_streambuf<Allocator>::prepare(size_type n) ->
|
||||
list_.push_back(e);
|
||||
if(out_ == list_.end())
|
||||
out_ = list_.iterator_to(e);
|
||||
if(n >= e.size())
|
||||
{
|
||||
out_end_ = e.size();
|
||||
n -= e.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
out_end_ = n;
|
||||
n = 0;
|
||||
}
|
||||
out_end_ = n;
|
||||
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
|
||||
debug_check();
|
||||
}
|
||||
for(auto it = reuse.begin(); it != reuse.end();)
|
||||
{
|
||||
auto& e = *it++;
|
||||
reuse.erase(list_.iterator_to(e));
|
||||
auto const len = e.size() + sizeof(e);
|
||||
alloc_traits::destroy(this->member(), &e);
|
||||
alloc_traits::deallocate(this->member(),
|
||||
reinterpret_cast<char*>(&e), len);
|
||||
#endif
|
||||
}
|
||||
return mutable_buffers_type(*this);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_streambuf<Allocator>::commit(size_type n)
|
||||
basic_multi_buffer<Allocator>::
|
||||
commit(size_type n)
|
||||
{
|
||||
if(list_.empty())
|
||||
return;
|
||||
@@ -647,14 +728,18 @@ basic_streambuf<Allocator>::commit(size_type n)
|
||||
{
|
||||
out_pos_ += n;
|
||||
in_size_ += n;
|
||||
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
|
||||
debug_check();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
++out_;
|
||||
n -= avail;
|
||||
out_pos_ = 0;
|
||||
in_size_ += avail;
|
||||
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
|
||||
debug_check();
|
||||
#endif
|
||||
}
|
||||
|
||||
n = (std::min)(n, out_end_ - out_pos_);
|
||||
@@ -666,26 +751,31 @@ basic_streambuf<Allocator>::commit(size_type n)
|
||||
out_pos_ = 0;
|
||||
out_end_ = 0;
|
||||
}
|
||||
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
|
||||
debug_check();
|
||||
#endif
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_streambuf<Allocator>::consume(size_type n)
|
||||
basic_multi_buffer<Allocator>::
|
||||
consume(size_type n)
|
||||
{
|
||||
if(list_.empty())
|
||||
return;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
if(list_.begin() != out_)
|
||||
{
|
||||
auto const avail = list_.front().size() - in_pos_;
|
||||
auto const avail =
|
||||
list_.front().size() - in_pos_;
|
||||
if(n < avail)
|
||||
{
|
||||
in_size_ -= n;
|
||||
in_pos_ += n;
|
||||
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
|
||||
debug_check();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
n -= avail;
|
||||
@@ -693,11 +783,10 @@ basic_streambuf<Allocator>::consume(size_type n)
|
||||
in_pos_ = 0;
|
||||
auto& e = list_.front();
|
||||
list_.erase(list_.iterator_to(e));
|
||||
auto const len = e.size() + sizeof(e);
|
||||
alloc_traits::destroy(this->member(), &e);
|
||||
alloc_traits::deallocate(this->member(),
|
||||
reinterpret_cast<char*>(&e), len);
|
||||
delete_element(e);
|
||||
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
|
||||
debug_check();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -724,20 +813,45 @@ basic_streambuf<Allocator>::consume(size_type n)
|
||||
out_end_ = 0;
|
||||
}
|
||||
}
|
||||
#if BEAST_MULTI_BUFFER_DEBUG_CHECK
|
||||
debug_check();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_streambuf<Allocator>::
|
||||
clear()
|
||||
basic_multi_buffer<Allocator>::
|
||||
delete_element(element& e)
|
||||
{
|
||||
auto const len = sizeof(e) + e.size();
|
||||
alloc_traits::destroy(this->member(), &e);
|
||||
alloc_traits::deallocate(this->member(),
|
||||
reinterpret_cast<char*>(&e), len);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_multi_buffer<Allocator>::
|
||||
delete_list()
|
||||
{
|
||||
for(auto iter = list_.begin(); iter != list_.end();)
|
||||
delete_element(*iter++);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_multi_buffer<Allocator>::
|
||||
reset()
|
||||
{
|
||||
delete_list();
|
||||
list_.clear();
|
||||
out_ = list_.begin();
|
||||
out_ = list_.end();
|
||||
in_size_ = 0;
|
||||
in_pos_ = 0;
|
||||
out_pos_ = 0;
|
||||
@@ -745,24 +859,41 @@ clear()
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class DynamicBuffer>
|
||||
inline
|
||||
void
|
||||
basic_streambuf<Allocator>::
|
||||
move_assign(basic_streambuf& other, std::false_type)
|
||||
basic_multi_buffer<Allocator>::
|
||||
copy_from(DynamicBuffer const& buffer)
|
||||
{
|
||||
if(buffer.size() == 0)
|
||||
return;
|
||||
using boost::asio::buffer_copy;
|
||||
if(this->member() != other.member())
|
||||
{
|
||||
commit(buffer_copy(prepare(other.size()), other.data()));
|
||||
other.clear();
|
||||
}
|
||||
else
|
||||
move_assign(other, std::true_type{});
|
||||
commit(buffer_copy(
|
||||
prepare(buffer.size()), buffer.data()));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_streambuf<Allocator>::
|
||||
move_assign(basic_streambuf& other, std::true_type)
|
||||
basic_multi_buffer<Allocator>::
|
||||
move_assign(basic_multi_buffer& other, std::false_type)
|
||||
{
|
||||
if(this->member() != other.member())
|
||||
{
|
||||
copy_from(other);
|
||||
other.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
move_assign(other, std::true_type{});
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_multi_buffer<Allocator>::
|
||||
move_assign(basic_multi_buffer& other, std::true_type)
|
||||
{
|
||||
this->member() = std::move(other.member());
|
||||
auto const at_end =
|
||||
@@ -783,38 +914,101 @@ move_assign(basic_streambuf& other, std::true_type)
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_streambuf<Allocator>::
|
||||
copy_assign(basic_streambuf const& other, std::false_type)
|
||||
basic_multi_buffer<Allocator>::
|
||||
copy_assign(
|
||||
basic_multi_buffer const& other, std::false_type)
|
||||
{
|
||||
beast::detail::ignore_unused(other);
|
||||
reset();
|
||||
max_ = other.max_;
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_streambuf<Allocator>::
|
||||
copy_assign(basic_streambuf const& other, std::true_type)
|
||||
basic_multi_buffer<Allocator>::
|
||||
copy_assign(
|
||||
basic_multi_buffer const& other, std::true_type)
|
||||
{
|
||||
reset();
|
||||
max_ = other.max_;
|
||||
this->member() = other.member();
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_streambuf<Allocator>::delete_list()
|
||||
basic_multi_buffer<Allocator>::
|
||||
swap(basic_multi_buffer& other)
|
||||
{
|
||||
for(auto iter = list_.begin(); iter != list_.end();)
|
||||
{
|
||||
auto& e = *iter++;
|
||||
auto const n = e.size() + sizeof(e);
|
||||
alloc_traits::destroy(this->member(), &e);
|
||||
alloc_traits::deallocate(this->member(),
|
||||
reinterpret_cast<char*>(&e), n);
|
||||
}
|
||||
swap(other, typename
|
||||
alloc_traits::propagate_on_container_swap{});
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_multi_buffer<Allocator>::
|
||||
swap(basic_multi_buffer& other, std::true_type)
|
||||
{
|
||||
using std::swap;
|
||||
auto const at_end0 =
|
||||
out_ == list_.end();
|
||||
auto const at_end1 =
|
||||
other.out_ == other.list_.end();
|
||||
swap(this->member(), other.member());
|
||||
swap(list_, other.list_);
|
||||
swap(out_, other.out_);
|
||||
if(at_end1)
|
||||
out_ = list_.end();
|
||||
if(at_end0)
|
||||
other.out_ = other.list_.end();
|
||||
swap(in_size_, other.in_size_);
|
||||
swap(in_pos_, other.in_pos_);
|
||||
swap(out_pos_, other.out_pos_);
|
||||
swap(out_end_, other.out_end_);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_multi_buffer<Allocator>::
|
||||
swap(basic_multi_buffer& other, std::false_type)
|
||||
{
|
||||
BOOST_ASSERT(this->member() == other.member());
|
||||
using std::swap;
|
||||
auto const at_end0 =
|
||||
out_ == list_.end();
|
||||
auto const at_end1 =
|
||||
other.out_ == other.list_.end();
|
||||
swap(list_, other.list_);
|
||||
swap(out_, other.out_);
|
||||
if(at_end1)
|
||||
out_ = list_.end();
|
||||
if(at_end0)
|
||||
other.out_ = other.list_.end();
|
||||
swap(in_size_, other.in_size_);
|
||||
swap(in_pos_, other.in_pos_);
|
||||
swap(out_pos_, other.out_pos_);
|
||||
swap(out_end_, other.out_end_);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_streambuf<Allocator>::debug_check() const
|
||||
swap(
|
||||
basic_multi_buffer<Allocator>& lhs,
|
||||
basic_multi_buffer<Allocator>& rhs)
|
||||
{
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_multi_buffer<Allocator>::
|
||||
debug_check() const
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
using boost::asio::buffer_size;
|
||||
@@ -852,33 +1046,6 @@ basic_streambuf<Allocator>::debug_check() const
|
||||
#endif
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
std::size_t
|
||||
read_size_helper(basic_streambuf<
|
||||
Allocator> const& streambuf, std::size_t max_size)
|
||||
{
|
||||
BOOST_ASSERT(max_size >= 1);
|
||||
// If we already have an allocated
|
||||
// buffer, try to fill that up first
|
||||
auto const avail = streambuf.capacity() - streambuf.size();
|
||||
if (avail > 0)
|
||||
return (std::min)(avail, max_size);
|
||||
// Try to have just one new block allocated
|
||||
constexpr std::size_t low = 512;
|
||||
if (streambuf.alloc_size_ > low)
|
||||
return (std::min)(max_size, streambuf.alloc_size_);
|
||||
// ...but enforce a 512 byte minimum.
|
||||
return (std::min)(max_size, low);
|
||||
}
|
||||
|
||||
template<class Alloc, class T>
|
||||
basic_streambuf<Alloc>&
|
||||
operator<<(basic_streambuf<Alloc>& streambuf, T const& t)
|
||||
{
|
||||
detail::write_dynabuf(streambuf, t);
|
||||
return streambuf;
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
75
include/beast/core/impl/read_size.ipp
Normal file
75
include/beast/core/impl/read_size.ipp
Normal file
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_IMPL_READ_SIZE_IPP
|
||||
#define BEAST_IMPL_READ_SIZE_IPP
|
||||
|
||||
namespace beast {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class T, class = void>
|
||||
struct has_read_size_helper : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct has_read_size_helper<T, decltype(
|
||||
read_size_helper(std::declval<T&>(), 512),
|
||||
(void)0)> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template<class DynamicBuffer>
|
||||
std::size_t
|
||||
read_size(DynamicBuffer& buffer,
|
||||
std::size_t max_size, std::true_type)
|
||||
{
|
||||
return read_size_helper(buffer, max_size);
|
||||
}
|
||||
|
||||
template<class DynamicBuffer>
|
||||
std::size_t
|
||||
read_size(DynamicBuffer& buffer,
|
||||
std::size_t max_size, std::false_type)
|
||||
{
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(max_size >= 1);
|
||||
auto const size = buffer.size();
|
||||
auto const limit = buffer.max_size() - size;
|
||||
BOOST_ASSERT(size <= buffer.max_size());
|
||||
return std::min<std::size_t>(
|
||||
std::max<std::size_t>(512, buffer.capacity() - size),
|
||||
std::min<std::size_t>(max_size, limit));
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
template<class DynamicBuffer>
|
||||
inline
|
||||
std::size_t
|
||||
read_size(
|
||||
DynamicBuffer& buffer, std::size_t max_size)
|
||||
{
|
||||
return detail::read_size(buffer, max_size,
|
||||
detail::has_read_size_helper<DynamicBuffer>{});
|
||||
}
|
||||
|
||||
template<class DynamicBuffer>
|
||||
std::size_t
|
||||
read_size_or_throw(
|
||||
DynamicBuffer& buffer, std::size_t max_size)
|
||||
{
|
||||
auto const n = read_size(buffer, max_size);
|
||||
if(n == 0)
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"buffer overflow"});
|
||||
return n;
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
129
include/beast/core/impl/static_buffer.ipp
Normal file
129
include/beast/core/impl/static_buffer.ipp
Normal file
@@ -0,0 +1,129 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_IMPL_STATIC_BUFFER_IPP
|
||||
#define BEAST_IMPL_STATIC_BUFFER_IPP
|
||||
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/* Memory is laid out thusly:
|
||||
|
||||
begin_ ..|.. in_ ..|.. out_ ..|.. last_ ..|.. end_
|
||||
*/
|
||||
|
||||
inline
|
||||
auto
|
||||
static_buffer::
|
||||
data() const ->
|
||||
const_buffers_type
|
||||
{
|
||||
return {in_, dist(in_, out_)};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
static_buffer::
|
||||
prepare(std::size_t n) ->
|
||||
mutable_buffers_type
|
||||
{
|
||||
return prepare_impl(n);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
static_buffer::
|
||||
reset(void* p, std::size_t n)
|
||||
{
|
||||
reset_impl(p, n);
|
||||
}
|
||||
|
||||
template<class>
|
||||
void
|
||||
static_buffer::
|
||||
reset_impl(void* p, std::size_t n)
|
||||
{
|
||||
begin_ =
|
||||
reinterpret_cast<char*>(p);
|
||||
in_ = begin_;
|
||||
out_ = begin_;
|
||||
last_ = begin_;
|
||||
end_ = begin_ + n;
|
||||
}
|
||||
|
||||
template<class>
|
||||
auto
|
||||
static_buffer::
|
||||
prepare_impl(std::size_t n) ->
|
||||
mutable_buffers_type
|
||||
{
|
||||
if(n <= dist(out_, end_))
|
||||
{
|
||||
last_ = out_ + n;
|
||||
return {out_, n};
|
||||
}
|
||||
auto const len = size();
|
||||
if(n > capacity() - len)
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"buffer overflow"});
|
||||
if(len > 0)
|
||||
std::memmove(begin_, in_, len);
|
||||
in_ = begin_;
|
||||
out_ = in_ + len;
|
||||
last_ = out_ + n;
|
||||
return {out_, n};
|
||||
}
|
||||
|
||||
template<class>
|
||||
void
|
||||
static_buffer::
|
||||
consume_impl(std::size_t n)
|
||||
{
|
||||
if(n >= size())
|
||||
{
|
||||
in_ = begin_;
|
||||
out_ = in_;
|
||||
return;
|
||||
}
|
||||
in_ += n;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<std::size_t N>
|
||||
static_buffer_n<N>::
|
||||
static_buffer_n(static_buffer_n const& other)
|
||||
: static_buffer(buf_, N)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
this->commit(buffer_copy(
|
||||
this->prepare(other.size()), other.data()));
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
auto
|
||||
static_buffer_n<N>::
|
||||
operator=(static_buffer_n const& other) ->
|
||||
static_buffer_n<N>&
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
this->consume(this->size());
|
||||
this->commit(buffer_copy(
|
||||
this->prepare(other.size()), other.data()));
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -1,307 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_IMPL_STATIC_STREAMBUF_IPP
|
||||
#define BEAST_IMPL_STATIC_STREAMBUF_IPP
|
||||
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace beast {
|
||||
|
||||
class static_streambuf::const_buffers_type
|
||||
{
|
||||
std::size_t n_;
|
||||
std::uint8_t const* p_;
|
||||
|
||||
public:
|
||||
using value_type = boost::asio::const_buffer;
|
||||
|
||||
class const_iterator;
|
||||
|
||||
const_buffers_type() = delete;
|
||||
const_buffers_type(
|
||||
const_buffers_type const&) = default;
|
||||
const_buffers_type& operator=(
|
||||
const_buffers_type const&) = default;
|
||||
|
||||
const_iterator
|
||||
begin() const;
|
||||
|
||||
const_iterator
|
||||
end() const;
|
||||
|
||||
private:
|
||||
friend class static_streambuf;
|
||||
|
||||
const_buffers_type(
|
||||
std::uint8_t const* p, std::size_t n)
|
||||
: n_(n)
|
||||
, p_(p)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class static_streambuf::const_buffers_type::const_iterator
|
||||
{
|
||||
std::size_t n_ = 0;
|
||||
std::uint8_t const* p_ = nullptr;
|
||||
|
||||
public:
|
||||
using value_type = boost::asio::const_buffer;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::bidirectional_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
const_iterator(const_iterator&& other) = default;
|
||||
const_iterator(const_iterator const& other) = default;
|
||||
const_iterator& operator=(const_iterator&& other) = default;
|
||||
const_iterator& operator=(const_iterator const& other) = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return p_ == other.p_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return value_type{p_, n_};
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const = delete;
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
p_ += n_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator--()
|
||||
{
|
||||
p_ -= n_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator--(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class const_buffers_type;
|
||||
|
||||
const_iterator(
|
||||
std::uint8_t const* p, std::size_t n)
|
||||
: n_(n)
|
||||
, p_(p)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
auto
|
||||
static_streambuf::const_buffers_type::begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{p_, n_};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
static_streambuf::const_buffers_type::end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{p_ + n_, n_};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class static_streambuf::mutable_buffers_type
|
||||
{
|
||||
std::size_t n_;
|
||||
std::uint8_t* p_;
|
||||
|
||||
public:
|
||||
using value_type = boost::asio::mutable_buffer;
|
||||
|
||||
class const_iterator;
|
||||
|
||||
mutable_buffers_type() = delete;
|
||||
mutable_buffers_type(
|
||||
mutable_buffers_type const&) = default;
|
||||
mutable_buffers_type& operator=(
|
||||
mutable_buffers_type const&) = default;
|
||||
|
||||
const_iterator
|
||||
begin() const;
|
||||
|
||||
const_iterator
|
||||
end() const;
|
||||
|
||||
private:
|
||||
friend class static_streambuf;
|
||||
|
||||
mutable_buffers_type(
|
||||
std::uint8_t* p, std::size_t n)
|
||||
: n_(n)
|
||||
, p_(p)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class static_streambuf::mutable_buffers_type::const_iterator
|
||||
{
|
||||
std::size_t n_ = 0;
|
||||
std::uint8_t* p_ = nullptr;
|
||||
|
||||
public:
|
||||
using value_type = boost::asio::mutable_buffer;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::bidirectional_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
const_iterator(const_iterator&& other) = default;
|
||||
const_iterator(const_iterator const& other) = default;
|
||||
const_iterator& operator=(const_iterator&& other) = default;
|
||||
const_iterator& operator=(const_iterator const& other) = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return p_ == other.p_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return value_type{p_, n_};
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const = delete;
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
p_ += n_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator--()
|
||||
{
|
||||
p_ -= n_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator--(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class mutable_buffers_type;
|
||||
|
||||
const_iterator(std::uint8_t* p, std::size_t n)
|
||||
: n_(n)
|
||||
, p_(p)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
auto
|
||||
static_streambuf::mutable_buffers_type::begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{p_, n_};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
static_streambuf::mutable_buffers_type::end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{p_ + n_, n_};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
inline
|
||||
auto
|
||||
static_streambuf::data() const ->
|
||||
const_buffers_type
|
||||
{
|
||||
return const_buffers_type{in_,
|
||||
static_cast<std::size_t>(out_ - in_)};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
static_streambuf::prepare(std::size_t n) ->
|
||||
mutable_buffers_type
|
||||
{
|
||||
if(n > static_cast<std::size_t>(end_ - out_))
|
||||
throw detail::make_exception<std::length_error>(
|
||||
"no space in streambuf", __FILE__, __LINE__);
|
||||
last_ = out_ + n;
|
||||
return mutable_buffers_type{out_, n};
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
614
include/beast/core/impl/static_string.ipp
Normal file
614
include/beast/core/impl/static_string.ipp
Normal file
@@ -0,0 +1,614 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_IMPL_STATIC_STRING_IPP
|
||||
#define BEAST_IMPL_STATIC_STRING_IPP
|
||||
|
||||
#include <beast/core/detail/static_string.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
namespace beast {
|
||||
|
||||
//
|
||||
// (constructor)
|
||||
//
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string()
|
||||
{
|
||||
n_ = 0;
|
||||
term();
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(size_type count, CharT ch)
|
||||
{
|
||||
assign(count, ch);
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<std::size_t M>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(static_string<M, CharT, Traits> const& other,
|
||||
size_type pos)
|
||||
{
|
||||
assign(other, pos);
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<std::size_t M>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(static_string<M, CharT, Traits> const& other,
|
||||
size_type pos, size_type count)
|
||||
{
|
||||
assign(other, pos, count);
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(CharT const* s, size_type count)
|
||||
{
|
||||
assign(s, count);
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(CharT const* s)
|
||||
{
|
||||
assign(s);
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<class InputIt>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(InputIt first, InputIt last)
|
||||
{
|
||||
assign(first, last);
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(static_string const& s)
|
||||
{
|
||||
assign(s);
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<std::size_t M>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(static_string<M, CharT, Traits> const& s)
|
||||
{
|
||||
assign(s);
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(std::initializer_list<CharT> init)
|
||||
{
|
||||
assign(init.begin(), init.end());
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(string_view_type sv)
|
||||
{
|
||||
assign(sv);
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<class T, class>
|
||||
static_string<N, CharT, Traits>::
|
||||
static_string(T const& t, size_type pos, size_type n)
|
||||
{
|
||||
assign(t, pos, n);
|
||||
}
|
||||
|
||||
//
|
||||
// (assignment)
|
||||
//
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
assign(size_type count, CharT ch) ->
|
||||
static_string&
|
||||
{
|
||||
if(count > max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"count > max_size()"});
|
||||
n_ = count;
|
||||
Traits::assign(&s_[0], n_, ch);
|
||||
term();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
assign(static_string const& str) ->
|
||||
static_string&
|
||||
{
|
||||
n_ = str.n_;
|
||||
Traits::copy(&s_[0], &str.s_[0], n_ + 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<std::size_t M>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
assign(static_string<M, CharT, Traits> const& str,
|
||||
size_type pos, size_type count) ->
|
||||
static_string&
|
||||
{
|
||||
auto const ss = str.substr(pos, count);
|
||||
return assign(ss.data(), ss.size());
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
assign(CharT const* s, size_type count) ->
|
||||
static_string&
|
||||
{
|
||||
if(count > max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"count > max_size()"});
|
||||
n_ = count;
|
||||
Traits::copy(&s_[0], s, n_);
|
||||
term();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<class InputIt>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
assign(InputIt first, InputIt last) ->
|
||||
static_string&
|
||||
{
|
||||
std::size_t const n = std::distance(first, last);
|
||||
if(n > max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"n > max_size()"});
|
||||
n_ = n;
|
||||
for(auto it = &s_[0]; first != last; ++it, ++first)
|
||||
Traits::assign(*it, *first);
|
||||
term();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<class T>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
assign(T const& t, size_type pos, size_type count) ->
|
||||
typename std::enable_if<std::is_convertible<T,
|
||||
string_view_type>::value, static_string&>::type
|
||||
{
|
||||
auto const sv = string_view_type(t).substr(pos, count);
|
||||
if(sv.size() > max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"sv.size() > max_size()"});
|
||||
n_ = sv.size();
|
||||
Traits::copy(&s_[0], &sv[0], n_);
|
||||
term();
|
||||
return *this;
|
||||
}
|
||||
|
||||
//
|
||||
// Element access
|
||||
//
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
at(size_type pos) ->
|
||||
reference
|
||||
{
|
||||
if(pos >= size())
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range{
|
||||
"pos >= size()"});
|
||||
return s_[pos];
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
at(size_type pos) const ->
|
||||
const_reference
|
||||
{
|
||||
if(pos >= size())
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range{
|
||||
"pos >= size()"});
|
||||
return s_[pos];
|
||||
}
|
||||
|
||||
//
|
||||
// Capacity
|
||||
//
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
void
|
||||
static_string<N, CharT, Traits>::
|
||||
reserve(std::size_t n)
|
||||
{
|
||||
if(n > max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"n > max_size()"});
|
||||
}
|
||||
|
||||
//
|
||||
// Operations
|
||||
//
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
void
|
||||
static_string<N, CharT, Traits>::
|
||||
clear()
|
||||
{
|
||||
n_ = 0;
|
||||
term();
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
insert(size_type index, size_type count, CharT ch) ->
|
||||
static_string&
|
||||
{
|
||||
if(index > size())
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range{
|
||||
"index > size()"});
|
||||
insert(begin() + index, count, ch);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
insert(size_type index, CharT const* s, size_type count) ->
|
||||
static_string&
|
||||
{
|
||||
if(index > size())
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range{
|
||||
"index > size()"});
|
||||
if(size() + count > max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"size() + count > max_size()"});
|
||||
Traits::move(
|
||||
&s_[index + count], &s_[index], size() - index);
|
||||
n_ += count;
|
||||
Traits::copy(&s_[index], s, count);
|
||||
term();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<std::size_t M>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
insert(size_type index,
|
||||
static_string<M, CharT, Traits> const& str,
|
||||
size_type index_str, size_type count) ->
|
||||
static_string&
|
||||
{
|
||||
auto const ss = str.substr(index_str, count);
|
||||
return insert(index, ss.data(), ss.size());
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
insert(const_iterator pos, size_type count, CharT ch) ->
|
||||
iterator
|
||||
{
|
||||
if(size() + count > max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"size() + count() > max_size()"});
|
||||
auto const index = pos - &s_[0];
|
||||
Traits::move(
|
||||
&s_[index + count], &s_[index], size() - index);
|
||||
n_ += count;
|
||||
Traits::assign(&s_[index], count, ch);
|
||||
term();
|
||||
return &s_[index];
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<class InputIt>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
insert(const_iterator pos, InputIt first, InputIt last) ->
|
||||
typename std::enable_if<
|
||||
detail::is_input_iterator<InputIt>::value,
|
||||
iterator>::type
|
||||
{
|
||||
std::size_t const count = std::distance(first, last);
|
||||
if(size() + count > max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"size() + count > max_size()"});
|
||||
std::size_t const index = pos - begin();
|
||||
Traits::move(
|
||||
&s_[index + count], &s_[index], size() - index);
|
||||
n_ += count;
|
||||
for(auto it = begin() + index;
|
||||
first != last; ++it, ++first)
|
||||
Traits::assign(*it, *first);
|
||||
term();
|
||||
return begin() + index;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<class T>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
insert(size_type index, const T& t,
|
||||
size_type index_str, size_type count) ->
|
||||
typename std::enable_if<std::is_convertible<
|
||||
T const&, string_view_type>::value &&
|
||||
! std::is_convertible<T const&, CharT const*>::value,
|
||||
static_string&>::type
|
||||
{
|
||||
auto const str =
|
||||
string_view_type(t).substr(index_str, count);
|
||||
return insert(index, str.data(), str.size());
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
erase(size_type index, size_type count) ->
|
||||
static_string&
|
||||
{
|
||||
if(index > size())
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range{
|
||||
"index > size()"});
|
||||
auto const n = (std::min)(count, size() - index);
|
||||
Traits::move(
|
||||
&s_[index], &s_[index + n], size() - (index + n) + 1);
|
||||
n_ -= n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
erase(const_iterator pos) ->
|
||||
iterator
|
||||
{
|
||||
erase(pos - begin(), 1);
|
||||
return begin() + (pos - begin());
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
erase(const_iterator first, const_iterator last) ->
|
||||
iterator
|
||||
{
|
||||
erase(first - begin(),
|
||||
std::distance(first, last));
|
||||
return begin() + (first - begin());
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
void
|
||||
static_string<N, CharT, Traits>::
|
||||
push_back(CharT ch)
|
||||
{
|
||||
if(size() >= max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"size() >= max_size()"});
|
||||
Traits::assign(s_[n_++], ch);
|
||||
term();
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<std::size_t M>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
append(static_string<M, CharT, Traits> const& str,
|
||||
size_type pos, size_type count) ->
|
||||
static_string&
|
||||
{
|
||||
// Valid range is [0, size)
|
||||
if(pos >= str.size())
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range{
|
||||
"pos > str.size()"});
|
||||
string_view_type const ss{&str.s_[pos],
|
||||
(std::min)(count, str.size() - pos)};
|
||||
insert(size(), ss.data(), ss.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
substr(size_type pos, size_type count) const ->
|
||||
string_view_type
|
||||
{
|
||||
if(pos > size())
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range{
|
||||
"pos > size()"});
|
||||
return{&s_[pos], (std::min)(count, size() - pos)};
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
copy(CharT* dest, size_type count, size_type pos) const ->
|
||||
size_type
|
||||
{
|
||||
auto const str = substr(pos, count);
|
||||
Traits::copy(dest, str.data(), str.size());
|
||||
return str.size();
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
void
|
||||
static_string<N, CharT, Traits>::
|
||||
resize(std::size_t n)
|
||||
{
|
||||
if(n > max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"n > max_size()"});
|
||||
n_ = n;
|
||||
term();
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
void
|
||||
static_string<N, CharT, Traits>::
|
||||
resize(std::size_t n, CharT c)
|
||||
{
|
||||
if(n > max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"n > max_size()"});
|
||||
if(n > n_)
|
||||
Traits::assign(&s_[n_], n - n_, c);
|
||||
n_ = n;
|
||||
term();
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
void
|
||||
static_string<N, CharT, Traits>::
|
||||
swap(static_string& str)
|
||||
{
|
||||
static_string tmp(str);
|
||||
str.n_ = n_;
|
||||
Traits::copy(&str.s_[0], &s_[0], n_ + 1);
|
||||
n_ = tmp.n_;
|
||||
Traits::copy(&s_[0], &tmp.s_[0], n_ + 1);
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
template<std::size_t M>
|
||||
void
|
||||
static_string<N, CharT, Traits>::
|
||||
swap(static_string<M, CharT, Traits>& str)
|
||||
{
|
||||
if(size() > str.max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"size() > str.max_size()"});
|
||||
if(str.size() > max_size())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"str.size() > max_size()"});
|
||||
static_string tmp(str);
|
||||
str.n_ = n_;
|
||||
Traits::copy(&str.s_[0], &s_[0], n_ + 1);
|
||||
n_ = tmp.n_;
|
||||
Traits::copy(&s_[0], &tmp.s_[0], n_ + 1);
|
||||
}
|
||||
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
assign_char(CharT ch, std::true_type) ->
|
||||
static_string&
|
||||
{
|
||||
n_ = 1;
|
||||
Traits::assign(s_[0], ch);
|
||||
term();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<std::size_t N, class CharT, class Traits>
|
||||
auto
|
||||
static_string<N, CharT, Traits>::
|
||||
assign_char(CharT, std::false_type) ->
|
||||
static_string&
|
||||
{
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"max_size() == 0"});
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Integer>
|
||||
static_string<max_digits(sizeof(Integer))>
|
||||
to_static_string(Integer x, std::true_type)
|
||||
{
|
||||
if(x == 0)
|
||||
return {'0'};
|
||||
static_string<detail::max_digits(
|
||||
sizeof(Integer))> s;
|
||||
if(x < 0)
|
||||
{
|
||||
x = -x;
|
||||
char buf[max_digits(sizeof(x))];
|
||||
char* p = buf;
|
||||
for(;x > 0; x /= 10)
|
||||
*p++ = "0123456789"[x % 10];
|
||||
s.resize(1 + p - buf);
|
||||
s[0] = '-';
|
||||
auto d = &s[1];
|
||||
while(p > buf)
|
||||
*d++ = *--p;
|
||||
}
|
||||
else
|
||||
{
|
||||
char buf[max_digits(sizeof(x))];
|
||||
char* p = buf;
|
||||
for(;x > 0; x /= 10)
|
||||
*p++ = "0123456789"[x % 10];
|
||||
s.resize(p - buf);
|
||||
auto d = &s[0];
|
||||
while(p > buf)
|
||||
*d++ = *--p;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
template<class Integer>
|
||||
static_string<max_digits(sizeof(Integer))>
|
||||
to_static_string(Integer x, std::false_type)
|
||||
{
|
||||
if(x == 0)
|
||||
return {'0'};
|
||||
char buf[max_digits(sizeof(x))];
|
||||
char* p = buf;
|
||||
for(;x > 0; x /= 10)
|
||||
*p++ = "0123456789"[x % 10];
|
||||
static_string<detail::max_digits(
|
||||
sizeof(Integer))> s;
|
||||
s.resize(p - buf);
|
||||
auto d = &s[0];
|
||||
while(p > buf)
|
||||
*d++ = *--p;
|
||||
return s;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
template<class Integer>
|
||||
static_string<detail::max_digits(sizeof(Integer))>
|
||||
to_static_string(Integer x)
|
||||
{
|
||||
using CharT = char;
|
||||
using Traits = std::char_traits<CharT>;
|
||||
BOOST_STATIC_ASSERT(std::is_integral<Integer>::value);
|
||||
char buf[detail::max_digits(sizeof(Integer))];
|
||||
auto last = buf + sizeof(buf);
|
||||
auto it = detail::raw_to_string<
|
||||
CharT, Integer, Traits>(last, sizeof(buf), x);
|
||||
static_string<detail::max_digits(sizeof(Integer))> s;
|
||||
s.resize(static_cast<std::size_t>(last - it));
|
||||
auto p = s.data();
|
||||
while(it < last)
|
||||
Traits::assign(*p++, *it++);
|
||||
return s;
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
104
include/beast/core/impl/string_param.ipp
Normal file
104
include/beast/core/impl/string_param.ipp
Normal file
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_IMPL_STRING_PARAM_IPP
|
||||
#define BEAST_IMPL_STRING_PARAM_IPP
|
||||
|
||||
namespace beast {
|
||||
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
std::is_integral<T>::value>::type
|
||||
string_param::
|
||||
print(T const& t)
|
||||
{
|
||||
auto const last = buf_ + sizeof(buf_);
|
||||
auto const it = detail::raw_to_string<
|
||||
char, T, std::char_traits<char>>(
|
||||
last, sizeof(buf_), t);
|
||||
sv_ = {it, static_cast<std::size_t>(
|
||||
last - it)};
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
! std::is_integral<T>::value &&
|
||||
! std::is_convertible<T, string_view>::value
|
||||
>::type
|
||||
string_param::
|
||||
print(T const& t)
|
||||
{
|
||||
os_.emplace(buf_, sizeof(buf_));
|
||||
*os_ << t;
|
||||
os_->flush();
|
||||
sv_ = os_->str();
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
string_param::
|
||||
print(string_view const& sv)
|
||||
{
|
||||
sv_ = sv;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
std::is_integral<T>::value>::type
|
||||
string_param::
|
||||
print_1(T const& t)
|
||||
{
|
||||
char buf[detail::max_digits(sizeof(T))];
|
||||
auto const last = buf + sizeof(buf);
|
||||
auto const it = detail::raw_to_string<
|
||||
char, T, std::char_traits<char>>(
|
||||
last, sizeof(buf), t);
|
||||
*os_ << string_view{it,
|
||||
static_cast<std::size_t>(last - it)};
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
! std::is_integral<T>::value>::type
|
||||
string_param::
|
||||
print_1(T const& t)
|
||||
{
|
||||
*os_ << t;
|
||||
}
|
||||
|
||||
template<class T0, class... TN>
|
||||
void
|
||||
string_param::
|
||||
print_n(T0 const& t0, TN const&... tn)
|
||||
{
|
||||
print_1(t0);
|
||||
print_n(tn...);
|
||||
}
|
||||
|
||||
template<class T0, class T1, class... TN>
|
||||
void
|
||||
string_param::
|
||||
print(T0 const& t0, T1 const& t1, TN const&... tn)
|
||||
{
|
||||
os_.emplace(buf_, sizeof(buf_));
|
||||
print_1(t0);
|
||||
print_1(t1);
|
||||
print_n(tn...);
|
||||
os_->flush();
|
||||
sv_ = os_->str();
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
string_param::
|
||||
string_param(Args const&... args)
|
||||
{
|
||||
print(args...);
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
323
include/beast/core/multi_buffer.hpp
Normal file
323
include/beast/core/multi_buffer.hpp
Normal file
@@ -0,0 +1,323 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_MULTI_BUFFER_HPP
|
||||
#define BEAST_MULTI_BUFFER_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/detail/empty_base_optimization.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** A @b DynamicBuffer that uses multiple buffers internally.
|
||||
|
||||
The implementation uses a sequence of one or more character arrays
|
||||
of varying sizes. Additional character array objects are appended to
|
||||
the sequence to accommodate changes in the size of the character
|
||||
sequence.
|
||||
|
||||
@note Meets the requirements of @b DynamicBuffer.
|
||||
|
||||
@tparam Allocator The allocator to use for managing memory.
|
||||
*/
|
||||
template<class Allocator>
|
||||
class basic_multi_buffer
|
||||
#if ! BEAST_DOXYGEN
|
||||
: private detail::empty_base_optimization<
|
||||
typename std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<char>>
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
#if BEAST_DOXYGEN
|
||||
/// The type of allocator used.
|
||||
using allocator_type = Allocator;
|
||||
#else
|
||||
using allocator_type = typename
|
||||
std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<char>;
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Storage for the list of buffers representing the input
|
||||
// and output sequences. The allocation for each element
|
||||
// contains `element` followed by raw storage bytes.
|
||||
class element;
|
||||
|
||||
using alloc_traits = std::allocator_traits<allocator_type>;
|
||||
using list_type = typename boost::intrusive::make_list<element,
|
||||
boost::intrusive::constant_time_size<true>>::type;
|
||||
using iterator = typename list_type::iterator;
|
||||
using const_iterator = typename list_type::const_iterator;
|
||||
|
||||
using size_type = typename std::allocator_traits<Allocator>::size_type;
|
||||
using const_buffer = boost::asio::const_buffer;
|
||||
using mutable_buffer = boost::asio::mutable_buffer;
|
||||
|
||||
static_assert(std::is_base_of<std::bidirectional_iterator_tag,
|
||||
typename std::iterator_traits<iterator>::iterator_category>::value,
|
||||
"BidirectionalIterator requirements not met");
|
||||
|
||||
static_assert(std::is_base_of<std::bidirectional_iterator_tag,
|
||||
typename std::iterator_traits<const_iterator>::iterator_category>::value,
|
||||
"BidirectionalIterator requirements not met");
|
||||
|
||||
std::size_t max_ =
|
||||
(std::numeric_limits<std::size_t>::max)();
|
||||
list_type list_; // list of allocated buffers
|
||||
iterator out_; // element that contains out_pos_
|
||||
size_type in_size_ = 0; // size of the input sequence
|
||||
size_type in_pos_ = 0; // input offset in list_.front()
|
||||
size_type out_pos_ = 0; // output offset in *out_
|
||||
size_type out_end_ = 0; // output end offset in list_.back()
|
||||
|
||||
public:
|
||||
#if BEAST_DOXYGEN
|
||||
/// The type used to represent the input sequence as a list of buffers.
|
||||
using const_buffers_type = implementation_defined;
|
||||
|
||||
/// The type used to represent the output sequence as a list of buffers.
|
||||
using mutable_buffers_type = implementation_defined;
|
||||
|
||||
#else
|
||||
class const_buffers_type;
|
||||
|
||||
class mutable_buffers_type;
|
||||
|
||||
#endif
|
||||
|
||||
/// Destructor
|
||||
~basic_multi_buffer();
|
||||
|
||||
/** Constructor
|
||||
|
||||
Upon construction, capacity will be zero.
|
||||
*/
|
||||
basic_multi_buffer();
|
||||
|
||||
/** Constructor.
|
||||
|
||||
@param limit The setting for @ref max_size.
|
||||
*/
|
||||
explicit
|
||||
basic_multi_buffer(std::size_t limit);
|
||||
|
||||
/** Constructor.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
basic_multi_buffer(Allocator const& alloc);
|
||||
|
||||
/** Constructor.
|
||||
|
||||
@param limit The setting for @ref max_size.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
basic_multi_buffer(
|
||||
std::size_t limit, Allocator const& alloc);
|
||||
|
||||
/** Move constructor
|
||||
|
||||
After the move, `*this` will have an empty output sequence.
|
||||
|
||||
@param other The object to move from. After the move,
|
||||
The object's state will be as if constructed using
|
||||
its current allocator and limit.
|
||||
*/
|
||||
basic_multi_buffer(basic_multi_buffer&& other);
|
||||
|
||||
/** Move constructor
|
||||
|
||||
After the move, `*this` will have an empty output sequence.
|
||||
|
||||
@param other The object to move from. After the move,
|
||||
The object's state will be as if constructed using
|
||||
its current allocator and limit.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
basic_multi_buffer(basic_multi_buffer&& other,
|
||||
Allocator const& alloc);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
@param other The object to copy from.
|
||||
*/
|
||||
basic_multi_buffer(basic_multi_buffer const& other);
|
||||
|
||||
/** Copy constructor
|
||||
|
||||
@param other The object to copy from.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
basic_multi_buffer(basic_multi_buffer const& other,
|
||||
Allocator const& alloc);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
@param other The object to copy from.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_multi_buffer(basic_multi_buffer<
|
||||
OtherAlloc> const& other);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
@param other The object to copy from.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_multi_buffer(basic_multi_buffer<
|
||||
OtherAlloc> const& other, allocator_type const& alloc);
|
||||
|
||||
/** Move assignment
|
||||
|
||||
After the move, `*this` will have an empty output sequence.
|
||||
|
||||
@param other The object to move from. After the move,
|
||||
The object's state will be as if constructed using
|
||||
its current allocator and limit.
|
||||
*/
|
||||
basic_multi_buffer&
|
||||
operator=(basic_multi_buffer&& other);
|
||||
|
||||
/** Copy assignment
|
||||
|
||||
After the copy, `*this` will have an empty output sequence.
|
||||
|
||||
@param other The object to copy from.
|
||||
*/
|
||||
basic_multi_buffer& operator=(basic_multi_buffer const& other);
|
||||
|
||||
/** Copy assignment
|
||||
|
||||
After the copy, `*this` will have an empty output sequence.
|
||||
|
||||
@param other The object to copy from.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_multi_buffer& operator=(
|
||||
basic_multi_buffer<OtherAlloc> const& other);
|
||||
|
||||
/// Returns a copy of the associated allocator.
|
||||
allocator_type
|
||||
get_allocator() const
|
||||
{
|
||||
return this->member();
|
||||
}
|
||||
|
||||
/// Returns the size of the input sequence.
|
||||
size_type
|
||||
size() const
|
||||
{
|
||||
return in_size_;
|
||||
}
|
||||
|
||||
/// Returns the permitted maximum sum of the sizes of the input and output sequence.
|
||||
size_type
|
||||
max_size() const
|
||||
{
|
||||
return max_;
|
||||
}
|
||||
|
||||
/// Returns the maximum sum of the sizes of the input sequence and output sequence the buffer can hold without requiring reallocation.
|
||||
std::size_t
|
||||
capacity() const;
|
||||
|
||||
/** Get a list of buffers that represents the input sequence.
|
||||
|
||||
@note These buffers remain valid across subsequent calls to `prepare`.
|
||||
*/
|
||||
const_buffers_type
|
||||
data() const;
|
||||
|
||||
/** Get a list of buffers that represents the output sequence, with the given size.
|
||||
|
||||
@note Buffers representing the input sequence acquired prior to
|
||||
this call remain valid.
|
||||
*/
|
||||
mutable_buffers_type
|
||||
prepare(size_type n);
|
||||
|
||||
/** Move bytes from the output sequence to the input sequence.
|
||||
|
||||
@note Buffers representing the input sequence acquired prior to
|
||||
this call remain valid.
|
||||
*/
|
||||
void
|
||||
commit(size_type n);
|
||||
|
||||
/// Remove bytes from the input sequence.
|
||||
void
|
||||
consume(size_type n);
|
||||
|
||||
template<class Alloc>
|
||||
friend
|
||||
void
|
||||
swap(
|
||||
basic_multi_buffer<Alloc>& lhs,
|
||||
basic_multi_buffer<Alloc>& rhs);
|
||||
|
||||
private:
|
||||
template<class OtherAlloc>
|
||||
friend class basic_multi_buffer;
|
||||
|
||||
void
|
||||
delete_element(element& e);
|
||||
|
||||
void
|
||||
delete_list();
|
||||
|
||||
void
|
||||
reset();
|
||||
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
copy_from(DynamicBuffer const& other);
|
||||
|
||||
void
|
||||
move_assign(basic_multi_buffer& other, std::false_type);
|
||||
|
||||
void
|
||||
move_assign(basic_multi_buffer& other, std::true_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_multi_buffer const& other, std::false_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_multi_buffer const& other, std::true_type);
|
||||
|
||||
void
|
||||
swap(basic_multi_buffer&);
|
||||
|
||||
void
|
||||
swap(basic_multi_buffer&, std::true_type);
|
||||
|
||||
void
|
||||
swap(basic_multi_buffer&, std::false_type);
|
||||
|
||||
void
|
||||
debug_check() const;
|
||||
};
|
||||
|
||||
/// A typical multi buffer
|
||||
using multi_buffer = basic_multi_buffer<std::allocator<char>>;
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/core/impl/multi_buffer.ipp>
|
||||
|
||||
#endif
|
||||
99
include/beast/core/ostream.hpp
Normal file
99
include/beast/core/ostream.hpp
Normal file
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WRITE_OSTREAM_HPP
|
||||
#define BEAST_WRITE_OSTREAM_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/type_traits.hpp>
|
||||
#include <beast/core/detail/ostream.hpp>
|
||||
#include <type_traits>
|
||||
#include <streambuf>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Return an object representing a @b ConstBufferSequence.
|
||||
|
||||
This function wraps a reference to a buffer sequence and permits
|
||||
the following operation:
|
||||
|
||||
@li `operator<<` to `std::ostream`. No character translation is
|
||||
performed; unprintable and null characters will be transferred
|
||||
as-is to the output stream.
|
||||
|
||||
@par Example
|
||||
@code
|
||||
multi_buffer b;
|
||||
...
|
||||
std::cout << buffers(b.data()) << std::endl;
|
||||
@endcode
|
||||
|
||||
@param b An object meeting the requirements of @b ConstBufferSequence
|
||||
to be streamed. The implementation will make a copy of this object.
|
||||
Ownership of the underlying memory is not transferred, the application
|
||||
is still responsible for managing its lifetime.
|
||||
*/
|
||||
template<class ConstBufferSequence>
|
||||
#if BEAST_DOXYGEN
|
||||
implementation_defined
|
||||
#else
|
||||
detail::buffers_helper<ConstBufferSequence>
|
||||
#endif
|
||||
buffers(ConstBufferSequence const& b)
|
||||
{
|
||||
static_assert(is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
return detail::buffers_helper<
|
||||
ConstBufferSequence>{b};
|
||||
}
|
||||
|
||||
/** Return an output stream that formats values into a @b DynamicBuffer.
|
||||
|
||||
This function wraps the caller provided @b DynamicBuffer into
|
||||
a `std::ostream` derived class, to allow `operator<<` stream style
|
||||
formatting operations.
|
||||
|
||||
@par Example
|
||||
@code
|
||||
ostream(buffer) << "Hello, world!" << std::endl;
|
||||
@endcode
|
||||
|
||||
@note Calling members of the underlying buffer before the output
|
||||
stream is destroyed results in undefined behavior.
|
||||
|
||||
@param buffer An object meeting the requirements of @b DynamicBuffer
|
||||
into which the formatted output will be placed.
|
||||
|
||||
@return An object derived from `std::ostream` which redirects output
|
||||
The wrapped dynamic buffer is not modified, a copy is made instead.
|
||||
Ownership of the underlying memory is not transferred, the application
|
||||
is still responsible for managing its lifetime. The caller is
|
||||
responsible for ensuring the dynamic buffer is not destroyed for the
|
||||
lifetime of the output stream.
|
||||
*/
|
||||
template<class DynamicBuffer>
|
||||
#if BEAST_DOXYGEN
|
||||
implementation_defined
|
||||
#else
|
||||
detail::ostream_helper<
|
||||
DynamicBuffer, char, std::char_traits<char>,
|
||||
detail::basic_streambuf_movable::value>
|
||||
#endif
|
||||
ostream(DynamicBuffer& buffer)
|
||||
{
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
return detail::ostream_helper<
|
||||
DynamicBuffer, char, std::char_traits<char>,
|
||||
detail::basic_streambuf_movable::value>{buffer};
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -1,30 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_PLACEHOLDERS_HPP
|
||||
#define BEAST_PLACEHOLDERS_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <functional>
|
||||
|
||||
namespace beast {
|
||||
namespace asio {
|
||||
|
||||
namespace placeholders {
|
||||
// asio placeholders that work with std::bind
|
||||
namespace {
|
||||
static auto const error (std::placeholders::_1);
|
||||
static auto const bytes_transferred (std::placeholders::_2);
|
||||
static auto const iterator (std::placeholders::_2);
|
||||
static auto const signal_number (std::placeholders::_2);
|
||||
}
|
||||
} // placeholders
|
||||
|
||||
} // asio
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -1,69 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_PREPARE_BUFFER_HPP
|
||||
#define BEAST_PREPARE_BUFFER_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Return a shortened buffer.
|
||||
|
||||
The returned buffer points to the same memory as the
|
||||
passed buffer, but with a size that is equal to or less
|
||||
than the size of the original buffer.
|
||||
|
||||
@param n The size of the returned buffer.
|
||||
|
||||
@param buffer The buffer to shorten. Ownership of the
|
||||
underlying memory is not transferred.
|
||||
|
||||
@return A new buffer that points to the first `n` bytes
|
||||
of the original buffer.
|
||||
*/
|
||||
inline
|
||||
boost::asio::const_buffer
|
||||
prepare_buffer(std::size_t n,
|
||||
boost::asio::const_buffer buffer)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
return { buffer_cast<void const*>(buffer),
|
||||
(std::min)(n, buffer_size(buffer)) };
|
||||
}
|
||||
|
||||
/** Return a shortened buffer.
|
||||
|
||||
The returned buffer points to the same memory as the
|
||||
passed buffer, but with a size that is equal to or less
|
||||
than the size of the original buffer.
|
||||
|
||||
@param n The size of the returned buffer.
|
||||
|
||||
@param buffer The buffer to shorten. Ownership of the
|
||||
underlying memory is not transferred.
|
||||
|
||||
@return A new buffer that points to the first `n` bytes
|
||||
of the original buffer.
|
||||
*/
|
||||
inline
|
||||
boost::asio::mutable_buffer
|
||||
prepare_buffer(std::size_t n,
|
||||
boost::asio::mutable_buffer buffer)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
return { buffer_cast<void*>(buffer),
|
||||
(std::min)(n, buffer_size(buffer)) };
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -1,53 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_PREPARE_BUFFERS_HPP
|
||||
#define BEAST_PREPARE_BUFFERS_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/detail/prepare_buffers.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Return a shortened buffer sequence.
|
||||
|
||||
This function returns a new buffer sequence which adapts the
|
||||
passed buffer sequence and efficiently presents a shorter subset
|
||||
of the original list of buffers starting with the first byte of
|
||||
the original sequence.
|
||||
|
||||
@param n The maximum number of bytes in the wrapped
|
||||
sequence. If this is larger than the size of passed,
|
||||
buffers, the resulting sequence will represent the
|
||||
entire input sequence.
|
||||
|
||||
@param buffers The buffer sequence to adapt. A copy of
|
||||
the sequence will be made, but ownership of the underlying
|
||||
memory is not transferred.
|
||||
*/
|
||||
template<class BufferSequence>
|
||||
#if GENERATING_DOCS
|
||||
implementation_defined
|
||||
#else
|
||||
inline
|
||||
detail::prepared_buffers<BufferSequence>
|
||||
#endif
|
||||
prepare_buffers(std::size_t n, BufferSequence const& buffers)
|
||||
{
|
||||
return detail::prepared_buffers<BufferSequence>(n, buffers);
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
60
include/beast/core/read_size.hpp
Normal file
60
include/beast/core/read_size.hpp
Normal file
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_READ_SIZE_HELPER_HPP
|
||||
#define BEAST_READ_SIZE_HELPER_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/type_traits.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Returns a natural read size.
|
||||
|
||||
This function inspects the capacity, size, and maximum
|
||||
size of the dynamic buffer. Then it computes a natural
|
||||
read size given the passed-in upper limit. It favors
|
||||
a read size that does not require a reallocation, subject
|
||||
to a reasonable minimum to avoid tiny reads.
|
||||
|
||||
@param buffer The dynamic buffer to inspect.
|
||||
|
||||
@param max_size An upper limit on the returned value.
|
||||
|
||||
@note If the buffer is already at its maximum size, zero
|
||||
is returned.
|
||||
*/
|
||||
template<class DynamicBuffer>
|
||||
std::size_t
|
||||
read_size(DynamicBuffer& buffer, std::size_t max_size);
|
||||
|
||||
/** Returns a natural read size or throw if the buffer is full.
|
||||
|
||||
This function inspects the capacity, size, and maximum
|
||||
size of the dynamic buffer. Then it computes a natural
|
||||
read size given the passed-in upper limit. It favors
|
||||
a read size that does not require a reallocation, subject
|
||||
to a reasonable minimum to avoid tiny reads.
|
||||
|
||||
@param buffer The dynamic buffer to inspect.
|
||||
|
||||
@param max_size An upper limit on the returned value.
|
||||
|
||||
@throws std::length_error if `max_size > 0` and the buffer
|
||||
is full.
|
||||
*/
|
||||
template<class DynamicBuffer>
|
||||
std::size_t
|
||||
read_size_or_throw(DynamicBuffer& buffer,
|
||||
std::size_t max_size);
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/core/impl/read_size.ipp>
|
||||
|
||||
#endif
|
||||
211
include/beast/core/span.hpp
Normal file
211
include/beast/core/span.hpp
Normal file
@@ -0,0 +1,211 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_CORE_SPAN_HPP
|
||||
#define BEAST_CORE_SPAN_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** A range of bytes expressed as a ContiguousContainer
|
||||
|
||||
This class implements a non-owning reference to a storage
|
||||
area of a certain size and having an underlying integral
|
||||
type with size of 1.
|
||||
|
||||
@tparam T The type pointed to by span iterators
|
||||
*/
|
||||
template<class T>
|
||||
class span
|
||||
{
|
||||
T* data_ = nullptr;
|
||||
std::size_t size_ = 0;
|
||||
|
||||
public:
|
||||
/// The type of value, including cv qualifiers
|
||||
using element_type = T;
|
||||
|
||||
/// The type of value of each span element
|
||||
using value_type = typename std::remove_const<T>::type;
|
||||
|
||||
/// The type of integer used to index the span
|
||||
using index_type = std::ptrdiff_t;
|
||||
|
||||
/// A pointer to a span element
|
||||
using pointer = T*;
|
||||
|
||||
/// A reference to a span element
|
||||
using reference = T&;
|
||||
|
||||
/// The iterator used by the container
|
||||
using iterator = pointer;
|
||||
|
||||
/// The const pointer used by the container
|
||||
using const_pointer = T const*;
|
||||
|
||||
/// The const reference used by the container
|
||||
using const_reference = T const&;
|
||||
|
||||
/// The const iterator used by the container
|
||||
using const_iterator = const_pointer;
|
||||
|
||||
/// Constructor
|
||||
span() = default;
|
||||
|
||||
/// Constructor
|
||||
span(span const&) = default;
|
||||
|
||||
/// Assignment
|
||||
span& operator=(span const&) = default;
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param data A pointer to the beginning of the range of elements
|
||||
|
||||
@param size The number of elements pointed to by `data`
|
||||
*/
|
||||
span(T* data, std::size_t size)
|
||||
: data_(data), size_(size)
|
||||
{
|
||||
}
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param container The container to construct from
|
||||
*/
|
||||
template<class ContiguousContainer
|
||||
#if ! BEAST_DOXYGEN
|
||||
, class = typename std::enable_if<
|
||||
detail::is_contiguous_container<
|
||||
ContiguousContainer, T>::value>::type
|
||||
#endif
|
||||
>
|
||||
explicit
|
||||
span(ContiguousContainer&& container)
|
||||
: data_(container.data())
|
||||
, size_(container.size())
|
||||
{
|
||||
}
|
||||
|
||||
#if ! BEAST_DOXYGEN
|
||||
template<class CharT, class Traits, class Allocator>
|
||||
explicit
|
||||
span(std::basic_string<CharT, Traits, Allocator>& s)
|
||||
: data_(&s[0])
|
||||
, size_(s.size())
|
||||
{
|
||||
}
|
||||
|
||||
template<class CharT, class Traits, class Allocator>
|
||||
explicit
|
||||
span(std::basic_string<CharT, Traits, Allocator> const& s)
|
||||
: data_(s.data())
|
||||
, size_(s.size())
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Assignment
|
||||
|
||||
@param container The container to assign from
|
||||
*/
|
||||
template<class ContiguousContainer>
|
||||
#if BEAST_DOXYGEN
|
||||
span&
|
||||
#else
|
||||
typename std::enable_if<detail::is_contiguous_container<
|
||||
ContiguousContainer, T>::value,
|
||||
span&>::type
|
||||
#endif
|
||||
operator=(ContiguousContainer&& container)
|
||||
{
|
||||
data_ = container.data();
|
||||
size_ = container.size();
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if ! BEAST_DOXYGEN
|
||||
template<class CharT, class Traits, class Allocator>
|
||||
span&
|
||||
operator=(std::basic_string<
|
||||
CharT, Traits, Allocator>& s)
|
||||
{
|
||||
data_ = &s[0];
|
||||
size_ = s.size();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class CharT, class Traits, class Allocator>
|
||||
span&
|
||||
operator=(std::basic_string<
|
||||
CharT, Traits, Allocator> const& s)
|
||||
{
|
||||
data_ = s.data();
|
||||
size_ = s.size();
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Returns `true` if the span is empty
|
||||
bool
|
||||
empty() const
|
||||
{
|
||||
return size_ == 0;
|
||||
}
|
||||
|
||||
/// Returns a pointer to the beginning of the span
|
||||
T*
|
||||
data() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
/// Returns the number of elements in the span
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
/// Returns an iterator to the beginning of the span
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
/// Returns an iterator to the beginning of the span
|
||||
const_iterator
|
||||
cbegin() const
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
/// Returns an iterator to one past the end of the span
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return data_ + size_;
|
||||
}
|
||||
|
||||
/// Returns an iterator to one past the end of the span
|
||||
const_iterator
|
||||
cend() const
|
||||
{
|
||||
return data_ + size_;
|
||||
}
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
218
include/beast/core/static_buffer.hpp
Normal file
218
include/beast/core/static_buffer.hpp
Normal file
@@ -0,0 +1,218 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_STATIC_BUFFER_HPP
|
||||
#define BEAST_STATIC_BUFFER_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** A @b DynamicBuffer with a fixed size internal buffer.
|
||||
|
||||
Ownership of the underlying storage belongs to the derived class.
|
||||
|
||||
@note Variables are usually declared using the template class
|
||||
@ref static_buffer_n; however, to reduce the number of instantiations
|
||||
of template functions receiving static stream buffer arguments in a
|
||||
deduced context, the signature of the receiving function should use
|
||||
@ref static_buffer.
|
||||
|
||||
When used with @ref static_buffer_n this implements a dynamic
|
||||
buffer using no memory allocations.
|
||||
|
||||
@see @ref static_buffer_n
|
||||
*/
|
||||
class static_buffer
|
||||
{
|
||||
char* begin_;
|
||||
char* in_;
|
||||
char* out_;
|
||||
char* last_;
|
||||
char* end_;
|
||||
|
||||
static_buffer(static_buffer const& other) = delete;
|
||||
static_buffer& operator=(static_buffer const&) = delete;
|
||||
|
||||
public:
|
||||
/// The type used to represent the input sequence as a list of buffers.
|
||||
using const_buffers_type = boost::asio::const_buffers_1;
|
||||
|
||||
/// The type used to represent the output sequence as a list of buffers.
|
||||
using mutable_buffers_type = boost::asio::mutable_buffers_1;
|
||||
|
||||
/** Constructor.
|
||||
|
||||
This creates a dynamic buffer using the provided storage area.
|
||||
|
||||
@param p A pointer to valid storage of at least `n` bytes.
|
||||
|
||||
@param n The number of valid bytes pointed to by `p`.
|
||||
*/
|
||||
static_buffer(void* p, std::size_t n)
|
||||
{
|
||||
reset_impl(p, n);
|
||||
}
|
||||
|
||||
/// Return the size of the input sequence.
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
return out_ - in_;
|
||||
}
|
||||
|
||||
/// Return the maximum sum of the input and output sequence sizes.
|
||||
std::size_t
|
||||
max_size() const
|
||||
{
|
||||
return dist(begin_, end_);
|
||||
}
|
||||
|
||||
/// Return the maximum sum of input and output sizes that can be held without an allocation.
|
||||
std::size_t
|
||||
capacity() const
|
||||
{
|
||||
return max_size();
|
||||
}
|
||||
|
||||
/** Get a list of buffers that represent the input sequence.
|
||||
|
||||
@note These buffers remain valid across subsequent calls to `prepare`.
|
||||
*/
|
||||
const_buffers_type
|
||||
data() const;
|
||||
|
||||
/** Get a list of buffers that represent the output sequence, with the given size.
|
||||
|
||||
@throws std::length_error if the size would exceed the limit
|
||||
imposed by the underlying mutable buffer sequence.
|
||||
|
||||
@note Buffers representing the input sequence acquired prior to
|
||||
this call remain valid.
|
||||
*/
|
||||
mutable_buffers_type
|
||||
prepare(std::size_t n);
|
||||
|
||||
/** Move bytes from the output sequence to the input sequence.
|
||||
|
||||
@note Buffers representing the input sequence acquired prior to
|
||||
this call remain valid.
|
||||
*/
|
||||
void
|
||||
commit(std::size_t n)
|
||||
{
|
||||
out_ += std::min<std::size_t>(n, last_ - out_);
|
||||
}
|
||||
|
||||
/// Remove bytes from the input sequence.
|
||||
void
|
||||
consume(std::size_t n)
|
||||
{
|
||||
consume_impl(n);
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Default constructor.
|
||||
|
||||
The buffer will be in an undefined state. It is necessary
|
||||
for the derived class to call @ref reset in order to
|
||||
initialize the object.
|
||||
*/
|
||||
static_buffer();
|
||||
|
||||
/** Reset the pointed-to buffer.
|
||||
|
||||
This function resets the internal state to the buffer provided.
|
||||
All input and output sequences are invalidated. This function
|
||||
allows the derived class to construct its members before
|
||||
initializing the static buffer.
|
||||
|
||||
@param p A pointer to valid storage of at least `n` bytes.
|
||||
|
||||
@param n The number of valid bytes pointed to by `p`.
|
||||
*/
|
||||
void
|
||||
reset(void* p, std::size_t n);
|
||||
|
||||
private:
|
||||
static
|
||||
inline
|
||||
std::size_t
|
||||
dist(char const* first, char const* last)
|
||||
{
|
||||
return static_cast<std::size_t>(last - first);
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
reset_impl(void* p, std::size_t n);
|
||||
|
||||
template<class = void>
|
||||
mutable_buffers_type
|
||||
prepare_impl(std::size_t n);
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
consume_impl(std::size_t n);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A @b DynamicBuffer with a fixed size internal buffer.
|
||||
|
||||
This implements a dynamic buffer using no memory allocations.
|
||||
|
||||
@tparam N The number of bytes in the internal buffer.
|
||||
|
||||
@note To reduce the number of template instantiations when passing
|
||||
objects of this type in a deduced context, the signature of the
|
||||
receiving function should use @ref static_buffer instead.
|
||||
|
||||
@see @ref static_buffer
|
||||
*/
|
||||
template<std::size_t N>
|
||||
class static_buffer_n : public static_buffer
|
||||
{
|
||||
char buf_[N];
|
||||
|
||||
public:
|
||||
/// Copy constructor
|
||||
static_buffer_n(static_buffer_n const&);
|
||||
|
||||
/// Copy assignment
|
||||
static_buffer_n& operator=(static_buffer_n const&);
|
||||
|
||||
/// Construct a static buffer.
|
||||
static_buffer_n()
|
||||
: static_buffer(buf_, N)
|
||||
{
|
||||
}
|
||||
|
||||
/// Returns the @ref static_buffer portion of this object
|
||||
static_buffer&
|
||||
base()
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Returns the @ref static_buffer portion of this object
|
||||
static_buffer const&
|
||||
base() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/core/impl/static_buffer.ipp>
|
||||
|
||||
#endif
|
||||
@@ -1,199 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_STATIC_STREAMBUF_HPP
|
||||
#define BEAST_STATIC_STREAMBUF_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <boost/utility/base_from_member.hpp>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** A @b `DynamicBuffer` with a fixed size internal buffer.
|
||||
|
||||
Ownership of the underlying storage belongs to the derived class.
|
||||
|
||||
@note Variables are usually declared using the template class
|
||||
@ref static_streambuf_n; however, to reduce the number of instantiations
|
||||
of template functions receiving static stream buffer arguments in a
|
||||
deduced context, the signature of the receiving function should use
|
||||
@ref static_streambuf.
|
||||
*/
|
||||
class static_streambuf
|
||||
{
|
||||
#if GENERATING_DOCS
|
||||
private:
|
||||
#else
|
||||
protected:
|
||||
#endif
|
||||
std::uint8_t* begin_;
|
||||
std::uint8_t* in_;
|
||||
std::uint8_t* out_;
|
||||
std::uint8_t* last_;
|
||||
std::uint8_t* end_;
|
||||
|
||||
public:
|
||||
#if GENERATING_DOCS
|
||||
/// The type used to represent the input sequence as a list of buffers.
|
||||
using const_buffers_type = implementation_defined;
|
||||
|
||||
/// The type used to represent the output sequence as a list of buffers.
|
||||
using mutable_buffers_type = implementation_defined;
|
||||
|
||||
#else
|
||||
class const_buffers_type;
|
||||
class mutable_buffers_type;
|
||||
|
||||
static_streambuf(
|
||||
static_streambuf const& other) noexcept = delete;
|
||||
|
||||
static_streambuf& operator=(
|
||||
static_streambuf const&) noexcept = delete;
|
||||
|
||||
#endif
|
||||
|
||||
/// Return the size of the input sequence.
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
return out_ - in_;
|
||||
}
|
||||
|
||||
/// Return the maximum sum of the input and output sequence sizes.
|
||||
std::size_t
|
||||
max_size() const
|
||||
{
|
||||
return end_ - begin_;
|
||||
}
|
||||
|
||||
/// Return the maximum sum of input and output sizes that can be held without an allocation.
|
||||
std::size_t
|
||||
capacity() const
|
||||
{
|
||||
return end_ - in_;
|
||||
}
|
||||
|
||||
/** Get a list of buffers that represent the input sequence.
|
||||
|
||||
@note These buffers remain valid across subsequent calls to `prepare`.
|
||||
*/
|
||||
const_buffers_type
|
||||
data() const;
|
||||
|
||||
/** Get a list of buffers that represent the output sequence, with the given size.
|
||||
|
||||
@throws std::length_error if the size would exceed the limit
|
||||
imposed by the underlying mutable buffer sequence.
|
||||
|
||||
@note Buffers representing the input sequence acquired prior to
|
||||
this call remain valid.
|
||||
*/
|
||||
mutable_buffers_type
|
||||
prepare(std::size_t n);
|
||||
|
||||
/** Move bytes from the output sequence to the input sequence.
|
||||
|
||||
@note Buffers representing the input sequence acquired prior to
|
||||
this call remain valid.
|
||||
*/
|
||||
void
|
||||
commit(std::size_t n)
|
||||
{
|
||||
out_ += std::min<std::size_t>(n, last_ - out_);
|
||||
}
|
||||
|
||||
/// Remove bytes from the input sequence.
|
||||
void
|
||||
consume(std::size_t n)
|
||||
{
|
||||
in_ += std::min<std::size_t>(n, out_ - in_);
|
||||
}
|
||||
|
||||
#if GENERATING_DOCS
|
||||
private:
|
||||
#else
|
||||
protected:
|
||||
#endif
|
||||
static_streambuf(std::uint8_t* p, std::size_t n)
|
||||
{
|
||||
reset(p, n);
|
||||
}
|
||||
|
||||
void
|
||||
reset(std::uint8_t* p, std::size_t n)
|
||||
{
|
||||
begin_ = p;
|
||||
in_ = p;
|
||||
out_ = p;
|
||||
last_ = p;
|
||||
end_ = p + n;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A `DynamicBuffer` with a fixed size internal buffer.
|
||||
|
||||
@tparam N The number of bytes in the internal buffer.
|
||||
|
||||
@note To reduce the number of template instantiations when passing
|
||||
objects of this type in a deduced context, the signature of the
|
||||
receiving function should use `static_streambuf` instead.
|
||||
*/
|
||||
template<std::size_t N>
|
||||
class static_streambuf_n
|
||||
: public static_streambuf
|
||||
#if ! GENERATING_DOCS
|
||||
, private boost::base_from_member<
|
||||
std::array<std::uint8_t, N>>
|
||||
#endif
|
||||
{
|
||||
using member_type = boost::base_from_member<
|
||||
std::array<std::uint8_t, N>>;
|
||||
public:
|
||||
#if GENERATING_DOCS
|
||||
private:
|
||||
#endif
|
||||
static_streambuf_n(
|
||||
static_streambuf_n const&) = delete;
|
||||
static_streambuf_n& operator=(
|
||||
static_streambuf_n const&) = delete;
|
||||
#if GENERATING_DOCS
|
||||
public:
|
||||
#endif
|
||||
|
||||
/// Construct a static stream buffer.
|
||||
static_streambuf_n()
|
||||
: static_streambuf(
|
||||
member_type::member.data(),
|
||||
member_type::member.size())
|
||||
{
|
||||
}
|
||||
|
||||
/** Reset the stream buffer.
|
||||
|
||||
Postconditions:
|
||||
The input sequence and output sequence are empty,
|
||||
`max_size()` returns `N`.
|
||||
*/
|
||||
void
|
||||
reset()
|
||||
{
|
||||
static_streambuf::reset(
|
||||
member_type::member.data(),
|
||||
member_type::member.size());
|
||||
}
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/core/impl/static_streambuf.ipp>
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,77 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_STREAM_CONCEPTS_HPP
|
||||
#define BEAST_STREAM_CONCEPTS_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/detail/stream_concepts.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/// Determine if `T` has the `get_io_service` member.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct has_get_io_service : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
using has_get_io_service = typename detail::has_get_io_service<T>::type;
|
||||
#endif
|
||||
|
||||
/// Determine if `T` meets the requirements of @b `AsyncReadStream`.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct is_AsyncReadStream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
using is_AsyncReadStream = typename detail::is_AsyncReadStream<T>::type;
|
||||
#endif
|
||||
|
||||
/// Determine if `T` meets the requirements of @b `AsyncWriteStream`.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct is_AsyncWriteStream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
using is_AsyncWriteStream = typename detail::is_AsyncWriteStream<T>::type;
|
||||
#endif
|
||||
|
||||
/// Determine if `T` meets the requirements of @b `SyncReadStream`.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct is_SyncReadStream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
using is_SyncReadStream = typename detail::is_SyncReadStream<T>::type;
|
||||
#endif
|
||||
|
||||
/// Determine if `T` meets the requirements of @b `SyncWriterStream`.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct is_SyncWriteStream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
using is_SyncWriteStream = typename detail::is_SyncWriteStream<T>::type;
|
||||
#endif
|
||||
|
||||
/// Determine if `T` meets the requirements of @b `AsyncStream`.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct is_AsyncStream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
using is_AsyncStream = std::integral_constant<bool,
|
||||
is_AsyncReadStream<T>::value && is_AsyncWriteStream<T>::value>;
|
||||
#endif
|
||||
|
||||
/// Determine if `T` meets the requirements of @b `SyncStream`.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct is_SyncStream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
using is_SyncStream = std::integral_constant<bool,
|
||||
is_SyncReadStream<T>::value && is_SyncWriteStream<T>::value>;
|
||||
#endif
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -1,345 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_STREAMBUF_HPP
|
||||
#define BEAST_STREAMBUF_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/detail/empty_base_optimization.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** A @b `DynamicBuffer` that uses multiple buffers internally.
|
||||
|
||||
The implementation uses a sequence of one or more character arrays
|
||||
of varying sizes. Additional character array objects are appended to
|
||||
the sequence to accommodate changes in the size of the character
|
||||
sequence.
|
||||
|
||||
@note Meets the requirements of @b DynamicBuffer.
|
||||
|
||||
@tparam Allocator The allocator to use for managing memory.
|
||||
*/
|
||||
template<class Allocator>
|
||||
class basic_streambuf
|
||||
#if ! GENERATING_DOCS
|
||||
: private detail::empty_base_optimization<
|
||||
typename std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<char>>
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
#if GENERATING_DOCS
|
||||
/// The type of allocator used.
|
||||
using allocator_type = Allocator;
|
||||
#else
|
||||
using allocator_type = typename
|
||||
std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<char>;
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Storage for the list of buffers representing the input
|
||||
// and output sequences. The allocation for each element
|
||||
// contains `element` followed by raw storage bytes.
|
||||
class element;
|
||||
|
||||
using alloc_traits = std::allocator_traits<allocator_type>;
|
||||
using list_type = typename boost::intrusive::make_list<element,
|
||||
boost::intrusive::constant_time_size<true>>::type;
|
||||
using iterator = typename list_type::iterator;
|
||||
using const_iterator = typename list_type::const_iterator;
|
||||
|
||||
using size_type = typename std::allocator_traits<Allocator>::size_type;
|
||||
using const_buffer = boost::asio::const_buffer;
|
||||
using mutable_buffer = boost::asio::mutable_buffer;
|
||||
|
||||
static_assert(std::is_base_of<std::bidirectional_iterator_tag,
|
||||
typename std::iterator_traits<iterator>::iterator_category>::value,
|
||||
"BidirectionalIterator requirements not met");
|
||||
|
||||
static_assert(std::is_base_of<std::bidirectional_iterator_tag,
|
||||
typename std::iterator_traits<const_iterator>::iterator_category>::value,
|
||||
"BidirectionalIterator requirements not met");
|
||||
|
||||
list_type list_; // list of allocated buffers
|
||||
iterator out_; // element that contains out_pos_
|
||||
size_type alloc_size_; // min amount to allocate
|
||||
size_type in_size_ = 0; // size of the input sequence
|
||||
size_type in_pos_ = 0; // input offset in list_.front()
|
||||
size_type out_pos_ = 0; // output offset in *out_
|
||||
size_type out_end_ = 0; // output end offset in list_.back()
|
||||
|
||||
public:
|
||||
#if GENERATING_DOCS
|
||||
/// The type used to represent the input sequence as a list of buffers.
|
||||
using const_buffers_type = implementation_defined;
|
||||
|
||||
/// The type used to represent the output sequence as a list of buffers.
|
||||
using mutable_buffers_type = implementation_defined;
|
||||
|
||||
#else
|
||||
class const_buffers_type;
|
||||
|
||||
class mutable_buffers_type;
|
||||
|
||||
#endif
|
||||
|
||||
/// Destructor.
|
||||
~basic_streambuf();
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
The new object will have the input sequence of
|
||||
the other stream buffer, and an empty output sequence.
|
||||
|
||||
@note After the move, the moved-from object will have
|
||||
an empty input and output sequence, with no internal
|
||||
buffers allocated.
|
||||
*/
|
||||
basic_streambuf(basic_streambuf&&);
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
The new object will have the input sequence of
|
||||
the other stream buffer, and an empty output sequence.
|
||||
|
||||
@note After the move, the moved-from object will have
|
||||
an empty input and output sequence, with no internal
|
||||
buffers allocated.
|
||||
|
||||
@param alloc The allocator to associate with the
|
||||
stream buffer.
|
||||
*/
|
||||
basic_streambuf(basic_streambuf&&,
|
||||
allocator_type const& alloc);
|
||||
|
||||
/** Move assignment.
|
||||
|
||||
This object will have the input sequence of
|
||||
the other stream buffer, and an empty output sequence.
|
||||
|
||||
@note After the move, the moved-from object will have
|
||||
an empty input and output sequence, with no internal
|
||||
buffers allocated.
|
||||
*/
|
||||
basic_streambuf&
|
||||
operator=(basic_streambuf&&);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
This object will have a copy of the other stream
|
||||
buffer's input sequence, and an empty output sequence.
|
||||
*/
|
||||
basic_streambuf(basic_streambuf const&);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
This object will have a copy of the other stream
|
||||
buffer's input sequence, and an empty output sequence.
|
||||
|
||||
@param alloc The allocator to associate with the
|
||||
stream buffer.
|
||||
*/
|
||||
basic_streambuf(basic_streambuf const&,
|
||||
allocator_type const& alloc);
|
||||
|
||||
/** Copy assignment.
|
||||
|
||||
This object will have a copy of the other stream
|
||||
buffer's input sequence, and an empty output sequence.
|
||||
*/
|
||||
basic_streambuf& operator=(basic_streambuf const&);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
This object will have a copy of the other stream
|
||||
buffer's input sequence, and an empty output sequence.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_streambuf(basic_streambuf<OtherAlloc> const&);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
This object will have a copy of the other stream
|
||||
buffer's input sequence, and an empty output sequence.
|
||||
|
||||
@param alloc The allocator to associate with the
|
||||
stream buffer.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_streambuf(basic_streambuf<OtherAlloc> const&,
|
||||
allocator_type const& alloc);
|
||||
|
||||
/** Copy assignment.
|
||||
|
||||
This object will have a copy of the other stream
|
||||
buffer's input sequence, and an empty output sequence.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_streambuf& operator=(basic_streambuf<OtherAlloc> const&);
|
||||
|
||||
/** Construct a stream buffer.
|
||||
|
||||
@param alloc_size The size of buffer to allocate. This is a
|
||||
soft limit, calls to prepare for buffers exceeding this size
|
||||
will allocate the larger size. The default allocation size
|
||||
is 1KB (1024 bytes).
|
||||
|
||||
@param alloc The allocator to use. If this parameter is
|
||||
unspecified, a default constructed allocator will be used.
|
||||
*/
|
||||
explicit
|
||||
basic_streambuf(std::size_t alloc_size = 1024,
|
||||
Allocator const& alloc = allocator_type{});
|
||||
|
||||
/// Returns a copy of the associated allocator.
|
||||
allocator_type
|
||||
get_allocator() const
|
||||
{
|
||||
return this->member();
|
||||
}
|
||||
|
||||
/** Returns the default allocation size.
|
||||
|
||||
This is the smallest size that the stream buffer will allocate.
|
||||
The size of the allocation can influence capacity, which will
|
||||
affect algorithms that use capacity to efficiently read from
|
||||
streams.
|
||||
*/
|
||||
std::size_t
|
||||
alloc_size() const
|
||||
{
|
||||
return alloc_size_;
|
||||
}
|
||||
|
||||
/** Set the default allocation size.
|
||||
|
||||
This is the smallest size that the stream buffer will allocate.
|
||||
The size of the allocation can influence capacity, which will
|
||||
affect algorithms that use capacity to efficiently read from
|
||||
streams.
|
||||
|
||||
@note This will not affect any already-existing allocations.
|
||||
|
||||
@param n The number of bytes.
|
||||
*/
|
||||
void
|
||||
alloc_size(std::size_t n)
|
||||
{
|
||||
alloc_size_ = n;
|
||||
}
|
||||
|
||||
/// Returns the size of the input sequence.
|
||||
size_type
|
||||
size() const
|
||||
{
|
||||
return in_size_;
|
||||
}
|
||||
|
||||
/// Returns the permitted maximum sum of the sizes of the input and output sequence.
|
||||
size_type
|
||||
max_size() const
|
||||
{
|
||||
return (std::numeric_limits<std::size_t>::max)();
|
||||
}
|
||||
|
||||
/// Returns the maximum sum of the sizes of the input sequence and output sequence the buffer can hold without requiring reallocation.
|
||||
std::size_t
|
||||
capacity() const;
|
||||
|
||||
/** Get a list of buffers that represents the input sequence.
|
||||
|
||||
@note These buffers remain valid across subsequent calls to `prepare`.
|
||||
*/
|
||||
const_buffers_type
|
||||
data() const;
|
||||
|
||||
/** Get a list of buffers that represents the output sequence, with the given size.
|
||||
|
||||
@note Buffers representing the input sequence acquired prior to
|
||||
this call remain valid.
|
||||
*/
|
||||
mutable_buffers_type
|
||||
prepare(size_type n);
|
||||
|
||||
/** Move bytes from the output sequence to the input sequence.
|
||||
|
||||
@note Buffers representing the input sequence acquired prior to
|
||||
this call remain valid.
|
||||
*/
|
||||
void
|
||||
commit(size_type n);
|
||||
|
||||
/// Remove bytes from the input sequence.
|
||||
void
|
||||
consume(size_type n);
|
||||
|
||||
// Helper for boost::asio::read_until
|
||||
template<class OtherAllocator>
|
||||
friend
|
||||
std::size_t
|
||||
read_size_helper(basic_streambuf<
|
||||
OtherAllocator> const& streambuf, std::size_t max_size);
|
||||
|
||||
private:
|
||||
void
|
||||
clear();
|
||||
|
||||
void
|
||||
move_assign(basic_streambuf& other, std::false_type);
|
||||
|
||||
void
|
||||
move_assign(basic_streambuf& other, std::true_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_streambuf const& other, std::false_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_streambuf const& other, std::true_type);
|
||||
|
||||
void
|
||||
delete_list();
|
||||
|
||||
void
|
||||
debug_check() const;
|
||||
};
|
||||
|
||||
/** A @b `DynamicBuffer` that uses multiple buffers internally.
|
||||
|
||||
The implementation uses a sequence of one or more character arrays
|
||||
of varying sizes. Additional character array objects are appended to
|
||||
the sequence to accommodate changes in the size of the character
|
||||
sequence.
|
||||
|
||||
@note Meets the requirements of @b `DynamicBuffer`.
|
||||
*/
|
||||
using streambuf = basic_streambuf<std::allocator<char>>;
|
||||
|
||||
/** Format output to a @ref basic_streambuf.
|
||||
|
||||
@param streambuf The @ref basic_streambuf to write to.
|
||||
|
||||
@param t The object to write.
|
||||
|
||||
@return A reference to the @ref basic_streambuf.
|
||||
*/
|
||||
template<class Allocator, class T>
|
||||
basic_streambuf<Allocator>&
|
||||
operator<<(basic_streambuf<Allocator>& streambuf, T const& t);
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/core/impl/streambuf.ipp>
|
||||
|
||||
#endif
|
||||
151
include/beast/core/string.hpp
Normal file
151
include/beast/core/string.hpp
Normal file
@@ -0,0 +1,151 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_STRING_HPP
|
||||
#define BEAST_STRING_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <boost/version.hpp>
|
||||
#ifndef BEAST_NO_BOOST_STRING_VIEW
|
||||
# if BOOST_VERSION >= 106400
|
||||
# define BEAST_NO_BOOST_STRING_VIEW 0
|
||||
# else
|
||||
# define BEAST_NO_BOOST_STRING_VIEW 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if BEAST_NO_BOOST_STRING_VIEW
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#else
|
||||
#include <boost/utility/string_view.hpp>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace beast {
|
||||
|
||||
#if BEAST_NO_BOOST_STRING_VIEW
|
||||
/// The type of string view used by the library
|
||||
using string_view = boost::string_ref;
|
||||
|
||||
/// The type of basic string view used by the library
|
||||
template<class CharT, class Traits>
|
||||
using basic_string_view =
|
||||
boost::basic_string_ref<CharT, Traits>;
|
||||
#else
|
||||
/// The type of string view used by the library
|
||||
using string_view = boost::string_view;
|
||||
|
||||
/// The type of basic string view used by the library
|
||||
template<class CharT, class Traits>
|
||||
using basic_string_view =
|
||||
boost::basic_string_view<CharT, Traits>;
|
||||
#endif
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
char
|
||||
ascii_tolower(char c)
|
||||
{
|
||||
if(c >= 'A' && c <= 'Z')
|
||||
c += 'a' - 'A';
|
||||
return c;
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
bool
|
||||
iequals(
|
||||
beast::string_view const& lhs,
|
||||
beast::string_view const& rhs)
|
||||
{
|
||||
auto n = lhs.size();
|
||||
if(rhs.size() != n)
|
||||
return false;
|
||||
auto p1 = lhs.data();
|
||||
auto p2 = rhs.data();
|
||||
char a, b;
|
||||
while(n--)
|
||||
{
|
||||
a = *p1++;
|
||||
b = *p2++;
|
||||
if(a != b)
|
||||
goto slow;
|
||||
}
|
||||
return true;
|
||||
|
||||
while(n--)
|
||||
{
|
||||
slow:
|
||||
if(ascii_tolower(a) != ascii_tolower(b))
|
||||
return false;
|
||||
a = *p1++;
|
||||
b = *p2++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
/** Returns `true` if two strings are equal, using a case-insensitive comparison.
|
||||
|
||||
The case-comparison operation is defined only for low-ASCII characters.
|
||||
|
||||
@param lhs The string on the left side of the equality
|
||||
|
||||
@param rhs The string on the right side of the equality
|
||||
*/
|
||||
inline
|
||||
bool
|
||||
iequals(
|
||||
beast::string_view const& lhs,
|
||||
beast::string_view const& rhs)
|
||||
{
|
||||
return detail::iequals(lhs, rhs);
|
||||
}
|
||||
|
||||
/** A case-insensitive less predicate for strings.
|
||||
|
||||
The case-comparison operation is defined only for low-ASCII characters.
|
||||
*/
|
||||
struct iless
|
||||
{
|
||||
bool
|
||||
operator()(
|
||||
string_view const& lhs,
|
||||
string_view const& rhs) const
|
||||
{
|
||||
using std::begin;
|
||||
using std::end;
|
||||
return std::lexicographical_compare(
|
||||
begin(lhs), end(lhs), begin(rhs), end(rhs),
|
||||
[](char c1, char c2)
|
||||
{
|
||||
return detail::ascii_tolower(c1) < detail::ascii_tolower(c2);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/** A case-insensitive equality predicate for strings.
|
||||
|
||||
The case-comparison operation is defined only for low-ASCII characters.
|
||||
*/
|
||||
struct iequal
|
||||
{
|
||||
bool
|
||||
operator()(
|
||||
string_view const& lhs,
|
||||
string_view const& rhs) const
|
||||
{
|
||||
return iequals(lhs, rhs);
|
||||
}
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
109
include/beast/core/string_param.hpp
Normal file
109
include/beast/core/string_param.hpp
Normal file
@@ -0,0 +1,109 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_STRING_PARAM_HPP
|
||||
#define BEAST_STRING_PARAM_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/string.hpp>
|
||||
#include <beast/core/static_string.hpp>
|
||||
#include <beast/core/detail/static_ostream.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** A function parameter which efficiently converts to string.
|
||||
|
||||
This is used as a function parameter type to allow callers
|
||||
notational convenience: objects other than strings may be
|
||||
passed in contexts where a string is expected. The conversion
|
||||
to string is made using `operator<<` to a non-dynamically
|
||||
allocated static buffer if possible, else to a `std::string`
|
||||
on overflow.
|
||||
*/
|
||||
class string_param
|
||||
{
|
||||
string_view sv_;
|
||||
char buf_[128];
|
||||
boost::optional<detail::static_ostream> os_;
|
||||
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
std::is_integral<T>::value>::type
|
||||
print(T const&);
|
||||
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
! std::is_integral<T>::value &&
|
||||
! std::is_convertible<T, string_view>::value
|
||||
>::type
|
||||
print(T const&);
|
||||
|
||||
void
|
||||
print(string_view const&);
|
||||
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
std::is_integral<T>::value>::type
|
||||
print_1(T const&);
|
||||
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
! std::is_integral<T>::value>::type
|
||||
print_1(T const&);
|
||||
|
||||
void
|
||||
print_n()
|
||||
{
|
||||
}
|
||||
|
||||
template<class T0, class... TN>
|
||||
void
|
||||
print_n(T0 const&, TN const&...);
|
||||
|
||||
template<class T0, class T1, class... TN>
|
||||
void
|
||||
print(T0 const&, T1 const&, TN const&...);
|
||||
|
||||
public:
|
||||
/// Copy constructor (disallowed)
|
||||
string_param(string_param const&) = delete;
|
||||
|
||||
/// Copy assignment (disallowed)
|
||||
string_param& operator=(string_param const&) = delete;
|
||||
|
||||
/** Constructor
|
||||
|
||||
This function constructs a string as if by concatenating
|
||||
the result of streaming each argument in order into an
|
||||
output stream. It is used as a notational convenience
|
||||
at call sites which expect a parameter with the semantics
|
||||
of a @ref string_view.
|
||||
|
||||
The implementation uses a small, internal static buffer
|
||||
to avoid memory allocations especially for the case where
|
||||
the list of arguments to be converted consists of a single
|
||||
integral type.
|
||||
|
||||
@param args One or more arguments to convert
|
||||
*/
|
||||
template<class... Args>
|
||||
string_param(Args const&... args);
|
||||
|
||||
/// Implicit conversion to @ref string_view
|
||||
operator string_view const() const
|
||||
{
|
||||
return sv_;
|
||||
}
|
||||
};
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/core/impl/string_param.ipp>
|
||||
|
||||
#endif
|
||||
@@ -1,53 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_TO_STRING_HPP
|
||||
#define BEAST_TO_STRING_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Convert a @b `ConstBufferSequence` to a `std::string`.
|
||||
|
||||
This function will convert the octets in a buffer sequence to a string.
|
||||
All octets will be inserted into the resulting string, including null
|
||||
or unprintable characters.
|
||||
|
||||
@param buffers The buffer sequence to convert.
|
||||
|
||||
@return A string representing the contents of the input area.
|
||||
|
||||
@note This function participates in overload resolution only if
|
||||
the buffers parameter meets the requirements of @b `ConstBufferSequence`.
|
||||
*/
|
||||
template<class ConstBufferSequence>
|
||||
#if GENERATING_DOCS
|
||||
std::string
|
||||
#else
|
||||
typename std::enable_if<
|
||||
is_ConstBufferSequence<ConstBufferSequence>::value,
|
||||
std::string>::type
|
||||
#endif
|
||||
to_string(ConstBufferSequence const& buffers)
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
std::string s;
|
||||
s.reserve(buffer_size(buffers));
|
||||
for(auto const& buffer : buffers)
|
||||
s.append(buffer_cast<char const*>(buffer),
|
||||
buffer_size(buffer));
|
||||
return s;
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
633
include/beast/core/type_traits.hpp
Normal file
633
include/beast/core/type_traits.hpp
Normal file
@@ -0,0 +1,633 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_TYPE_TRAITS_HPP
|
||||
#define BEAST_TYPE_TRAITS_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/file_base.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Buffer concepts
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Determine if `T` meets the requirements of @b ConstBufferSequence.
|
||||
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` meets the requirements,
|
||||
else the type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
template<class ConstBufferSequence>
|
||||
void f(ConstBufferSequence const& buffers)
|
||||
{
|
||||
static_assert(is_const_buffer_sequence<ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
...
|
||||
@endcode
|
||||
|
||||
Use with `std::enable_if` (SFINAE):
|
||||
|
||||
@code
|
||||
template<class ConstBufferSequence>
|
||||
typename std::enable_if<is_const_buffer_sequence<ConstBufferSequence>::value>::type
|
||||
f(ConstBufferSequence const& buffers);
|
||||
@endcode
|
||||
*/
|
||||
template<class T>
|
||||
#if BEAST_DOXYGEN
|
||||
struct is_const_buffer_sequence : std::integral_constant<bool, ...>
|
||||
#else
|
||||
struct is_const_buffer_sequence :
|
||||
detail::is_buffer_sequence<T, boost::asio::const_buffer>
|
||||
#endif
|
||||
{
|
||||
};
|
||||
|
||||
/** Determine if `T` meets the requirements of @b MutableBufferSequence.
|
||||
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` meets the requirements,
|
||||
else the type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
template<class MutableBufferSequence>
|
||||
void f(MutableBufferSequence const& buffers)
|
||||
{
|
||||
static_assert(is_const_buffer_sequence<MutableBufferSequence>::value,
|
||||
"MutableBufferSequence requirements not met");
|
||||
...
|
||||
@endcode
|
||||
|
||||
Use with `std::enable_if` (SFINAE):
|
||||
|
||||
@code
|
||||
template<class MutableBufferSequence>
|
||||
typename std::enable_if<is_mutable_buffer_sequence<MutableBufferSequence>::value>::type
|
||||
f(MutableBufferSequence const& buffers);
|
||||
@endcode
|
||||
*/
|
||||
template<class T>
|
||||
#if BEAST_DOXYGEN
|
||||
struct is_mutable_buffer_sequence : std::integral_constant<bool, ...>
|
||||
#else
|
||||
struct is_mutable_buffer_sequence :
|
||||
detail::is_buffer_sequence<T, boost::asio::mutable_buffer>
|
||||
#endif
|
||||
{
|
||||
};
|
||||
|
||||
/** Determine if `T` meets the requirements of @b DynamicBuffer.
|
||||
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` meets the requirements,
|
||||
else the type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
template<class DynamicBuffer>
|
||||
void f(DynamicBuffer& buffer)
|
||||
{
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
...
|
||||
@endcode
|
||||
|
||||
Use with `std::enable_if` (SFINAE):
|
||||
|
||||
@code
|
||||
template<class DynamicBuffer>
|
||||
typename std::enable_if<is_dynamic_buffer<DynamicBuffer>::value>::type
|
||||
f(DynamicBuffer const& buffer);
|
||||
@endcode
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
template<class T>
|
||||
struct is_dynamic_buffer : std::integral_constant<bool, ...> {};
|
||||
#else
|
||||
template<class T, class = void>
|
||||
struct is_dynamic_buffer : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_dynamic_buffer<T, detail::void_t<decltype(
|
||||
std::declval<std::size_t&>() =
|
||||
std::declval<T const&>().size(),
|
||||
std::declval<std::size_t&>() =
|
||||
std::declval<T const&>().max_size(),
|
||||
std::declval<std::size_t&>() =
|
||||
std::declval<T const&>().capacity(),
|
||||
std::declval<T&>().commit(std::declval<std::size_t>()),
|
||||
std::declval<T&>().consume(std::declval<std::size_t>()),
|
||||
(void)0)> > : std::integral_constant<bool,
|
||||
is_const_buffer_sequence<
|
||||
typename T::const_buffers_type>::value &&
|
||||
is_mutable_buffer_sequence<
|
||||
typename T::mutable_buffers_type>::value &&
|
||||
std::is_same<typename T::const_buffers_type,
|
||||
decltype(std::declval<T const&>().data())>::value &&
|
||||
std::is_same<typename T::mutable_buffers_type,
|
||||
decltype(std::declval<T&>().prepare(
|
||||
std::declval<std::size_t>()))>::value
|
||||
>
|
||||
{
|
||||
};
|
||||
|
||||
// Special case for Boost.Asio which doesn't adhere to
|
||||
// net-ts but still provides a read_size_helper so things work
|
||||
template<class Allocator>
|
||||
struct is_dynamic_buffer<
|
||||
boost::asio::basic_streambuf<Allocator>> : std::true_type
|
||||
{
|
||||
};
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Handler concepts
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Determine if `T` meets the requirements of @b CompletionHandler.
|
||||
|
||||
This trait checks whether a type meets the requirements for a completion
|
||||
handler, and is also callable with the specified signature.
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` meets the requirements,
|
||||
else the type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
struct handler
|
||||
{
|
||||
void operator()(error_code&);
|
||||
};
|
||||
|
||||
static_assert(is_completion_handler<handler, void(error_code&)>::value,
|
||||
"Not a completion handler");
|
||||
@endcode
|
||||
*/
|
||||
template<class T, class Signature>
|
||||
#if BEAST_DOXYGEN
|
||||
using is_completion_handler = std::integral_constant<bool, ...>;
|
||||
#else
|
||||
using is_completion_handler = std::integral_constant<bool,
|
||||
std::is_copy_constructible<typename std::decay<T>::type>::value &&
|
||||
detail::is_invocable<T, Signature>::value>;
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Stream concepts
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Determine if `T` has the `get_io_service` member.
|
||||
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` has the member
|
||||
function with the correct signature, else type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with tag dispatching:
|
||||
|
||||
@code
|
||||
template<class T>
|
||||
void maybe_hello(T& t, std::true_type)
|
||||
{
|
||||
t.get_io_service().post([]{ std::cout << "Hello, world!" << std::endl; });
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void maybe_hello(T&, std::false_type)
|
||||
{
|
||||
// T does not have get_io_service
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void maybe_hello(T& t)
|
||||
{
|
||||
maybe_hello(t, has_get_io_service<T>{});
|
||||
}
|
||||
@endcode
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
struct stream
|
||||
{
|
||||
boost::asio::io_service& get_io_service();
|
||||
};
|
||||
|
||||
static_assert(has_get_io_service<stream>::value,
|
||||
"Missing get_io_service member");
|
||||
@endcode
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
template<class T>
|
||||
struct has_get_io_service : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
template<class T, class = void>
|
||||
struct has_get_io_service : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct has_get_io_service<T, beast::detail::void_t<decltype(
|
||||
detail::accept_rv<boost::asio::io_service&>(
|
||||
std::declval<T&>().get_io_service()),
|
||||
(void)0)>> : std::true_type {};
|
||||
#endif
|
||||
|
||||
/** Returns `T::lowest_layer_type` if it exists, else `T`
|
||||
|
||||
This will contain a nested `type` equal to `T::lowest_layer_type`
|
||||
if it exists, else `type` will be equal to `T`.
|
||||
|
||||
@par Example
|
||||
|
||||
Declaring a wrapper:
|
||||
|
||||
@code
|
||||
template<class Stream>
|
||||
struct stream_wrapper
|
||||
{
|
||||
using next_layer_type = typename std::remove_reference<Stream>::type;
|
||||
using lowest_layer_type = typename get_lowest_layer<stream_type>::type;
|
||||
};
|
||||
@endcode
|
||||
|
||||
Defining a metafunction:
|
||||
|
||||
@code
|
||||
/// Alias for `std::true_type` if `T` wraps another stream
|
||||
template<class T>
|
||||
using is_stream_wrapper : std::integral_constant<bool,
|
||||
! std::is_same<T, typename get_lowest_layer<T>::type>::value> {};
|
||||
@endcode
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
template<class T>
|
||||
struct get_lowest_layer;
|
||||
#else
|
||||
template<class T, class = void>
|
||||
struct get_lowest_layer
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct get_lowest_layer<T, detail::void_t<
|
||||
typename T::lowest_layer_type>>
|
||||
{
|
||||
using type = typename T::lowest_layer_type;
|
||||
};
|
||||
#endif
|
||||
|
||||
/** Determine if `T` meets the requirements of @b AsyncReadStream.
|
||||
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` meets the requirements,
|
||||
else the type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
template<class AsyncReadStream>
|
||||
void f(AsyncReadStream& stream)
|
||||
{
|
||||
static_assert(is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
...
|
||||
@endcode
|
||||
|
||||
Use with `std::enable_if` (SFINAE):
|
||||
|
||||
@code
|
||||
template<class AsyncReadStream>
|
||||
typename std::enable_if<is_async_read_stream<AsyncReadStream>::value>::type
|
||||
f(AsyncReadStream& stream);
|
||||
@endcode
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
template<class T>
|
||||
struct is_async_read_stream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
template<class T, class = void>
|
||||
struct is_async_read_stream : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_async_read_stream<T, detail::void_t<decltype(
|
||||
std::declval<T>().async_read_some(
|
||||
std::declval<detail::MutableBufferSequence>(),
|
||||
std::declval<detail::ReadHandler>()),
|
||||
(void)0)>> : std::integral_constant<bool,
|
||||
has_get_io_service<T>::value
|
||||
> {};
|
||||
#endif
|
||||
|
||||
/** Determine if `T` meets the requirements of @b AsyncWriteStream.
|
||||
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` meets the requirements,
|
||||
else the type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
template<class AsyncWriteStream>
|
||||
void f(AsyncWriteStream& stream)
|
||||
{
|
||||
static_assert(is_async_write_stream<AsyncWriteStream>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
...
|
||||
@endcode
|
||||
|
||||
Use with `std::enable_if` (SFINAE):
|
||||
|
||||
@code
|
||||
template<class AsyncWriteStream>
|
||||
typename std::enable_if<is_async_write_stream<AsyncWriteStream>::value>::type
|
||||
f(AsyncWriteStream& stream);
|
||||
@endcode
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
template<class T>
|
||||
struct is_async_write_stream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
template<class T, class = void>
|
||||
struct is_async_write_stream : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_async_write_stream<T, detail::void_t<decltype(
|
||||
std::declval<T>().async_write_some(
|
||||
std::declval<detail::ConstBufferSequence>(),
|
||||
std::declval<detail::WriteHandler>()),
|
||||
(void)0)>> : std::integral_constant<bool,
|
||||
has_get_io_service<T>::value
|
||||
> {};
|
||||
#endif
|
||||
|
||||
/** Determine if `T` meets the requirements of @b SyncReadStream.
|
||||
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` meets the requirements,
|
||||
else the type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
template<class SyncReadStream>
|
||||
void f(SyncReadStream& stream)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
...
|
||||
@endcode
|
||||
|
||||
Use with `std::enable_if` (SFINAE):
|
||||
|
||||
@code
|
||||
template<class SyncReadStream>
|
||||
typename std::enable_if<is_sync_read_stream<SyncReadStream>::value>::type
|
||||
f(SyncReadStream& stream);
|
||||
@endcode
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
template<class T>
|
||||
struct is_sync_read_stream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
template<class T, class = void>
|
||||
struct is_sync_read_stream : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_sync_read_stream<T, detail::void_t<decltype(
|
||||
std::declval<std::size_t&>() = std::declval<T>().read_some(
|
||||
std::declval<detail::MutableBufferSequence>()),
|
||||
std::declval<std::size_t&>() = std::declval<T>().read_some(
|
||||
std::declval<detail::MutableBufferSequence>(),
|
||||
std::declval<boost::system::error_code&>()),
|
||||
(void)0)>> : std::integral_constant<bool,
|
||||
has_get_io_service<T>::value
|
||||
> {};
|
||||
#endif
|
||||
|
||||
/** Determine if `T` meets the requirements of @b SyncWriterStream.
|
||||
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` meets the requirements,
|
||||
else the type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
template<class SyncReadStream>
|
||||
void f(SyncReadStream& stream)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
...
|
||||
@endcode
|
||||
|
||||
Use with `std::enable_if` (SFINAE):
|
||||
|
||||
@code
|
||||
template<class SyncReadStream>
|
||||
typename std::enable_if<is_sync_read_stream<SyncReadStream>::value>::type
|
||||
f(SyncReadStream& stream);
|
||||
@endcode
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
template<class T>
|
||||
struct is_sync_write_stream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
template<class T, class = void>
|
||||
struct is_sync_write_stream : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_sync_write_stream<T, detail::void_t<decltype(
|
||||
std::declval<std::size_t&>() = std::declval<T&>().write_some(
|
||||
std::declval<detail::ConstBufferSequence>()),
|
||||
std::declval<std::size_t&>() = std::declval<T&>().write_some(
|
||||
std::declval<detail::ConstBufferSequence>(),
|
||||
std::declval<boost::system::error_code&>()),
|
||||
(void)0)>> : std::integral_constant<bool,
|
||||
has_get_io_service<T>::value
|
||||
> {};
|
||||
#endif
|
||||
|
||||
/** Determine if `T` meets the requirements of @b AsyncStream.
|
||||
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` meets the requirements,
|
||||
else the type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
template<class AsyncStream>
|
||||
void f(AsyncStream& stream)
|
||||
{
|
||||
static_assert(is_async_stream<AsyncStream>::value,
|
||||
"AsyncStream requirements not met");
|
||||
...
|
||||
@endcode
|
||||
|
||||
Use with `std::enable_if` (SFINAE):
|
||||
|
||||
@code
|
||||
template<class AsyncStream>
|
||||
typename std::enable_if<is_async_stream<AsyncStream>::value>::type
|
||||
f(AsyncStream& stream);
|
||||
@endcode
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
template<class T>
|
||||
struct is_async_stream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
template<class T>
|
||||
using is_async_stream = std::integral_constant<bool,
|
||||
is_async_read_stream<T>::value && is_async_write_stream<T>::value>;
|
||||
#endif
|
||||
|
||||
/** Determine if `T` meets the requirements of @b SyncStream.
|
||||
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` meets the requirements,
|
||||
else the type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
template<class SyncStream>
|
||||
void f(SyncStream& stream)
|
||||
{
|
||||
static_assert(is_sync_stream<SyncStream>::value,
|
||||
"SyncStream requirements not met");
|
||||
...
|
||||
@endcode
|
||||
|
||||
Use with `std::enable_if` (SFINAE):
|
||||
|
||||
@code
|
||||
template<class SyncStream>
|
||||
typename std::enable_if<is_sync_stream<SyncStream>::value>::type
|
||||
f(SyncStream& stream);
|
||||
@endcode
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
template<class T>
|
||||
struct is_sync_stream : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
template<class T>
|
||||
using is_sync_stream = std::integral_constant<bool,
|
||||
is_sync_read_stream<T>::value && is_sync_write_stream<T>::value>;
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// File concepts
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Determine if `T` meets the requirements of @b File.
|
||||
|
||||
Metafunctions are used to perform compile time checking of template
|
||||
types. This type will be `std::true_type` if `T` meets the requirements,
|
||||
else the type will be `std::false_type`.
|
||||
|
||||
@par Example
|
||||
|
||||
Use with `static_assert`:
|
||||
|
||||
@code
|
||||
template<class File>
|
||||
void f(File& file)
|
||||
{
|
||||
static_assert(is_file<File>::value,
|
||||
"File requirements not met");
|
||||
...
|
||||
@endcode
|
||||
|
||||
Use with `std::enable_if` (SFINAE):
|
||||
|
||||
@code
|
||||
template<class File>
|
||||
typename std::enable_if<is_file<File>::value>::type
|
||||
f(File& file);
|
||||
@endcode
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
template<class T>
|
||||
struct is_file : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
template<class T, class = void>
|
||||
struct is_file : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_file<T, detail::void_t<decltype(
|
||||
std::declval<bool&>() = std::declval<T const&>().is_open(),
|
||||
std::declval<T&>().close(std::declval<error_code&>()),
|
||||
std::declval<T&>().open(
|
||||
std::declval<char const*>(),
|
||||
std::declval<file_mode>(),
|
||||
std::declval<error_code&>()),
|
||||
std::declval<std::uint64_t&>() = std::declval<T&>().size(
|
||||
std::declval<error_code&>()),
|
||||
std::declval<std::uint64_t&>() = std::declval<T&>().pos(
|
||||
std::declval<error_code&>()),
|
||||
std::declval<T&>().seek(
|
||||
std::declval<std::uint64_t>(),
|
||||
std::declval<error_code&>()),
|
||||
std::declval<std::size_t&>() = std::declval<T&>().read(
|
||||
std::declval<void*>(),
|
||||
std::declval<std::size_t>(),
|
||||
std::declval<error_code&>()),
|
||||
std::declval<std::size_t&>() = std::declval<T&>().write(
|
||||
std::declval<void const*>(),
|
||||
std::declval<std::size_t>(),
|
||||
std::declval<error_code&>()),
|
||||
(void)0)>> : std::integral_constant<bool,
|
||||
std::is_default_constructible<T>::value &&
|
||||
std::is_destructible<T>::value
|
||||
> {};
|
||||
#endif
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -1,64 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_WRITE_DYNABUF_HPP
|
||||
#define BEAST_WRITE_DYNABUF_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/detail/write_dynabuf.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Write to a @b `DynamicBuffer`.
|
||||
|
||||
This function appends the serialized representation of each provided
|
||||
argument into the dynamic buffer. It is capable of converting the
|
||||
following types of arguments:
|
||||
|
||||
@li `boost::asio::const_buffer`
|
||||
|
||||
@li `boost::asio::mutable_buffer`
|
||||
|
||||
@li A type meeting the requirements of @b `ConvertibleToConstBuffer`
|
||||
|
||||
@li A type meeting the requirements of @b `ConstBufferSequence`
|
||||
|
||||
@li A type meeting the requirements of @b `MutableBufferSequence`
|
||||
|
||||
For all types not listed above, the function will invoke
|
||||
`boost::lexical_cast` on the argument in an attempt to convert to
|
||||
a string, which is then appended to the dynamic buffer.
|
||||
|
||||
When this function serializes numbers, it converts them to
|
||||
their text representation as if by a call to `std::to_string`.
|
||||
|
||||
@param dynabuf The dynamic buffer to write to.
|
||||
|
||||
@param args A list of one or more arguments to write.
|
||||
|
||||
@throws unspecified Any exceptions thrown by `boost::lexical_cast`.
|
||||
|
||||
@note This function participates in overload resolution only if
|
||||
the `dynabuf` parameter meets the requirements of @b `DynamicBuffer`.
|
||||
*/
|
||||
template<class DynamicBuffer, class... Args>
|
||||
#if GENERATING_DOCS
|
||||
void
|
||||
#else
|
||||
typename std::enable_if<is_DynamicBuffer<DynamicBuffer>::value>::type
|
||||
#endif
|
||||
write(DynamicBuffer& dynabuf, Args const&... args)
|
||||
{
|
||||
detail::write_dynabuf(dynabuf, args...);
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -10,20 +10,25 @@
|
||||
|
||||
#include <beast/config.hpp>
|
||||
|
||||
#include <beast/http/basic_fields.hpp>
|
||||
#include <beast/http/basic_parser_v1.hpp>
|
||||
#include <beast/http/chunk_encode.hpp>
|
||||
#include <beast/http/basic_parser.hpp>
|
||||
#include <beast/http/buffer_body.hpp>
|
||||
#include <beast/http/dynamic_body.hpp>
|
||||
#include <beast/http/empty_body.hpp>
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/field.hpp>
|
||||
#include <beast/http/fields.hpp>
|
||||
#include <beast/http/file_body.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/parse.hpp>
|
||||
#include <beast/http/parse_error.hpp>
|
||||
#include <beast/http/parser_v1.hpp>
|
||||
#include <beast/http/parser.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/http/reason.hpp>
|
||||
#include <beast/http/rfc7230.hpp>
|
||||
#include <beast/http/streambuf_body.hpp>
|
||||
#include <beast/http/serializer.hpp>
|
||||
#include <beast/http/span_body.hpp>
|
||||
#include <beast/http/status.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <beast/http/type_traits.hpp>
|
||||
#include <beast/http/vector_body.hpp>
|
||||
#include <beast/http/verb.hpp>
|
||||
#include <beast/http/write.hpp>
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_BASIC_DYNABUF_BODY_HPP
|
||||
#define BEAST_HTTP_BASIC_DYNABUF_BODY_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A message body represented by a @b `DynamicBuffer`
|
||||
|
||||
Meets the requirements of @b `Body`.
|
||||
*/
|
||||
template<class DynamicBuffer>
|
||||
struct basic_dynabuf_body
|
||||
{
|
||||
/// The type of the `message::body` member
|
||||
using value_type = DynamicBuffer;
|
||||
|
||||
#if GENERATING_DOCS
|
||||
private:
|
||||
#endif
|
||||
|
||||
class reader
|
||||
{
|
||||
value_type& sb_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
reader(message<isRequest,
|
||||
basic_dynabuf_body, Fields>& m) noexcept
|
||||
: sb_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code&) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
write(void const* data,
|
||||
std::size_t size, error_code&) noexcept
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
sb_.commit(buffer_copy(
|
||||
sb_.prepare(size), buffer(data, size)));
|
||||
}
|
||||
};
|
||||
|
||||
class writer
|
||||
{
|
||||
DynamicBuffer const& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<
|
||||
isRequest, basic_dynabuf_body, Fields> const& m) noexcept
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec) noexcept
|
||||
{
|
||||
beast::detail::ignore_unused(ec);
|
||||
}
|
||||
|
||||
std::uint64_t
|
||||
content_length() const noexcept
|
||||
{
|
||||
return body_.size();
|
||||
}
|
||||
|
||||
template<class WriteFunction>
|
||||
bool
|
||||
write(error_code&, WriteFunction&& wf) noexcept
|
||||
{
|
||||
wf(body_.data());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -1,307 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_BASIC_FIELDS_HPP
|
||||
#define BEAST_HTTP_BASIC_FIELDS_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/detail/empty_base_optimization.hpp>
|
||||
#include <beast/http/detail/basic_fields.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A container for storing HTTP header fields.
|
||||
|
||||
This container is designed to store the field value pairs that make
|
||||
up the fields and trailers in a HTTP message. Objects of this type
|
||||
are iterable, with each element holding the field name and field
|
||||
value.
|
||||
|
||||
Field names are stored as-is, but comparisons are case-insensitive.
|
||||
When the container is iterated, the fields are presented in the order
|
||||
of insertion. For fields with the same name, the container behaves
|
||||
as a `std::multiset`; there will be a separate value for each occurrence
|
||||
of the field name.
|
||||
|
||||
@note Meets the requirements of @b FieldSequence.
|
||||
*/
|
||||
template<class Allocator>
|
||||
class basic_fields :
|
||||
#if ! GENERATING_DOCS
|
||||
private beast::detail::empty_base_optimization<
|
||||
typename std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<
|
||||
detail::basic_fields_base::element>>,
|
||||
#endif
|
||||
public detail::basic_fields_base
|
||||
{
|
||||
using alloc_type = typename
|
||||
std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<
|
||||
detail::basic_fields_base::element>;
|
||||
|
||||
using alloc_traits =
|
||||
std::allocator_traits<alloc_type>;
|
||||
|
||||
using size_type =
|
||||
typename std::allocator_traits<Allocator>::size_type;
|
||||
|
||||
void
|
||||
delete_all();
|
||||
|
||||
void
|
||||
move_assign(basic_fields&, std::false_type);
|
||||
|
||||
void
|
||||
move_assign(basic_fields&, std::true_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_fields const&, std::false_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_fields const&, std::true_type);
|
||||
|
||||
template<class FieldSequence>
|
||||
void
|
||||
copy_from(FieldSequence const& fs)
|
||||
{
|
||||
for(auto const& e : fs)
|
||||
insert(e.first, e.second);
|
||||
}
|
||||
|
||||
public:
|
||||
/// The type of allocator used.
|
||||
using allocator_type = Allocator;
|
||||
|
||||
/** The value type of the field sequence.
|
||||
|
||||
Meets the requirements of @b Field.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
using value_type = implementation_defined;
|
||||
#endif
|
||||
|
||||
/// A const iterator to the field sequence
|
||||
#if GENERATING_DOCS
|
||||
using iterator = implementation_defined;
|
||||
#endif
|
||||
|
||||
/// A const iterator to the field sequence
|
||||
#if GENERATING_DOCS
|
||||
using const_iterator = implementation_defined;
|
||||
#endif
|
||||
|
||||
/// Default constructor.
|
||||
basic_fields() = default;
|
||||
|
||||
/// Destructor
|
||||
~basic_fields();
|
||||
|
||||
/** Construct the fields.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
explicit
|
||||
basic_fields(Allocator const& alloc);
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
The moved-from object becomes an empty field sequence.
|
||||
|
||||
@param other The object to move from.
|
||||
*/
|
||||
basic_fields(basic_fields&& other);
|
||||
|
||||
/** Move assignment.
|
||||
|
||||
The moved-from object becomes an empty field sequence.
|
||||
|
||||
@param other The object to move from.
|
||||
*/
|
||||
basic_fields& operator=(basic_fields&& other);
|
||||
|
||||
/// Copy constructor.
|
||||
basic_fields(basic_fields const&);
|
||||
|
||||
/// Copy assignment.
|
||||
basic_fields& operator=(basic_fields const&);
|
||||
|
||||
/// Copy constructor.
|
||||
template<class OtherAlloc>
|
||||
basic_fields(basic_fields<OtherAlloc> const&);
|
||||
|
||||
/// Copy assignment.
|
||||
template<class OtherAlloc>
|
||||
basic_fields& operator=(basic_fields<OtherAlloc> const&);
|
||||
|
||||
/// Construct from a field sequence.
|
||||
template<class FwdIt>
|
||||
basic_fields(FwdIt first, FwdIt last);
|
||||
|
||||
/// Returns `true` if the field sequence contains no elements.
|
||||
bool
|
||||
empty() const
|
||||
{
|
||||
return set_.empty();
|
||||
}
|
||||
|
||||
/// Returns the number of elements in the field sequence.
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
return set_.size();
|
||||
}
|
||||
|
||||
/// Returns a const iterator to the beginning of the field sequence.
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return list_.cbegin();
|
||||
}
|
||||
|
||||
/// Returns a const iterator to the end of the field sequence.
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return list_.cend();
|
||||
}
|
||||
|
||||
/// Returns a const iterator to the beginning of the field sequence.
|
||||
const_iterator
|
||||
cbegin() const
|
||||
{
|
||||
return list_.cbegin();
|
||||
}
|
||||
|
||||
/// Returns a const iterator to the end of the field sequence.
|
||||
const_iterator
|
||||
cend() const
|
||||
{
|
||||
return list_.cend();
|
||||
}
|
||||
|
||||
/// Returns `true` if the specified field exists.
|
||||
bool
|
||||
exists(boost::string_ref const& name) const
|
||||
{
|
||||
return set_.find(name, less{}) != set_.end();
|
||||
}
|
||||
|
||||
/// Returns the number of values for the specified field.
|
||||
std::size_t
|
||||
count(boost::string_ref const& name) const;
|
||||
|
||||
/** Returns an iterator to the case-insensitive matching field name.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
*/
|
||||
iterator
|
||||
find(boost::string_ref const& name) const;
|
||||
|
||||
/** Returns the value for a case-insensitive matching header, or `""`.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
*/
|
||||
boost::string_ref
|
||||
operator[](boost::string_ref const& name) const;
|
||||
|
||||
/// Clear the contents of the basic_fields.
|
||||
void
|
||||
clear() noexcept;
|
||||
|
||||
/** Remove a field.
|
||||
|
||||
If more than one field with the specified name exists, all
|
||||
matching fields will be removed.
|
||||
|
||||
@param name The name of the field(s) to remove.
|
||||
|
||||
@return The number of fields removed.
|
||||
*/
|
||||
std::size_t
|
||||
erase(boost::string_ref const& name);
|
||||
|
||||
/** Insert a field value.
|
||||
|
||||
If a field with the same name already exists, the
|
||||
existing field is untouched and a new field value pair
|
||||
is inserted into the container.
|
||||
|
||||
@param name The name of the field.
|
||||
|
||||
@param value A string holding the value of the field.
|
||||
*/
|
||||
void
|
||||
insert(boost::string_ref const& name, boost::string_ref value);
|
||||
|
||||
/** Insert a field value.
|
||||
|
||||
If a field with the same name already exists, the
|
||||
existing field is untouched and a new field value pair
|
||||
is inserted into the container.
|
||||
|
||||
@param name The name of the field
|
||||
|
||||
@param value The value of the field. The object will be
|
||||
converted to a string using `boost::lexical_cast`.
|
||||
*/
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
! std::is_constructible<boost::string_ref, T>::value>::type
|
||||
insert(boost::string_ref name, T const& value)
|
||||
{
|
||||
insert(name, boost::lexical_cast<std::string>(value));
|
||||
}
|
||||
|
||||
/** Replace a field value.
|
||||
|
||||
First removes any values with matching field names, then
|
||||
inserts the new field value.
|
||||
|
||||
@param name The name of the field.
|
||||
|
||||
@param value A string holding the value of the field.
|
||||
*/
|
||||
void
|
||||
replace(boost::string_ref const& name, boost::string_ref value);
|
||||
|
||||
/** Replace a field value.
|
||||
|
||||
First removes any values with matching field names, then
|
||||
inserts the new field value.
|
||||
|
||||
@param name The name of the field
|
||||
|
||||
@param value The value of the field. The object will be
|
||||
converted to a string using `boost::lexical_cast`.
|
||||
*/
|
||||
template<class T>
|
||||
typename std::enable_if<
|
||||
! std::is_constructible<boost::string_ref, T>::value>::type
|
||||
replace(boost::string_ref const& name, T const& value)
|
||||
{
|
||||
replace(name,
|
||||
boost::lexical_cast<std::string>(value));
|
||||
}
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/basic_fields.ipp>
|
||||
|
||||
#endif
|
||||
509
include/beast/http/basic_parser.hpp
Normal file
509
include/beast/http/basic_parser.hpp
Normal file
@@ -0,0 +1,509 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_BASIC_PARSER_HPP
|
||||
#define BEAST_HTTP_BASIC_PARSER_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/string.hpp>
|
||||
#include <beast/http/field.hpp>
|
||||
#include <beast/http/verb.hpp>
|
||||
#include <beast/http/detail/basic_parser.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A parser for decoding HTTP/1 wire format messages.
|
||||
|
||||
This parser is designed to efficiently parse messages in the
|
||||
HTTP/1 wire format. It allocates no memory when input is
|
||||
presented as a single contiguous buffer, and uses minimal
|
||||
state. It will handle chunked encoding and it understands
|
||||
the semantics of the Connection, Content-Length, and Upgrade
|
||||
fields.
|
||||
The parser is optimized for the case where the input buffer
|
||||
sequence consists of a single contiguous buffer. The
|
||||
@ref beast::flat_buffer class is provided, which guarantees
|
||||
that the input sequence of the stream buffer will be represented
|
||||
by exactly one contiguous buffer. To ensure the optimum performance
|
||||
of the parser, use @ref beast::flat_buffer with HTTP algorithms
|
||||
such as @ref beast::http::read, @ref beast::http::read_some,
|
||||
@ref beast::http::async_read, and @ref beast::http::async_read_some.
|
||||
Alternatively, the caller may use custom techniques to ensure that
|
||||
the structured portion of the HTTP message (header or chunk header)
|
||||
is contained in a linear buffer.
|
||||
|
||||
The interface uses CRTP (Curiously Recurring Template Pattern).
|
||||
To use this class directly, derive from @ref basic_parser. When
|
||||
bytes are presented, the implementation will make a series of zero
|
||||
or more calls to derived class members functions (termed "callbacks"
|
||||
in this context) matching a specific signature.
|
||||
|
||||
Every callback must be provided by the derived class, or else
|
||||
a compilation error will be generated. This exemplar shows
|
||||
the signature and description of the callbacks required in
|
||||
the derived class.
|
||||
For each callback, the function will ensure that `!ec` is `true`
|
||||
if there was no error or set to the appropriate error code if
|
||||
there was one. If an error is set, the value is propagated to
|
||||
the caller of the parser.
|
||||
|
||||
@tparam isRequest A `bool` indicating whether the parser will be
|
||||
presented with request or response message.
|
||||
|
||||
@tparam Derived The derived class type. This is part of the
|
||||
Curiously Recurring Template Pattern interface.
|
||||
|
||||
@note If the parser encounters a field value with obs-fold
|
||||
longer than 4 kilobytes in length, an error is generated.
|
||||
*/
|
||||
template<bool isRequest, class Derived>
|
||||
class basic_parser
|
||||
: private detail::basic_parser_base
|
||||
{
|
||||
template<bool OtherIsRequest, class OtherDerived>
|
||||
friend class basic_parser;
|
||||
|
||||
// limit on the size of the stack flat buffer
|
||||
static std::size_t constexpr max_stack_buffer = 8192;
|
||||
|
||||
// Message will be complete after reading header
|
||||
static unsigned constexpr flagSkipBody = 1<< 0;
|
||||
|
||||
// Consume input buffers across semantic boundaries
|
||||
static unsigned constexpr flagEager = 1<< 1;
|
||||
|
||||
// The parser has read at least one byte
|
||||
static unsigned constexpr flagGotSome = 1<< 2;
|
||||
|
||||
// Message semantics indicate a body is expected.
|
||||
// cleared if flagSkipBody set
|
||||
//
|
||||
static unsigned constexpr flagHasBody = 1<< 3;
|
||||
|
||||
static unsigned constexpr flagHTTP11 = 1<< 4;
|
||||
static unsigned constexpr flagNeedEOF = 1<< 5;
|
||||
static unsigned constexpr flagExpectCRLF = 1<< 6;
|
||||
static unsigned constexpr flagConnectionClose = 1<< 7;
|
||||
static unsigned constexpr flagConnectionUpgrade = 1<< 8;
|
||||
static unsigned constexpr flagConnectionKeepAlive = 1<< 9;
|
||||
static unsigned constexpr flagContentLength = 1<< 10;
|
||||
static unsigned constexpr flagChunked = 1<< 11;
|
||||
static unsigned constexpr flagUpgrade = 1<< 12;
|
||||
static unsigned constexpr flagFinalChunk = 1<< 13;
|
||||
|
||||
static
|
||||
std::uint64_t
|
||||
default_body_limit(std::true_type)
|
||||
{
|
||||
// limit for requests
|
||||
return 1 * 1024 * 1024; // 1MB
|
||||
}
|
||||
|
||||
static
|
||||
std::uint64_t
|
||||
default_body_limit(std::false_type)
|
||||
{
|
||||
// limit for responses
|
||||
return 8 * 1024 * 1024; // 8MB
|
||||
}
|
||||
|
||||
std::uint64_t body_limit_; // max payload body
|
||||
std::uint64_t len_; // size of chunk or body
|
||||
std::unique_ptr<char[]> buf_; // temp storage
|
||||
std::size_t buf_len_ = 0; // size of buf_
|
||||
std::size_t skip_ = 0; // resume search here
|
||||
std::uint32_t
|
||||
header_limit_ = 8192; // max header size
|
||||
unsigned short status_; // response status
|
||||
state state_ = // initial state
|
||||
state::nothing_yet;
|
||||
unsigned f_ = 0; // flags
|
||||
|
||||
public:
|
||||
/// `true` if this parser parses requests, `false` for responses.
|
||||
using is_request =
|
||||
std::integral_constant<bool, isRequest>;
|
||||
|
||||
/// Copy constructor (disallowed)
|
||||
basic_parser(basic_parser const&) = delete;
|
||||
|
||||
/// Copy assignment (disallowed)
|
||||
basic_parser& operator=(basic_parser const&) = delete;
|
||||
|
||||
/// Destructor
|
||||
~basic_parser() = default;
|
||||
|
||||
/// Default constructor
|
||||
basic_parser();
|
||||
|
||||
/** Move constructor
|
||||
|
||||
After the move, the only valid operation on the
|
||||
moved-from object is destruction.
|
||||
*/
|
||||
template<class OtherDerived>
|
||||
basic_parser(basic_parser<isRequest, OtherDerived>&&);
|
||||
|
||||
/** Returns a reference to this object as a @ref basic_parser.
|
||||
|
||||
This is used to pass a derived class where a base class is
|
||||
expected, to choose a correct function overload when the
|
||||
resolution would be ambiguous.
|
||||
*/
|
||||
basic_parser&
|
||||
base()
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Returns a constant reference to this object as a @ref basic_parser.
|
||||
|
||||
This is used to pass a derived class where a base class is
|
||||
expected, to choose a correct function overload when the
|
||||
resolution would be ambiguous.
|
||||
*/
|
||||
basic_parser const&
|
||||
base() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Returns `true` if the parser has received at least one byte of input.
|
||||
bool
|
||||
got_some() const
|
||||
{
|
||||
return state_ != state::nothing_yet;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message is complete.
|
||||
|
||||
The message is complete after the full header is prduced
|
||||
and one of the following is true:
|
||||
|
||||
@li The skip body option was set.
|
||||
|
||||
@li The semantics of the message indicate there is no body.
|
||||
|
||||
@li The semantics of the message indicate a body is expected,
|
||||
and the entire body was parsed.
|
||||
*/
|
||||
bool
|
||||
is_done() const
|
||||
{
|
||||
return state_ == state::complete;
|
||||
}
|
||||
|
||||
/** Returns `true` if a the parser has produced the full header.
|
||||
*/
|
||||
bool
|
||||
is_header_done() const
|
||||
{
|
||||
return state_ > state::fields;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message is an upgrade message.
|
||||
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
bool
|
||||
is_upgrade() const
|
||||
{
|
||||
return (f_ & flagConnectionUpgrade) != 0;
|
||||
}
|
||||
|
||||
/** Returns `true` if the last value for Transfer-Encoding is "chunked".
|
||||
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
bool
|
||||
is_chunked() const
|
||||
{
|
||||
return (f_ & flagChunked) != 0;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message has keep-alive connection semantics.
|
||||
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
bool
|
||||
is_keep_alive() const;
|
||||
|
||||
/** Returns the optional value of Content-Length if known.
|
||||
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
boost::optional<std::uint64_t>
|
||||
content_length() const;
|
||||
|
||||
/** Returns `true` if the message semantics require an end of file.
|
||||
|
||||
Depending on the contents of the header, the parser may
|
||||
require and end of file notification to know where the end
|
||||
of the body lies. If this function returns `true` it will be
|
||||
necessary to call @ref put_eof when there will never be additional
|
||||
data from the input.
|
||||
*/
|
||||
bool
|
||||
need_eof() const
|
||||
{
|
||||
return (f_ & flagNeedEOF) != 0;
|
||||
}
|
||||
|
||||
/** Set the limit on the payload body.
|
||||
|
||||
This function sets the maximum allowed size of the payload body,
|
||||
before any encodings except chunked have been removed. Depending
|
||||
on the message semantics, one of these cases will apply:
|
||||
|
||||
@li The Content-Length is specified and exceeds the limit. In
|
||||
this case the result @ref error::body_limit is returned
|
||||
immediately after the header is parsed.
|
||||
|
||||
@li The Content-Length is unspecified and the chunked encoding
|
||||
is not specified as the last encoding. In this case the end of
|
||||
message is determined by the end of file indicator on the
|
||||
associated stream or input source. If a sufficient number of
|
||||
body payload octets are presented to the parser to exceed the
|
||||
configured limit, the parse fails with the result
|
||||
@ref error::body_limit
|
||||
|
||||
@li The Transfer-Encoding specifies the chunked encoding as the
|
||||
last encoding. In this case, when the number of payload body
|
||||
octets produced by removing the chunked encoding exceeds
|
||||
the configured limit, the parse fails with the result
|
||||
@ref error::body_limit.
|
||||
|
||||
Setting the limit after any body octets have been parsed
|
||||
results in undefined behavior.
|
||||
|
||||
The default limit is 1MB for requests and 8MB for responses.
|
||||
|
||||
@param v The payload body limit to set
|
||||
*/
|
||||
void
|
||||
body_limit(std::uint64_t v)
|
||||
{
|
||||
body_limit_ = v;
|
||||
}
|
||||
|
||||
/** Set a limit on the total size of the header.
|
||||
|
||||
This function sets the maximum allowed size of the header
|
||||
including all field name, value, and delimiter characters
|
||||
and also including the CRLF sequences in the serialized
|
||||
input. If the end of the header is not found within the
|
||||
limit of the header size, the error @ref error::header_limit
|
||||
is returned by @ref put.
|
||||
|
||||
Setting the limit after any header octets have been parsed
|
||||
results in undefined behavior.
|
||||
*/
|
||||
void
|
||||
header_limit(std::uint32_t v)
|
||||
{
|
||||
header_limit_ = v;
|
||||
}
|
||||
|
||||
/// Returns `true` if the eager parse option is set.
|
||||
bool
|
||||
eager() const
|
||||
{
|
||||
return (f_ & flagEager) != 0;
|
||||
}
|
||||
|
||||
/** Set the eager parse option.
|
||||
|
||||
Normally the parser returns after successfully parsing a structured
|
||||
element (header, chunk header, or chunk body) even if there are octets
|
||||
remaining in the input. This is necessary when attempting to parse the
|
||||
header first, or when the caller wants to inspect information which may
|
||||
be invalidated by subsequent parsing, such as a chunk extension. The
|
||||
`eager` option controls whether the parser keeps going after parsing
|
||||
structured element if there are octets remaining in the buffer and no
|
||||
error occurs. This option is automatically set or cleared during certain
|
||||
stream operations to improve performance with no change in functionality.
|
||||
|
||||
The default setting is `false`.
|
||||
|
||||
@param v `true` to set the eager parse option or `false` to disable it.
|
||||
*/
|
||||
void
|
||||
eager(bool v)
|
||||
{
|
||||
if(v)
|
||||
f_ |= flagEager;
|
||||
else
|
||||
f_ &= ~flagEager;
|
||||
}
|
||||
|
||||
/// Returns `true` if the skip parse option is set.
|
||||
bool
|
||||
skip()
|
||||
{
|
||||
return (f_ & flagSkipBody) != 0;
|
||||
}
|
||||
|
||||
/** Set the skip parse option.
|
||||
|
||||
This option controls whether or not the parser expects to see an HTTP
|
||||
body, regardless of the presence or absence of certain fields such as
|
||||
Content-Length or a chunked Transfer-Encoding. Depending on the request,
|
||||
some responses do not carry a body. For example, a 200 response to a
|
||||
CONNECT request from a tunneling proxy, or a response to a HEAD request.
|
||||
In these cases, callers may use this function inform the parser that
|
||||
no body is expected. The parser will consider the message complete
|
||||
after the header has been received.
|
||||
|
||||
@param v `true` to set the skip body option or `false` to disable it.
|
||||
|
||||
@note This function must called before any bytes are processed.
|
||||
*/
|
||||
void
|
||||
skip(bool v);
|
||||
|
||||
/** Write a buffer sequence to the parser.
|
||||
|
||||
This function attempts to incrementally parse the HTTP
|
||||
message data stored in the caller provided buffers. Upon
|
||||
success, a positive return value indicates that the parser
|
||||
made forward progress, consuming that number of
|
||||
bytes.
|
||||
|
||||
In some cases there may be an insufficient number of octets
|
||||
in the input buffer in order to make forward progress. This
|
||||
is indicated by the code @ref error::need_more. When
|
||||
this happens, the caller should place additional bytes into
|
||||
the buffer sequence and call @ref put again.
|
||||
|
||||
The error code @ref error::need_more is special. When this
|
||||
error is returned, a subsequent call to @ref put may succeed
|
||||
if the buffers have been updated. Otherwise, upon error
|
||||
the parser may not be restarted.
|
||||
|
||||
@param buffers An object meeting the requirements of
|
||||
@b ConstBufferSequence that represents the next chunk of
|
||||
message data. If the length of this buffer sequence is
|
||||
one, the implementation will not allocate additional memory.
|
||||
The class @ref flat_buffer is provided as one way to
|
||||
meet this requirement
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@return The number of octets consumed in the buffer
|
||||
sequence. The caller should remove these octets even if the
|
||||
error is set.
|
||||
*/
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const& buffers, error_code& ec);
|
||||
|
||||
#if ! BEAST_DOXYGEN
|
||||
std::size_t
|
||||
put(boost::asio::const_buffers_1 const& buffer,
|
||||
error_code& ec);
|
||||
#endif
|
||||
|
||||
/** Inform the parser that the end of stream was reached.
|
||||
|
||||
In certain cases, HTTP needs to know where the end of
|
||||
the stream is. For example, sometimes servers send
|
||||
responses without Content-Length and expect the client
|
||||
to consume input (for the body) until EOF. Callbacks
|
||||
and errors will still be processed as usual.
|
||||
|
||||
This is typically called when a read from the
|
||||
underlying stream object sets the error code to
|
||||
`boost::asio::error::eof`.
|
||||
|
||||
@note Only valid after parsing a complete header.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
void
|
||||
put_eof(error_code& ec);
|
||||
|
||||
private:
|
||||
inline
|
||||
Derived&
|
||||
impl()
|
||||
{
|
||||
return *static_cast<Derived*>(this);
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put_from_stack(std::size_t size,
|
||||
ConstBufferSequence const& buffers,
|
||||
error_code& ec);
|
||||
|
||||
void
|
||||
maybe_need_more(
|
||||
char const* p, std::size_t n,
|
||||
error_code& ec);
|
||||
|
||||
void
|
||||
parse_start_line(
|
||||
char const*& p, char const* last,
|
||||
error_code& ec, std::true_type);
|
||||
|
||||
void
|
||||
parse_start_line(
|
||||
char const*& p, char const* last,
|
||||
error_code& ec, std::false_type);
|
||||
|
||||
void
|
||||
parse_fields(
|
||||
char const*& p, char const* last,
|
||||
error_code& ec);
|
||||
|
||||
void
|
||||
finish_header(
|
||||
error_code& ec, std::true_type);
|
||||
|
||||
void
|
||||
finish_header(
|
||||
error_code& ec, std::false_type);
|
||||
|
||||
void
|
||||
parse_body(char const*& p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
parse_body_to_eof(char const*& p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
parse_chunk_header(char const*& p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
parse_chunk_body(char const*& p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
do_field(field f,
|
||||
string_view value, error_code& ec);
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/basic_parser.ipp>
|
||||
|
||||
#endif
|
||||
@@ -1,856 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_BASIC_PARSER_v1_HPP
|
||||
#define BEAST_HTTP_BASIC_PARSER_v1_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/parse_error.hpp>
|
||||
#include <beast/http/rfc7230.hpp>
|
||||
#include <beast/http/detail/basic_parser_v1.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Parse flags
|
||||
|
||||
The set of parser bit flags are returned by @ref basic_parser_v1::flags.
|
||||
*/
|
||||
enum parse_flag
|
||||
{
|
||||
chunked = 1,
|
||||
connection_keep_alive = 2,
|
||||
connection_close = 4,
|
||||
connection_upgrade = 8,
|
||||
trailing = 16,
|
||||
upgrade = 32,
|
||||
skipbody = 64,
|
||||
contentlength = 128,
|
||||
paused = 256
|
||||
};
|
||||
|
||||
/** Body maximum size option.
|
||||
|
||||
Sets the maximum number of cumulative bytes allowed including
|
||||
all body octets. Octets in chunk-encoded bodies are counted
|
||||
after decoding. A value of zero indicates no limit on
|
||||
the number of body octets.
|
||||
|
||||
The default body maximum size for requests is 4MB (four
|
||||
megabytes or 4,194,304 bytes) and unlimited for responses.
|
||||
|
||||
@note Objects of this type are used with @ref basic_parser_v1::set_option.
|
||||
*/
|
||||
struct body_max_size
|
||||
{
|
||||
std::size_t value;
|
||||
|
||||
explicit
|
||||
body_max_size(std::size_t v)
|
||||
: value(v)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/** Header maximum size option.
|
||||
|
||||
Sets the maximum number of cumulative bytes allowed
|
||||
including all header octets. A value of zero indicates
|
||||
no limit on the number of header octets.
|
||||
|
||||
The default header maximum size is 16KB (16,384 bytes).
|
||||
|
||||
@note Objects of this type are used with @ref basic_parser_v1::set_option.
|
||||
*/
|
||||
struct header_max_size
|
||||
{
|
||||
std::size_t value;
|
||||
|
||||
explicit
|
||||
header_max_size(std::size_t v)
|
||||
: value(v)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/** A value indicating how the parser should treat the body.
|
||||
|
||||
This value is returned from the `on_header` callback in
|
||||
the derived class. It controls what the parser does next
|
||||
in terms of the message body.
|
||||
*/
|
||||
enum class body_what
|
||||
{
|
||||
/** The parser should expect a body, keep reading.
|
||||
*/
|
||||
normal,
|
||||
|
||||
/** Skip parsing of the body.
|
||||
|
||||
When returned by `on_header` this causes parsing to
|
||||
complete and control to return to the caller. This
|
||||
could be used when sending a response to a HEAD
|
||||
request, for example.
|
||||
*/
|
||||
skip,
|
||||
|
||||
/** The message represents an UPGRADE request.
|
||||
|
||||
When returned by `on_body_prepare` this causes parsing
|
||||
to complete and control to return to the caller.
|
||||
*/
|
||||
upgrade,
|
||||
|
||||
/** Suspend parsing before reading the body.
|
||||
|
||||
When returned by `on_body_prepare` this causes parsing
|
||||
to pause. Control is returned to the caller, and the
|
||||
parser state is preserved such that a subsequent call
|
||||
to the parser will begin reading the message body.
|
||||
|
||||
This could be used by callers to inspect the HTTP
|
||||
header before committing to read the body. For example,
|
||||
to choose the body type based on the fields. Or to
|
||||
respond to an Expect: 100-continue request.
|
||||
*/
|
||||
pause
|
||||
};
|
||||
|
||||
/// The value returned when no content length is known or applicable.
|
||||
static std::uint64_t constexpr no_content_length =
|
||||
(std::numeric_limits<std::uint64_t>::max)();
|
||||
|
||||
/** A parser for decoding HTTP/1 wire format messages.
|
||||
|
||||
This parser is designed to efficiently parse messages in the
|
||||
HTTP/1 wire format. It allocates no memory and uses minimal
|
||||
state. It will handle chunked encoding and it understands the
|
||||
semantics of the Connection and Content-Length header fields.
|
||||
|
||||
The interface uses CRTP (Curiously Recurring Template Pattern).
|
||||
To use this class, derive from basic_parser. When bytes are
|
||||
presented, the implementation will make a series of zero or
|
||||
more calls to derived class members functions (referred to as
|
||||
"callbacks" from here on) matching a specific signature.
|
||||
|
||||
Every callback must be provided by the derived class, or else
|
||||
a compilation error will be generated. This exemplar shows
|
||||
the signature and description of the callbacks required in
|
||||
the derived class.
|
||||
|
||||
@code
|
||||
template<bool isRequest>
|
||||
struct exemplar : basic_parser_v1<isRequest, exemplar>
|
||||
{
|
||||
// Called when the first valid octet of a new message is received
|
||||
//
|
||||
void on_start(error_code&);
|
||||
|
||||
// Called for each piece of the Request-Method
|
||||
//
|
||||
void on_method(boost::string_ref const&, error_code&);
|
||||
|
||||
// Called for each piece of the Request-URI
|
||||
//
|
||||
void on_uri(boost::string_ref const&, error_code&);
|
||||
|
||||
// Called for each piece of the reason-phrase
|
||||
//
|
||||
void on_reason(boost::string_ref const&, error_code&);
|
||||
|
||||
// Called after the entire Request-Line has been parsed successfully.
|
||||
//
|
||||
void on_request(error_code&);
|
||||
|
||||
// Called after the entire Response-Line has been parsed successfully.
|
||||
//
|
||||
void on_response(error_code&);
|
||||
|
||||
// Called for each piece of the current header field.
|
||||
//
|
||||
void on_field(boost::string_ref const&, error_code&);
|
||||
|
||||
// Called for each piece of the current header value.
|
||||
//
|
||||
void on_value(boost::string_ref const&, error_code&)
|
||||
|
||||
// Called when the entire header has been parsed successfully.
|
||||
//
|
||||
void
|
||||
on_header(std::uint64_t content_length, error_code&);
|
||||
|
||||
// Called after on_header, before the body is parsed
|
||||
//
|
||||
body_what
|
||||
on_body_what(std::uint64_t content_length, error_code&);
|
||||
|
||||
// Called for each piece of the body.
|
||||
//
|
||||
// If the header indicates chunk encoding, the chunk
|
||||
// encoding is removed from the buffer before being
|
||||
// passed to the callback.
|
||||
//
|
||||
void on_body(boost::string_ref const&, error_code&);
|
||||
|
||||
// Called when the entire message has been parsed successfully.
|
||||
// At this point, @ref complete returns `true`, and the parser
|
||||
// is ready to parse another message if `keep_alive` would
|
||||
// return `true`.
|
||||
//
|
||||
void on_complete(error_code&) {}
|
||||
};
|
||||
@endcode
|
||||
|
||||
The return value of `on_body_what` is special, it controls
|
||||
whether or not the parser should expect a body. See @ref body_what
|
||||
for choices of the return value.
|
||||
|
||||
If a callback sets an error, parsing stops at the current octet
|
||||
and the error is returned to the caller. Callbacks must not throw
|
||||
exceptions.
|
||||
|
||||
@tparam isRequest A `bool` indicating whether the parser will be
|
||||
presented with request or response message.
|
||||
|
||||
@tparam Derived The derived class type. This is part of the
|
||||
Curiously Recurring Template Pattern interface.
|
||||
*/
|
||||
template<bool isRequest, class Derived>
|
||||
class basic_parser_v1 : public detail::parser_base
|
||||
{
|
||||
private:
|
||||
template<bool, class>
|
||||
friend class basic_parser_v1;
|
||||
|
||||
using self = basic_parser_v1;
|
||||
typedef void(self::*pmf_t)(error_code&, boost::string_ref const&);
|
||||
|
||||
enum field_state : std::uint8_t
|
||||
{
|
||||
h_general = 0,
|
||||
h_C,
|
||||
h_CO,
|
||||
h_CON,
|
||||
|
||||
h_matching_connection,
|
||||
h_matching_proxy_connection,
|
||||
h_matching_content_length,
|
||||
h_matching_transfer_encoding,
|
||||
h_matching_upgrade,
|
||||
|
||||
h_connection,
|
||||
h_content_length0,
|
||||
h_content_length,
|
||||
h_content_length_ows,
|
||||
h_transfer_encoding,
|
||||
h_upgrade,
|
||||
|
||||
h_matching_transfer_encoding_chunked,
|
||||
h_matching_transfer_encoding_general,
|
||||
h_matching_connection_keep_alive,
|
||||
h_matching_connection_close,
|
||||
h_matching_connection_upgrade,
|
||||
|
||||
h_transfer_encoding_chunked,
|
||||
h_transfer_encoding_chunked_ows,
|
||||
|
||||
h_connection_keep_alive,
|
||||
h_connection_keep_alive_ows,
|
||||
h_connection_close,
|
||||
h_connection_close_ows,
|
||||
h_connection_upgrade,
|
||||
h_connection_upgrade_ows,
|
||||
h_connection_token,
|
||||
h_connection_token_ows
|
||||
};
|
||||
|
||||
std::size_t h_max_;
|
||||
std::size_t h_left_;
|
||||
std::size_t b_max_;
|
||||
std::size_t b_left_;
|
||||
std::uint64_t content_length_;
|
||||
pmf_t cb_;
|
||||
state s_ : 8;
|
||||
unsigned fs_ : 8;
|
||||
unsigned pos_ : 8; // position in field state
|
||||
unsigned http_major_ : 16;
|
||||
unsigned http_minor_ : 16;
|
||||
unsigned status_code_ : 16;
|
||||
unsigned flags_ : 9;
|
||||
bool upgrade_ : 1; // true if parser exited for upgrade
|
||||
|
||||
public:
|
||||
/// Default constructor
|
||||
basic_parser_v1();
|
||||
|
||||
/// Copy constructor.
|
||||
template<class OtherDerived>
|
||||
basic_parser_v1(basic_parser_v1<
|
||||
isRequest, OtherDerived> const& other);
|
||||
|
||||
/// Copy assignment.
|
||||
template<class OtherDerived>
|
||||
basic_parser_v1& operator=(basic_parser_v1<
|
||||
isRequest, OtherDerived> const& other);
|
||||
|
||||
/** Set options on the parser.
|
||||
|
||||
@param args One or more parser options to set.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
template<class... Args>
|
||||
void
|
||||
set_option(Args&&... args)
|
||||
#else
|
||||
template<class A1, class A2, class... An>
|
||||
void
|
||||
set_option(A1&& a1, A2&& a2, An&&... an)
|
||||
#endif
|
||||
{
|
||||
set_option(std::forward<A1>(a1));
|
||||
set_option(std::forward<A2>(a2),
|
||||
std::forward<An>(an)...);
|
||||
}
|
||||
|
||||
/// Set the header maximum size option
|
||||
void
|
||||
set_option(header_max_size const& o)
|
||||
{
|
||||
h_max_ = o.value;
|
||||
h_left_ = h_max_;
|
||||
}
|
||||
|
||||
/// Set the body maximum size option
|
||||
void
|
||||
set_option(body_max_size const& o)
|
||||
{
|
||||
b_max_ = o.value;
|
||||
b_left_ = b_max_;
|
||||
}
|
||||
|
||||
/// Returns internal flags associated with the parser.
|
||||
unsigned
|
||||
flags() const
|
||||
{
|
||||
return flags_;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message end is indicated by eof.
|
||||
|
||||
This function returns true if the semantics of the message require
|
||||
that the end of the message is signaled by an end of file. For
|
||||
example, if the message is a HTTP/1.0 message and the Content-Length
|
||||
is unspecified, the end of the message is indicated by an end of file.
|
||||
|
||||
@return `true` if write_eof must be used to indicate the message end.
|
||||
*/
|
||||
bool
|
||||
needs_eof() const
|
||||
{
|
||||
return needs_eof(
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
/** Returns the major HTTP version number.
|
||||
|
||||
Examples:
|
||||
* Returns 1 for HTTP/1.1
|
||||
* Returns 1 for HTTP/1.0
|
||||
|
||||
@return The HTTP major version number.
|
||||
*/
|
||||
unsigned
|
||||
http_major() const
|
||||
{
|
||||
return http_major_;
|
||||
}
|
||||
|
||||
/** Returns the minor HTTP version number.
|
||||
|
||||
Examples:
|
||||
* Returns 1 for HTTP/1.1
|
||||
* Returns 0 for HTTP/1.0
|
||||
|
||||
@return The HTTP minor version number.
|
||||
*/
|
||||
unsigned
|
||||
http_minor() const
|
||||
{
|
||||
return http_minor_;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message is an upgrade message.
|
||||
|
||||
A value of `true` indicates that the parser has successfully
|
||||
completed parsing a HTTP upgrade message.
|
||||
|
||||
@return `true` if the message is an upgrade message.
|
||||
*/
|
||||
bool
|
||||
upgrade() const
|
||||
{
|
||||
return upgrade_;
|
||||
}
|
||||
|
||||
/** Returns the numeric HTTP Status-Code of a response.
|
||||
|
||||
@return The Status-Code.
|
||||
*/
|
||||
unsigned
|
||||
status_code() const
|
||||
{
|
||||
return status_code_;
|
||||
}
|
||||
|
||||
/** Returns `true` if the connection should be kept open.
|
||||
|
||||
@note This function is only valid to call when the parser
|
||||
is complete.
|
||||
*/
|
||||
bool
|
||||
keep_alive() const;
|
||||
|
||||
/** Returns `true` if the parse has completed succesfully.
|
||||
|
||||
When the parse has completed successfully, and the semantics
|
||||
of the parsed message indicate that the connection is still
|
||||
active, a subsequent call to `write` will begin parsing a
|
||||
new message.
|
||||
|
||||
@return `true` If the parsing has completed successfully.
|
||||
*/
|
||||
bool
|
||||
complete() const
|
||||
{
|
||||
return
|
||||
s_ == s_restart ||
|
||||
s_ == s_closed_complete ||
|
||||
(flags_ & parse_flag::paused);
|
||||
}
|
||||
|
||||
/** Write a sequence of buffers to the parser.
|
||||
|
||||
@param buffers An object meeting the requirements of
|
||||
ConstBufferSequence that represents the input sequence.
|
||||
|
||||
@param ec Set to the error, if any error occurred.
|
||||
|
||||
@return The number of bytes consumed in the input sequence.
|
||||
*/
|
||||
template<class ConstBufferSequence>
|
||||
#if GENERATING_DOCS
|
||||
std::size_t
|
||||
#else
|
||||
typename std::enable_if<
|
||||
! std::is_convertible<ConstBufferSequence,
|
||||
boost::asio::const_buffer>::value,
|
||||
std::size_t>::type
|
||||
#endif
|
||||
write(ConstBufferSequence const& buffers, error_code& ec);
|
||||
|
||||
/** Write a single buffer of data to the parser.
|
||||
|
||||
@param buffer The buffer to write.
|
||||
@param ec Set to the error, if any error occurred.
|
||||
|
||||
@return The number of bytes consumed in the buffer.
|
||||
*/
|
||||
std::size_t
|
||||
write(boost::asio::const_buffer const& buffer, error_code& ec);
|
||||
|
||||
/** Called to indicate the end of file.
|
||||
|
||||
HTTP needs to know where the end of the stream is. For example,
|
||||
sometimes servers send responses without Content-Length and
|
||||
expect the client to consume input (for the body) until EOF.
|
||||
Callbacks and errors will still be processed as usual.
|
||||
|
||||
@note This is typically called when a socket read returns eof.
|
||||
*/
|
||||
void
|
||||
write_eof(error_code& ec);
|
||||
|
||||
protected:
|
||||
/** Reset the parsing state.
|
||||
|
||||
The state of the parser is reset to expect the beginning of
|
||||
a new request or response. The old state is discarded.
|
||||
*/
|
||||
void
|
||||
reset();
|
||||
|
||||
private:
|
||||
Derived&
|
||||
impl()
|
||||
{
|
||||
return *static_cast<Derived*>(this);
|
||||
}
|
||||
|
||||
void
|
||||
reset(std::true_type)
|
||||
{
|
||||
s_ = s_req_start;
|
||||
}
|
||||
|
||||
void
|
||||
reset(std::false_type)
|
||||
{
|
||||
s_ = s_res_start;
|
||||
}
|
||||
|
||||
void
|
||||
init(std::true_type)
|
||||
{
|
||||
// Request: 16KB max header, 4MB max body
|
||||
h_max_ = 16 * 1024;
|
||||
b_max_ = 4 * 1024 * 1024;
|
||||
}
|
||||
|
||||
void
|
||||
init(std::false_type)
|
||||
{
|
||||
// Response: 16KB max header, unlimited body
|
||||
h_max_ = 16 * 1024;
|
||||
b_max_ = 0;
|
||||
}
|
||||
|
||||
void
|
||||
init()
|
||||
{
|
||||
init(std::integral_constant<bool, isRequest>{});
|
||||
reset();
|
||||
}
|
||||
|
||||
bool
|
||||
needs_eof(std::true_type) const;
|
||||
|
||||
bool
|
||||
needs_eof(std::false_type) const;
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_start : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_start<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_start(
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type { };
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_method : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_method<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_method(
|
||||
std::declval<boost::string_ref>(),
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_uri : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_uri<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_uri(
|
||||
std::declval<boost::string_ref>(),
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_reason : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_reason<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_reason(
|
||||
std::declval<boost::string_ref>(),
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_request : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_request<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_request(
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_response : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_response<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_response(
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_field : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_field<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_field(
|
||||
std::declval<boost::string_ref>(),
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_value : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_value<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_value(
|
||||
std::declval<boost::string_ref>(),
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_headers : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_headers<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_header(
|
||||
std::declval<std::uint64_t>(),
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
// VFALCO Can we use std::is_detected? Is C++11 capable?
|
||||
template<class C>
|
||||
class check_on_body_what_t
|
||||
{
|
||||
template<class T, class R = std::is_convertible<decltype(
|
||||
std::declval<T>().on_body_what(
|
||||
std::declval<std::uint64_t>(),
|
||||
std::declval<error_code&>())),
|
||||
body_what>>
|
||||
static R check(int);
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
static bool const value = type::value;
|
||||
};
|
||||
template<class C>
|
||||
using check_on_body_what =
|
||||
std::integral_constant<bool, check_on_body_what_t<C>::value>;
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_body : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_body<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_body(
|
||||
std::declval<boost::string_ref>(),
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_complete : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_complete<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_complete(
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
void call_on_start(error_code& ec)
|
||||
{
|
||||
static_assert(check_on_start<Derived>::value,
|
||||
"on_start requirements not met");
|
||||
impl().on_start(ec);
|
||||
}
|
||||
|
||||
void call_on_method(error_code& ec,
|
||||
boost::string_ref const& s, std::true_type)
|
||||
{
|
||||
static_assert(check_on_method<Derived>::value,
|
||||
"on_method requirements not met");
|
||||
if(h_max_ && s.size() > h_left_)
|
||||
{
|
||||
ec = parse_error::header_too_big;
|
||||
return;
|
||||
}
|
||||
h_left_ -= s.size();
|
||||
impl().on_method(s, ec);
|
||||
}
|
||||
|
||||
void call_on_method(error_code&,
|
||||
boost::string_ref const&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_method(error_code& ec,
|
||||
boost::string_ref const& s)
|
||||
{
|
||||
call_on_method(ec, s,
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void call_on_uri(error_code& ec,
|
||||
boost::string_ref const& s, std::true_type)
|
||||
{
|
||||
static_assert(check_on_uri<Derived>::value,
|
||||
"on_uri requirements not met");
|
||||
if(h_max_ && s.size() > h_left_)
|
||||
{
|
||||
ec = parse_error::header_too_big;
|
||||
return;
|
||||
}
|
||||
h_left_ -= s.size();
|
||||
impl().on_uri(s, ec);
|
||||
}
|
||||
|
||||
void call_on_uri(error_code&,
|
||||
boost::string_ref const&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_uri(error_code& ec,
|
||||
boost::string_ref const& s)
|
||||
{
|
||||
call_on_uri(ec, s,
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void call_on_reason(error_code& ec,
|
||||
boost::string_ref const& s, std::true_type)
|
||||
{
|
||||
static_assert(check_on_reason<Derived>::value,
|
||||
"on_reason requirements not met");
|
||||
if(h_max_ && s.size() > h_left_)
|
||||
{
|
||||
ec = parse_error::header_too_big;
|
||||
return;
|
||||
}
|
||||
h_left_ -= s.size();
|
||||
impl().on_reason(s, ec);
|
||||
}
|
||||
|
||||
void call_on_reason(error_code&,
|
||||
boost::string_ref const&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_reason(error_code& ec, boost::string_ref const& s)
|
||||
{
|
||||
call_on_reason(ec, s,
|
||||
std::integral_constant<bool, ! isRequest>{});
|
||||
}
|
||||
|
||||
void call_on_request(error_code& ec, std::true_type)
|
||||
{
|
||||
static_assert(check_on_request<Derived>::value,
|
||||
"on_request requirements not met");
|
||||
impl().on_request(ec);
|
||||
}
|
||||
|
||||
void call_on_request(error_code&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_request(error_code& ec)
|
||||
{
|
||||
call_on_request(ec,
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void call_on_response(error_code& ec, std::true_type)
|
||||
{
|
||||
static_assert(check_on_response<Derived>::value,
|
||||
"on_response requirements not met");
|
||||
impl().on_response(ec);
|
||||
}
|
||||
|
||||
void call_on_response(error_code&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_response(error_code& ec)
|
||||
{
|
||||
call_on_response(ec,
|
||||
std::integral_constant<bool, ! isRequest>{});
|
||||
}
|
||||
|
||||
void call_on_field(error_code& ec,
|
||||
boost::string_ref const& s)
|
||||
{
|
||||
static_assert(check_on_field<Derived>::value,
|
||||
"on_field requirements not met");
|
||||
if(h_max_ && s.size() > h_left_)
|
||||
{
|
||||
ec = parse_error::header_too_big;
|
||||
return;
|
||||
}
|
||||
h_left_ -= s.size();
|
||||
impl().on_field(s, ec);
|
||||
}
|
||||
|
||||
void call_on_value(error_code& ec,
|
||||
boost::string_ref const& s)
|
||||
{
|
||||
static_assert(check_on_value<Derived>::value,
|
||||
"on_value requirements not met");
|
||||
if(h_max_ && s.size() > h_left_)
|
||||
{
|
||||
ec = parse_error::header_too_big;
|
||||
return;
|
||||
}
|
||||
h_left_ -= s.size();
|
||||
impl().on_value(s, ec);
|
||||
}
|
||||
|
||||
void
|
||||
call_on_headers(error_code& ec)
|
||||
{
|
||||
static_assert(check_on_headers<Derived>::value,
|
||||
"on_header requirements not met");
|
||||
impl().on_header(content_length_, ec);
|
||||
}
|
||||
|
||||
body_what
|
||||
call_on_body_what(error_code& ec)
|
||||
{
|
||||
static_assert(check_on_body_what<Derived>::value,
|
||||
"on_body_what requirements not met");
|
||||
return impl().on_body_what(content_length_, ec);
|
||||
}
|
||||
|
||||
void call_on_body(error_code& ec,
|
||||
boost::string_ref const& s)
|
||||
{
|
||||
static_assert(check_on_body<Derived>::value,
|
||||
"on_body requirements not met");
|
||||
if(b_max_ && s.size() > b_left_)
|
||||
{
|
||||
ec = parse_error::body_too_big;
|
||||
return;
|
||||
}
|
||||
b_left_ -= s.size();
|
||||
impl().on_body(s, ec);
|
||||
}
|
||||
|
||||
void call_on_complete(error_code& ec)
|
||||
{
|
||||
static_assert(check_on_complete<Derived>::value,
|
||||
"on_complete requirements not met");
|
||||
impl().on_complete(ec);
|
||||
}
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/basic_parser_v1.ipp>
|
||||
|
||||
#endif
|
||||
224
include/beast/http/buffer_body.hpp
Normal file
224
include/beast/http/buffer_body.hpp
Normal file
@@ -0,0 +1,224 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_BUFFER_BODY_HPP
|
||||
#define BEAST_HTTP_BUFFER_BODY_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/type_traits.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A @b Body using a caller provided buffer
|
||||
|
||||
Messages using this body type may be serialized and parsed.
|
||||
To use this class, the caller must initialize the members
|
||||
of @ref buffer_body::value_type to appropriate values before
|
||||
each call to read or write during a stream operation.
|
||||
*/
|
||||
struct buffer_body
|
||||
{
|
||||
/// The type of the body member when used in a message.
|
||||
struct value_type
|
||||
{
|
||||
/** A pointer to a contiguous area of memory of @ref size octets, else `nullptr`.
|
||||
|
||||
@par When Serializing
|
||||
|
||||
If this is `nullptr` and `more` is `true`, the error
|
||||
@ref error::need_buffer will be returned from @ref serializer::get
|
||||
Otherwise, the serializer will use the memory pointed to
|
||||
by `data` having `size` octets of valid storage as the
|
||||
next buffer representing the body.
|
||||
|
||||
@par When Parsing
|
||||
|
||||
If this is `nullptr`, the error @ref error::need_buffer
|
||||
will be returned from @ref parser::put. Otherwise, the
|
||||
parser will store body octets into the memory pointed to
|
||||
by `data` having `size` octets of valid storage. After
|
||||
octets are stored, the `data` and `size` members are
|
||||
adjusted: `data` is incremented to point to the next
|
||||
octet after the data written, while `size` is decremented
|
||||
to reflect the remaining space at the memory location
|
||||
pointed to by `data`.
|
||||
*/
|
||||
void* data = nullptr;
|
||||
|
||||
/** The number of octets in the buffer pointed to by @ref data.
|
||||
|
||||
@par When Serializing
|
||||
|
||||
If `data` is `nullptr` during serialization, this value
|
||||
is ignored. Otherwise, it represents the number of valid
|
||||
body octets pointed to by `data`.
|
||||
|
||||
@par When Parsing
|
||||
|
||||
The value of this field will be decremented during parsing
|
||||
to indicate the number of remaining free octets in the
|
||||
buffer pointed to by `data`. When it reaches zero, the
|
||||
parser will return @ref error::need_buffer, indicating to
|
||||
the caller that the values of `data` and `size` should be
|
||||
updated to point to a new memory buffer.
|
||||
*/
|
||||
std::size_t size = 0;
|
||||
|
||||
/** `true` if this is not the last buffer.
|
||||
|
||||
@par When Serializing
|
||||
|
||||
If this is `true` and `data` is `nullptr`, the error
|
||||
@ref error::need_buffer will be returned from @ref serializer::get
|
||||
|
||||
@par When Parsing
|
||||
|
||||
This field is not used during parsing.
|
||||
*/
|
||||
bool more = true;
|
||||
};
|
||||
|
||||
/** The algorithm for serializing the body
|
||||
|
||||
Meets the requirements of @b BodyReader.
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
using reader = implementation_defined;
|
||||
#else
|
||||
class reader
|
||||
{
|
||||
bool toggle_ = false;
|
||||
value_type const& body_;
|
||||
|
||||
public:
|
||||
using const_buffers_type =
|
||||
boost::asio::const_buffers_1;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
reader(message<isRequest,
|
||||
buffer_body, Fields> const& msg)
|
||||
: body_(msg.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
boost::optional<
|
||||
std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec)
|
||||
{
|
||||
if(toggle_)
|
||||
{
|
||||
if(body_.more)
|
||||
{
|
||||
toggle_ = false;
|
||||
ec = error::need_buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
return boost::none;
|
||||
}
|
||||
if(body_.data)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
toggle_ = true;
|
||||
return {{const_buffers_type{
|
||||
body_.data, body_.size}, body_.more}};
|
||||
}
|
||||
if(body_.more)
|
||||
ec = error::need_buffer;
|
||||
else
|
||||
ec.assign(0, ec.category());
|
||||
return boost::none;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** The algorithm for parsing the body
|
||||
|
||||
Meets the requirements of @b BodyReader.
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
using writer = implementation_defined;
|
||||
#else
|
||||
class writer
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest, buffer_body, Fields>& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<std::uint64_t> const&, error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
using boost::asio::buffer_copy;
|
||||
if(! body_.data)
|
||||
{
|
||||
ec = error::need_buffer;
|
||||
return 0;
|
||||
}
|
||||
auto const bytes_transferred =
|
||||
buffer_copy(boost::asio::buffer(
|
||||
body_.data, body_.size), buffers);
|
||||
body_.data = reinterpret_cast<char*>(
|
||||
body_.data) + bytes_transferred;
|
||||
body_.size -= bytes_transferred;
|
||||
if(bytes_transferred == buffer_size(buffers))
|
||||
ec.assign(0, ec.category());
|
||||
else
|
||||
ec = error::need_buffer;
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
#if ! BEAST_DOXYGEN
|
||||
// operator<< is not supported for buffer_body
|
||||
template<bool isRequest, class Fields>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, message<isRequest,
|
||||
buffer_body, Fields> const& msg) = delete;
|
||||
#endif
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -1,75 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_CHUNK_ENCODE_HPP
|
||||
#define BEAST_HTTP_CHUNK_ENCODE_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/buffer_cat.hpp>
|
||||
#include <beast/http/detail/chunk_encode.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Returns a chunk-encoded ConstBufferSequence.
|
||||
|
||||
This returns a buffer sequence representing the
|
||||
first chunk of a chunked transfer coded body.
|
||||
|
||||
@param fin `true` if this is the last chunk.
|
||||
|
||||
@param buffers The input buffer sequence.
|
||||
|
||||
@return A chunk-encoded ConstBufferSequence representing the input.
|
||||
|
||||
@see <a href=https://tools.ietf.org/html/rfc7230#section-4.1.3>rfc7230 section 4.1.3</a>
|
||||
*/
|
||||
template<class ConstBufferSequence>
|
||||
#if GENERATING_DOCS
|
||||
implementation_defined
|
||||
#else
|
||||
beast::detail::buffer_cat_helper<
|
||||
detail::chunk_encode_delim,
|
||||
ConstBufferSequence,
|
||||
boost::asio::const_buffers_1>
|
||||
#endif
|
||||
chunk_encode(bool fin, ConstBufferSequence const& buffers)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
return buffer_cat(
|
||||
detail::chunk_encode_delim{buffer_size(buffers)},
|
||||
buffers,
|
||||
fin ? boost::asio::const_buffers_1{"\r\n0\r\n\r\n", 7}
|
||||
: boost::asio::const_buffers_1{"\r\n", 2});
|
||||
}
|
||||
|
||||
/** Returns a chunked encoding final chunk.
|
||||
|
||||
@see <a href=https://tools.ietf.org/html/rfc7230#section-4.1.3>rfc7230 section 4.1.3</a>
|
||||
*/
|
||||
inline
|
||||
#if GENERATING_DOCS
|
||||
implementation_defined
|
||||
#else
|
||||
boost::asio::const_buffers_1
|
||||
#endif
|
||||
chunk_encode_final()
|
||||
{
|
||||
return boost::asio::const_buffers_1{"0\r\n\r\n", 5};
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -1,231 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_TYPE_CHECK_HPP
|
||||
#define BEAST_HTTP_TYPE_CHECK_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct write_function
|
||||
{
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(ConstBufferSequence const&);
|
||||
};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct has_value_type : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct has_value_type<T, beast::detail::void_t<
|
||||
typename T::value_type
|
||||
> > : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct has_content_length : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct has_content_length<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().content_length()
|
||||
)> > : std::true_type
|
||||
{
|
||||
static_assert(std::is_convertible<
|
||||
decltype(std::declval<T>().content_length()),
|
||||
std::uint64_t>::value,
|
||||
"Writer::content_length requirements not met");
|
||||
};
|
||||
|
||||
template<class T, class M>
|
||||
class is_Writer
|
||||
{
|
||||
template<class U, class R = decltype(
|
||||
std::declval<U>().init(std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check1(int);
|
||||
template<class>
|
||||
static std::false_type check1(...);
|
||||
using type1 = decltype(check1<T>(0));
|
||||
|
||||
// VFALCO This is unfortunate, we have to provide the template
|
||||
// argument type because this is not a deduced context?
|
||||
//
|
||||
template<class U, class R =
|
||||
std::is_convertible<decltype(
|
||||
std::declval<U>().template write<detail::write_function>(
|
||||
std::declval<error_code&>(),
|
||||
std::declval<detail::write_function>()))
|
||||
, bool>>
|
||||
static R check2(int);
|
||||
template<class>
|
||||
static std::false_type check2(...);
|
||||
using type2 = decltype(check2<T>(0));
|
||||
|
||||
public:
|
||||
static_assert(std::is_same<
|
||||
typename M::body_type::writer, T>::value,
|
||||
"Mismatched writer and message");
|
||||
|
||||
using type = std::integral_constant<bool,
|
||||
std::is_nothrow_constructible<T, M const&>::value
|
||||
&& type1::value
|
||||
&& type2::value
|
||||
>;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class is_Parser
|
||||
{
|
||||
template<class U, class R =
|
||||
std::is_convertible<decltype(
|
||||
std::declval<U>().complete()),
|
||||
bool>>
|
||||
static R check1(int);
|
||||
template<class>
|
||||
static std::false_type check1(...);
|
||||
using type1 = decltype(check1<T>(0));
|
||||
|
||||
template<class U, class R =
|
||||
std::is_convertible<decltype(
|
||||
std::declval<U>().write(
|
||||
std::declval<boost::asio::const_buffers_1 const&>(),
|
||||
std::declval<error_code&>())),
|
||||
std::size_t>>
|
||||
static R check2(int);
|
||||
template<class>
|
||||
static std::false_type check2(...);
|
||||
using type2 = decltype(check2<T>(0));
|
||||
|
||||
template<class U, class R = decltype(
|
||||
std::declval<U>().write_eof(std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check3(int);
|
||||
template<class>
|
||||
static std::false_type check3(...);
|
||||
using type3 = decltype(check3<T>(0));
|
||||
|
||||
public:
|
||||
using type = std::integral_constant<bool,
|
||||
type1::value &&
|
||||
type2::value &&
|
||||
type3::value
|
||||
>;
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
/// Determine if `T` meets the requirements of @b Body.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct is_Body : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
using is_Body = detail::has_value_type<T>;
|
||||
#endif
|
||||
|
||||
/** Determine if a @b Body has a nested type `reader`.
|
||||
|
||||
@tparam T The type to check, which must meet the
|
||||
requirements of @b Body.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
template<class T>
|
||||
struct has_reader : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct has_reader : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct has_reader<T, beast::detail::void_t<
|
||||
typename T::reader
|
||||
> > : std::true_type {};
|
||||
#endif
|
||||
|
||||
/** Determine if a @b Body has a nested type `writer`.
|
||||
|
||||
@tparam T The type to check, which must meet the
|
||||
requirements of @b Body.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
template<class T>
|
||||
struct has_writer : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct has_writer : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct has_writer<T, beast::detail::void_t<
|
||||
typename T::writer
|
||||
> > : std::true_type {};
|
||||
#endif
|
||||
|
||||
/** Determine if `T` meets the requirements of @b Reader for `M`.
|
||||
|
||||
@tparam T The type to test.
|
||||
|
||||
@tparam M The message type to test with, which must be of
|
||||
type `message`.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
template<class T, class M>
|
||||
struct is_Reader : std::integral_constant<bool, ...> {};
|
||||
#else
|
||||
template<class T, class M, class = beast::detail::void_t<>>
|
||||
struct is_Reader : std::false_type {};
|
||||
|
||||
template<class T, class M>
|
||||
struct is_Reader<T, M, beast::detail::void_t<decltype(
|
||||
std::declval<T>().init(
|
||||
std::declval<error_code&>()),
|
||||
std::declval<T>().write(
|
||||
std::declval<void const*>(),
|
||||
std::declval<std::size_t>(),
|
||||
std::declval<error_code&>())
|
||||
)> > : std::integral_constant<bool,
|
||||
std::is_nothrow_constructible<T, M&>::value
|
||||
>
|
||||
{
|
||||
static_assert(std::is_same<
|
||||
typename M::body_type::reader, T>::value,
|
||||
"Mismatched reader and message");
|
||||
};
|
||||
#endif
|
||||
|
||||
/** Determine if `T` meets the requirements of @b Writer for `M`.
|
||||
|
||||
@tparam T The type to test.
|
||||
|
||||
@tparam M The message type to test with, which must be of
|
||||
type `message`.
|
||||
*/
|
||||
template<class T, class M>
|
||||
#if GENERATING_DOCS
|
||||
struct is_Writer : std::integral_constant<bool, ...> {};
|
||||
#else
|
||||
using is_Writer = typename detail::is_Writer<T, M>::type;
|
||||
#endif
|
||||
|
||||
/// Determine if `T` meets the requirements of @b Parser.
|
||||
template<class T>
|
||||
#if GENERATING_DOCS
|
||||
struct is_Parser : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
using is_Parser = typename detail::is_Parser<T>::type;
|
||||
#endif
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -1,214 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_DETAIL_BASIC_FIELDS_HPP
|
||||
#define BEAST_HTTP_DETAIL_BASIC_FIELDS_HPP
|
||||
|
||||
#include <beast/core/detail/ci_char_traits.hpp>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <boost/intrusive/set.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<class Allocator>
|
||||
class basic_fields;
|
||||
|
||||
namespace detail {
|
||||
|
||||
class basic_fields_base
|
||||
{
|
||||
public:
|
||||
struct value_type
|
||||
{
|
||||
std::string first;
|
||||
std::string second;
|
||||
|
||||
value_type(boost::string_ref const& name_,
|
||||
boost::string_ref const& value_)
|
||||
: first(name_)
|
||||
, second(value_)
|
||||
{
|
||||
}
|
||||
|
||||
boost::string_ref
|
||||
name() const
|
||||
{
|
||||
return first;
|
||||
}
|
||||
|
||||
boost::string_ref
|
||||
value() const
|
||||
{
|
||||
return second;
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
template<class Allocator>
|
||||
friend class beast::http::basic_fields;
|
||||
|
||||
struct element
|
||||
: boost::intrusive::set_base_hook <
|
||||
boost::intrusive::link_mode <
|
||||
boost::intrusive::normal_link>>
|
||||
, boost::intrusive::list_base_hook <
|
||||
boost::intrusive::link_mode <
|
||||
boost::intrusive::normal_link>>
|
||||
{
|
||||
value_type data;
|
||||
|
||||
element(boost::string_ref const& name,
|
||||
boost::string_ref const& value)
|
||||
: data(name, value)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct less : private beast::detail::ci_less
|
||||
{
|
||||
template<class String>
|
||||
bool
|
||||
operator()(String const& lhs, element const& rhs) const
|
||||
{
|
||||
return ci_less::operator()(lhs, rhs.data.first);
|
||||
}
|
||||
|
||||
template<class String>
|
||||
bool
|
||||
operator()(element const& lhs, String const& rhs) const
|
||||
{
|
||||
return ci_less::operator()(lhs.data.first, rhs);
|
||||
}
|
||||
|
||||
bool
|
||||
operator()(element const& lhs, element const& rhs) const
|
||||
{
|
||||
return ci_less::operator()(
|
||||
lhs.data.first, rhs.data.first);
|
||||
}
|
||||
};
|
||||
|
||||
using list_t = boost::intrusive::make_list<element,
|
||||
boost::intrusive::constant_time_size<false>>::type;
|
||||
|
||||
using set_t = boost::intrusive::make_multiset<element,
|
||||
boost::intrusive::constant_time_size<true>,
|
||||
boost::intrusive::compare<less>>::type;
|
||||
|
||||
// data
|
||||
set_t set_;
|
||||
list_t list_;
|
||||
|
||||
basic_fields_base(set_t&& set, list_t&& list)
|
||||
: set_(std::move(set))
|
||||
, list_(std::move(list))
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
class const_iterator;
|
||||
|
||||
using iterator = const_iterator;
|
||||
|
||||
basic_fields_base() = default;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class basic_fields_base::const_iterator
|
||||
{
|
||||
using iter_type = list_t::const_iterator;
|
||||
|
||||
iter_type it_;
|
||||
|
||||
template<class Allocator>
|
||||
friend class beast::http::basic_fields;
|
||||
|
||||
friend class basic_fields_base;
|
||||
|
||||
const_iterator(iter_type it)
|
||||
: it_(it)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
using value_type =
|
||||
typename basic_fields_base::value_type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type const&;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::bidirectional_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
const_iterator(const_iterator&& other) = default;
|
||||
const_iterator(const_iterator const& other) = default;
|
||||
const_iterator& operator=(const_iterator&& other) = default;
|
||||
const_iterator& operator=(const_iterator const& other) = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return it_ == other.it_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return it_->data;
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const
|
||||
{
|
||||
return &**this;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
++it_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator--()
|
||||
{
|
||||
--it_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator--(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
194
include/beast/http/detail/basic_parsed_list.hpp
Normal file
194
include/beast/http/detail/basic_parsed_list.hpp
Normal file
@@ -0,0 +1,194 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_DETAIL_BASIC_PARSED_LIST_HPP
|
||||
#define BEAST_HTTP_DETAIL_BASIC_PARSED_LIST_HPP
|
||||
|
||||
#include <beast/core/string.hpp>
|
||||
#include <beast/core/detail/empty_base_optimization.hpp>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
/** A list parser which presents the sequence as a container.
|
||||
*/
|
||||
template<class Policy>
|
||||
class basic_parsed_list
|
||||
{
|
||||
string_view s_;
|
||||
|
||||
public:
|
||||
/// The type of policy this list uses for parsing.
|
||||
using policy_type = Policy;
|
||||
|
||||
/// The type of each element in the list.
|
||||
using value_type = typename Policy::value_type;
|
||||
|
||||
/// A constant iterator to a list element.
|
||||
#if BEAST_DOXYGEN
|
||||
using const_iterator = implementation_defined;
|
||||
#else
|
||||
class const_iterator;
|
||||
#endif
|
||||
|
||||
class const_iterator
|
||||
: private beast::detail::
|
||||
empty_base_optimization<Policy>
|
||||
{
|
||||
basic_parsed_list const* list_ = nullptr;
|
||||
char const* it_ = nullptr;
|
||||
typename Policy::value_type v_;
|
||||
bool error_ = false;
|
||||
|
||||
public:
|
||||
using value_type =
|
||||
typename Policy::value_type;
|
||||
using reference = value_type const&;
|
||||
using pointer = value_type const*;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::forward_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
|
||||
bool
|
||||
operator==(
|
||||
const_iterator const& other) const
|
||||
{
|
||||
return
|
||||
other.list_ == list_ &&
|
||||
other.it_ == it_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(
|
||||
const_iterator const& other) const
|
||||
{
|
||||
return ! (*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return v_;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
bool
|
||||
error() const
|
||||
{
|
||||
return error_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class basic_parsed_list;
|
||||
|
||||
const_iterator(
|
||||
basic_parsed_list const& list, bool at_end)
|
||||
: list_(&list)
|
||||
, it_(at_end ? nullptr :
|
||||
list.s_.begin())
|
||||
{
|
||||
if(! at_end)
|
||||
increment();
|
||||
}
|
||||
|
||||
void
|
||||
increment()
|
||||
{
|
||||
if(! this->member()(
|
||||
v_, it_, list_->s_))
|
||||
{
|
||||
it_ = nullptr;
|
||||
error_ = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Construct a list from a string
|
||||
explicit
|
||||
basic_parsed_list(string_view s)
|
||||
: s_(s)
|
||||
{
|
||||
}
|
||||
|
||||
/// Return a const iterator to the beginning of the list
|
||||
const_iterator begin() const;
|
||||
|
||||
/// Return a const iterator to the end of the list
|
||||
const_iterator end() const;
|
||||
|
||||
/// Return a const iterator to the beginning of the list
|
||||
const_iterator cbegin() const;
|
||||
|
||||
/// Return a const iterator to the end of the list
|
||||
const_iterator cend() const;
|
||||
};
|
||||
|
||||
template<class Policy>
|
||||
inline
|
||||
auto
|
||||
basic_parsed_list<Policy>::
|
||||
begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, false};
|
||||
}
|
||||
|
||||
template<class Policy>
|
||||
inline
|
||||
auto
|
||||
basic_parsed_list<Policy>::
|
||||
end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, true};
|
||||
}
|
||||
|
||||
template<class Policy>
|
||||
inline
|
||||
auto
|
||||
basic_parsed_list<Policy>::
|
||||
cbegin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, false};
|
||||
}
|
||||
|
||||
template<class Policy>
|
||||
inline
|
||||
auto
|
||||
basic_parsed_list<Policy>::
|
||||
cend() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, true};
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
|
||||
718
include/beast/http/detail/basic_parser.hpp
Normal file
718
include/beast/http/detail/basic_parser.hpp
Normal file
@@ -0,0 +1,718 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_DETAIL_BASIC_PARSER_HPP
|
||||
#define BEAST_HTTP_DETAIL_BASIC_PARSER_HPP
|
||||
|
||||
#include <beast/core/static_string.hpp>
|
||||
#include <beast/core/string.hpp>
|
||||
#include <beast/core/detail/cpu_info.hpp>
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/detail/rfc7230.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/version.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
class basic_parser_base
|
||||
{
|
||||
protected:
|
||||
// limit on the size of the obs-fold buffer
|
||||
//
|
||||
// https://stackoverflow.com/questions/686217/maximum-on-http-header-values
|
||||
//
|
||||
static std::size_t constexpr max_obs_fold = 4096;
|
||||
|
||||
enum class state
|
||||
{
|
||||
nothing_yet = 0,
|
||||
start_line,
|
||||
fields,
|
||||
body0,
|
||||
body,
|
||||
body_to_eof0,
|
||||
body_to_eof,
|
||||
chunk_header0,
|
||||
chunk_header,
|
||||
chunk_body,
|
||||
complete
|
||||
};
|
||||
|
||||
static
|
||||
bool
|
||||
is_pathchar(char c)
|
||||
{
|
||||
// VFALCO This looks the same as the one below...
|
||||
|
||||
// TEXT = <any OCTET except CTLs, and excluding LWS>
|
||||
static bool constexpr tab[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
||||
};
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
bool
|
||||
unhex(unsigned char& d, char c)
|
||||
{
|
||||
static signed char constexpr tab[256] = {
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 16
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 32
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, // 48
|
||||
-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 64
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 80
|
||||
-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 96
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 112
|
||||
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 128
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 144
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 160
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 176
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 192
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 208
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 224
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 240
|
||||
};
|
||||
d = static_cast<unsigned char>(
|
||||
tab[static_cast<unsigned char>(c)]);
|
||||
return d != static_cast<unsigned char>(-1);
|
||||
}
|
||||
|
||||
static
|
||||
bool
|
||||
is_digit(char c)
|
||||
{
|
||||
return static_cast<unsigned char>(c-'0') < 10;
|
||||
}
|
||||
|
||||
static
|
||||
bool
|
||||
is_print(char c)
|
||||
{
|
||||
return static_cast<unsigned char>(c-32) < 95;
|
||||
}
|
||||
|
||||
template<class FwdIt>
|
||||
static
|
||||
FwdIt
|
||||
trim_front(FwdIt it, FwdIt const& end)
|
||||
{
|
||||
while(it != end)
|
||||
{
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
template<class RanIt>
|
||||
static
|
||||
RanIt
|
||||
trim_back(
|
||||
RanIt it, RanIt const& first)
|
||||
{
|
||||
while(it != first)
|
||||
{
|
||||
auto const c = it[-1];
|
||||
if(c != ' ' && c != '\t')
|
||||
break;
|
||||
--it;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
static
|
||||
string_view
|
||||
make_string(char const* first, char const* last)
|
||||
{
|
||||
return {first, static_cast<
|
||||
std::size_t>(last - first)};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
std::pair<char const*, bool>
|
||||
find_fast(
|
||||
char const* buf,
|
||||
char const* buf_end,
|
||||
char const* ranges,
|
||||
size_t ranges_size)
|
||||
{
|
||||
bool found = false;
|
||||
boost::ignore_unused(buf_end, ranges, ranges_size);
|
||||
return {buf, found};
|
||||
}
|
||||
|
||||
// VFALCO Can SIMD help this?
|
||||
static
|
||||
char const*
|
||||
find_eol(
|
||||
char const* it, char const* last,
|
||||
error_code& ec)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
if(it == last)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return nullptr;
|
||||
}
|
||||
if(*it == '\r')
|
||||
{
|
||||
if(++it == last)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return nullptr;
|
||||
}
|
||||
if(*it != '\n')
|
||||
{
|
||||
ec = error::bad_line_ending;
|
||||
return nullptr;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return ++it;
|
||||
}
|
||||
// VFALCO Should we handle the legacy case
|
||||
// for lines terminated with a single '\n'?
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// VFALCO Can SIMD help this?
|
||||
static
|
||||
char const*
|
||||
find_eom(char const* p, char const* last)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
if(p + 4 > last)
|
||||
return nullptr;
|
||||
if(p[3] != '\n')
|
||||
{
|
||||
if(p[3] == '\r')
|
||||
++p;
|
||||
else
|
||||
p += 4;
|
||||
}
|
||||
else if(p[2] != '\r')
|
||||
{
|
||||
p += 4;
|
||||
}
|
||||
else if(p[1] != '\n')
|
||||
{
|
||||
p += 2;
|
||||
}
|
||||
else if(p[0] != '\r')
|
||||
{
|
||||
p += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return p + 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
char const*
|
||||
parse_token_to_eol(
|
||||
char const* p,
|
||||
char const* last,
|
||||
char const*& token_last,
|
||||
error_code& ec)
|
||||
{
|
||||
for(;; ++p)
|
||||
{
|
||||
if(p >= last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return p;
|
||||
}
|
||||
if(BOOST_UNLIKELY(! is_print(*p)))
|
||||
if((BOOST_LIKELY(static_cast<
|
||||
unsigned char>(*p) < '\040') &&
|
||||
BOOST_LIKELY(*p != '\011')) ||
|
||||
BOOST_UNLIKELY(*p == '\177'))
|
||||
goto found_control;
|
||||
}
|
||||
found_control:
|
||||
if(BOOST_LIKELY(*p == '\r'))
|
||||
{
|
||||
if(++p >= last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return last;
|
||||
}
|
||||
if(*p++ != '\n')
|
||||
{
|
||||
ec = error::bad_line_ending;
|
||||
return last;
|
||||
}
|
||||
token_last = p - 2;
|
||||
}
|
||||
#if 0
|
||||
// VFALCO This allows `\n` by itself
|
||||
// to terminate a line
|
||||
else if(*p == '\n')
|
||||
{
|
||||
token_last = p;
|
||||
++p;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
// invalid character
|
||||
return nullptr;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
template<class Iter, class Unsigned>
|
||||
static
|
||||
bool
|
||||
parse_dec(Iter it, Iter last, Unsigned& v)
|
||||
{
|
||||
if(! is_digit(*it))
|
||||
return false;
|
||||
v = *it - '0';
|
||||
for(;;)
|
||||
{
|
||||
if(! is_digit(*++it))
|
||||
break;
|
||||
auto const d = *it - '0';
|
||||
if(v > ((std::numeric_limits<
|
||||
Unsigned>::max)() - 10) / 10)
|
||||
return false;
|
||||
v = 10 * v + d;
|
||||
}
|
||||
return it == last;
|
||||
}
|
||||
|
||||
template<class Iter, class Unsigned>
|
||||
bool
|
||||
parse_hex(Iter& it, Unsigned& v)
|
||||
{
|
||||
unsigned char d;
|
||||
if(! unhex(d, *it))
|
||||
return false;
|
||||
v = d;
|
||||
for(;;)
|
||||
{
|
||||
if(! unhex(d, *++it))
|
||||
break;
|
||||
auto const v0 = v;
|
||||
v = 16 * v + d;
|
||||
if(v < v0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static
|
||||
bool
|
||||
parse_crlf(char const*& it)
|
||||
{
|
||||
if( it[0] != '\r' || it[1] != '\n')
|
||||
return false;
|
||||
it += 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
parse_method(
|
||||
char const*& it, char const* last,
|
||||
string_view& result, error_code& ec)
|
||||
{
|
||||
// parse token SP
|
||||
auto const first = it;
|
||||
for(;; ++it)
|
||||
{
|
||||
if(it + 1 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(! detail::is_tchar(*it))
|
||||
break;
|
||||
}
|
||||
if(it + 1 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(*it != ' ')
|
||||
{
|
||||
ec = error::bad_method;
|
||||
return;
|
||||
}
|
||||
if(it == first)
|
||||
{
|
||||
// cannot be empty
|
||||
ec = error::bad_method;
|
||||
return;
|
||||
}
|
||||
result = make_string(first, it++);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
parse_target(
|
||||
char const*& it, char const* last,
|
||||
string_view& result, error_code& ec)
|
||||
{
|
||||
// parse target SP
|
||||
auto const first = it;
|
||||
for(;; ++it)
|
||||
{
|
||||
if(it + 1 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(! is_pathchar(*it))
|
||||
break;
|
||||
}
|
||||
if(it + 1 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(*it != ' ')
|
||||
{
|
||||
ec = error::bad_target;
|
||||
return;
|
||||
}
|
||||
if(it == first)
|
||||
{
|
||||
// cannot be empty
|
||||
ec = error::bad_target;
|
||||
return;
|
||||
}
|
||||
result = make_string(first, it++);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
parse_version(
|
||||
char const*& it, char const* last,
|
||||
int& result, error_code& ec)
|
||||
{
|
||||
if(it + 8 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(*it++ != 'H')
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
if(*it++ != 'T')
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
if(*it++ != 'T')
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
if(*it++ != 'P')
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
if(*it++ != '/')
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
if(! is_digit(*it))
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
result = 10 * (*it++ - '0');
|
||||
if(*it++ != '.')
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
if(! is_digit(*it))
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
result += *it++ - '0';
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
parse_status(
|
||||
char const*& it, char const* last,
|
||||
unsigned short& result, error_code& ec)
|
||||
{
|
||||
// parse 3(digit) SP
|
||||
if(it + 4 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(! is_digit(*it))
|
||||
{
|
||||
ec = error::bad_status;
|
||||
return;
|
||||
}
|
||||
result = 100 * (*it++ - '0');
|
||||
if(! is_digit(*it))
|
||||
{
|
||||
ec = error::bad_status;
|
||||
return;
|
||||
}
|
||||
result += 10 * (*it++ - '0');
|
||||
if(! is_digit(*it))
|
||||
{
|
||||
ec = error::bad_status;
|
||||
return;
|
||||
}
|
||||
result += *it++ - '0';
|
||||
if(*it++ != ' ')
|
||||
{
|
||||
ec = error::bad_status;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
parse_reason(
|
||||
char const*& it, char const* last,
|
||||
string_view& result, error_code& ec)
|
||||
{
|
||||
auto const first = it;
|
||||
char const* token_last = nullptr;
|
||||
auto p = parse_token_to_eol(
|
||||
it, last, token_last, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! p)
|
||||
{
|
||||
ec = error::bad_reason;
|
||||
return;
|
||||
}
|
||||
result = make_string(first, token_last);
|
||||
it = p;
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
void
|
||||
parse_field(
|
||||
char const*& p,
|
||||
char const* last,
|
||||
string_view& name,
|
||||
string_view& value,
|
||||
static_string<N>& buf,
|
||||
error_code& ec)
|
||||
{
|
||||
/* header-field = field-name ":" OWS field-value OWS
|
||||
|
||||
field-name = token
|
||||
field-value = *( field-content / obs-fold )
|
||||
field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
||||
field-vchar = VCHAR / obs-text
|
||||
|
||||
obs-fold = CRLF 1*( SP / HTAB )
|
||||
; obsolete line folding
|
||||
; see Section 3.2.4
|
||||
|
||||
token = 1*<any CHAR except CTLs or separators>
|
||||
CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||
sep = "(" | ")" | "<" | ">" | "@"
|
||||
| "," | ";" | ":" | "\" | <">
|
||||
| "/" | "[" | "]" | "?" | "="
|
||||
| "{" | "}" | SP | HT
|
||||
*/
|
||||
static char const* is_token =
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0"
|
||||
"\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1"
|
||||
"\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
|
||||
|
||||
// name
|
||||
BOOST_ALIGNMENT(16) static const char ranges1[] =
|
||||
"\x00 " /* control chars and up to SP */
|
||||
"\"\"" /* 0x22 */
|
||||
"()" /* 0x28,0x29 */
|
||||
",," /* 0x2c */
|
||||
"//" /* 0x2f */
|
||||
":@" /* 0x3a-0x40 */
|
||||
"[]" /* 0x5b-0x5d */
|
||||
"{\377"; /* 0x7b-0xff */
|
||||
auto first = p;
|
||||
bool found;
|
||||
std::tie(p, found) = find_fast(
|
||||
p, last, ranges1, sizeof(ranges1)-1);
|
||||
if(! found && p >= last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
for(;;)
|
||||
{
|
||||
if(*p == ':')
|
||||
break;
|
||||
if(! is_token[static_cast<
|
||||
unsigned char>(*p)])
|
||||
{
|
||||
ec = error::bad_field;
|
||||
return;
|
||||
}
|
||||
++p;
|
||||
if(p >= last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(p == first)
|
||||
{
|
||||
// empty name
|
||||
ec = error::bad_field;
|
||||
return;
|
||||
}
|
||||
name = make_string(first, p);
|
||||
++p; // eat ':'
|
||||
char const* token_last;
|
||||
for(;;)
|
||||
{
|
||||
// eat leading ' ' and '\t'
|
||||
for(;;++p)
|
||||
{
|
||||
if(p + 1 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(! (*p == ' ' || *p == '\t'))
|
||||
break;
|
||||
}
|
||||
// parse to CRLF
|
||||
first = p;
|
||||
p = parse_token_to_eol(p, last, token_last, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! p)
|
||||
{
|
||||
ec = error::bad_value;
|
||||
return;
|
||||
}
|
||||
// Look 1 char past the CRLF to handle obs-fold.
|
||||
if(p + 1 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
token_last =
|
||||
trim_back(token_last, first);
|
||||
if(*p != ' ' && *p != '\t')
|
||||
{
|
||||
value = make_string(first, token_last);
|
||||
return;
|
||||
}
|
||||
++p;
|
||||
if(token_last != first)
|
||||
break;
|
||||
}
|
||||
buf.resize(0);
|
||||
buf.append(first, token_last);
|
||||
BOOST_ASSERT(! buf.empty());
|
||||
try
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
// eat leading ' ' and '\t'
|
||||
for(;;++p)
|
||||
{
|
||||
if(p + 1 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(! (*p == ' ' || *p == '\t'))
|
||||
break;
|
||||
}
|
||||
// parse to CRLF
|
||||
first = p;
|
||||
p = parse_token_to_eol(p, last, token_last, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! p)
|
||||
{
|
||||
ec = error::bad_value;
|
||||
return;
|
||||
}
|
||||
// Look 1 char past the CRLF to handle obs-fold.
|
||||
if(p + 1 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
token_last = trim_back(token_last, first);
|
||||
if(first != token_last)
|
||||
{
|
||||
buf.push_back(' ');
|
||||
buf.append(first, token_last);
|
||||
}
|
||||
if(*p != ' ' && *p != '\t')
|
||||
{
|
||||
value = {buf.data(), buf.size()};
|
||||
return;
|
||||
}
|
||||
++p;
|
||||
}
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
ec = error::header_limit;
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -1,146 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_DETAIL_BASIC_PARSER_V1_HPP
|
||||
#define BEAST_HTTP_DETAIL_BASIC_PARSER_V1_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
template<class = void>
|
||||
struct parser_str_t
|
||||
{
|
||||
static char constexpr close[6] = "close";
|
||||
static char constexpr chunked[8] = "chunked";
|
||||
static char constexpr keep_alive[11] = "keep-alive";
|
||||
|
||||
static char constexpr upgrade[8] = "upgrade";
|
||||
static char constexpr connection[11] = "connection";
|
||||
static char constexpr content_length[15] = "content-length";
|
||||
static char constexpr proxy_connection[17] = "proxy-connection";
|
||||
static char constexpr transfer_encoding[18] = "transfer-encoding";
|
||||
};
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::close[6];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::chunked[8];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::keep_alive[11];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::upgrade[8];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::connection[11];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::content_length[15];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::proxy_connection[17];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::transfer_encoding[18];
|
||||
|
||||
using parser_str = parser_str_t<>;
|
||||
|
||||
class parser_base
|
||||
{
|
||||
protected:
|
||||
enum state : std::uint8_t
|
||||
{
|
||||
s_dead = 1,
|
||||
|
||||
s_req_start,
|
||||
s_req_method0,
|
||||
s_req_method,
|
||||
s_req_url0,
|
||||
s_req_url,
|
||||
s_req_http,
|
||||
s_req_http_H,
|
||||
s_req_http_HT,
|
||||
s_req_http_HTT,
|
||||
s_req_http_HTTP,
|
||||
s_req_major,
|
||||
s_req_dot,
|
||||
s_req_minor,
|
||||
s_req_cr,
|
||||
s_req_lf,
|
||||
|
||||
s_res_start,
|
||||
s_res_H,
|
||||
s_res_HT,
|
||||
s_res_HTT,
|
||||
s_res_HTTP,
|
||||
s_res_major,
|
||||
s_res_dot,
|
||||
s_res_minor,
|
||||
s_res_space_1,
|
||||
s_res_status0,
|
||||
s_res_status1,
|
||||
s_res_status2,
|
||||
s_res_space_2,
|
||||
s_res_reason0,
|
||||
s_res_reason,
|
||||
s_res_line_lf,
|
||||
s_res_line_done,
|
||||
|
||||
s_header_name0,
|
||||
s_header_name,
|
||||
s_header_value0_lf,
|
||||
s_header_value0_almost_done,
|
||||
s_header_value0,
|
||||
s_header_value,
|
||||
s_header_value_lf,
|
||||
s_header_value_almost_done,
|
||||
s_header_value_unfold,
|
||||
|
||||
s_headers_almost_done,
|
||||
s_headers_done,
|
||||
|
||||
s_chunk_size0,
|
||||
s_chunk_size,
|
||||
s_chunk_ext_name0,
|
||||
s_chunk_ext_name,
|
||||
s_chunk_ext_val,
|
||||
s_chunk_size_lf,
|
||||
s_chunk_data0,
|
||||
s_chunk_data,
|
||||
s_chunk_data_cr,
|
||||
s_chunk_data_lf,
|
||||
|
||||
s_body_pause,
|
||||
s_body_identity0,
|
||||
s_body_identity,
|
||||
s_body_identity_eof0,
|
||||
s_body_identity_eof,
|
||||
|
||||
s_complete,
|
||||
s_restart,
|
||||
s_closed_complete
|
||||
};
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -17,20 +17,22 @@ namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
class chunk_encode_delim
|
||||
/** A buffer sequence containing a chunk-encoding header
|
||||
*/
|
||||
class chunk_header
|
||||
{
|
||||
boost::asio::const_buffer cb_;
|
||||
|
||||
// Storage for the longest hex string we might need, plus delimiters.
|
||||
std::array<char, 2 * sizeof(std::size_t) + 2> buf_;
|
||||
// Storage for the longest hex string we might need
|
||||
char buf_[2 * sizeof(std::size_t)];
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
copy(chunk_encode_delim const& other);
|
||||
copy(chunk_header const& other);
|
||||
|
||||
template<class = void>
|
||||
void
|
||||
setup(std::size_t n);
|
||||
prepare_impl(std::size_t n);
|
||||
|
||||
template<class OutIter>
|
||||
static
|
||||
@@ -55,15 +57,27 @@ public:
|
||||
|
||||
using const_iterator = value_type const*;
|
||||
|
||||
chunk_encode_delim(chunk_encode_delim const& other)
|
||||
/** Constructor (default)
|
||||
|
||||
Default-constructed chunk headers are in an
|
||||
undefined state.
|
||||
*/
|
||||
chunk_header() = default;
|
||||
|
||||
/// Copy constructor
|
||||
chunk_header(chunk_header const& other)
|
||||
{
|
||||
copy(other);
|
||||
}
|
||||
|
||||
/** Construct a chunk header
|
||||
|
||||
@param n The number of octets in this chunk.
|
||||
*/
|
||||
explicit
|
||||
chunk_encode_delim(std::size_t n)
|
||||
chunk_header(std::size_t n)
|
||||
{
|
||||
setup(n);
|
||||
prepare_impl(n);
|
||||
}
|
||||
|
||||
const_iterator
|
||||
@@ -77,31 +91,55 @@ public:
|
||||
{
|
||||
return begin() + 1;
|
||||
}
|
||||
|
||||
void
|
||||
prepare(std::size_t n)
|
||||
{
|
||||
prepare_impl(n);
|
||||
}
|
||||
};
|
||||
|
||||
template<class>
|
||||
void
|
||||
chunk_encode_delim::
|
||||
copy(chunk_encode_delim const& other)
|
||||
chunk_header::
|
||||
copy(chunk_header const& other)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
auto const n =
|
||||
boost::asio::buffer_size(other.cb_);
|
||||
buf_ = other.buf_;
|
||||
cb_ = boost::asio::const_buffer(
|
||||
&buf_[buf_.size() - n], n);
|
||||
auto const mb = boost::asio::mutable_buffers_1(
|
||||
&buf_[sizeof(buf_) - n], n);
|
||||
cb_ = *mb.begin();
|
||||
buffer_copy(mb,
|
||||
boost::asio::const_buffers_1(other.cb_));
|
||||
}
|
||||
|
||||
template<class>
|
||||
void
|
||||
chunk_encode_delim::
|
||||
setup(std::size_t n)
|
||||
chunk_header::
|
||||
prepare_impl(std::size_t n)
|
||||
{
|
||||
buf_[buf_.size() - 2] = '\r';
|
||||
buf_[buf_.size() - 1] = '\n';
|
||||
auto it = to_hex(buf_.end() - 2, n);
|
||||
auto const end = &buf_[sizeof(buf_)];
|
||||
auto it = to_hex(end, n);
|
||||
cb_ = boost::asio::const_buffer{&*it,
|
||||
static_cast<std::size_t>(
|
||||
std::distance(it, buf_.end()))};
|
||||
std::distance(it, end))};
|
||||
}
|
||||
|
||||
/// Returns a buffer sequence holding a CRLF for chunk encoding
|
||||
inline
|
||||
boost::asio::const_buffers_1
|
||||
chunk_crlf()
|
||||
{
|
||||
return {"\r\n", 2};
|
||||
}
|
||||
|
||||
/// Returns a buffer sequence holding a final chunk header
|
||||
inline
|
||||
boost::asio::const_buffers_1
|
||||
chunk_final()
|
||||
{
|
||||
return {"0\r\n", 3};
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#ifndef BEAST_HTTP_DETAIL_RFC7230_HPP
|
||||
#define BEAST_HTTP_DETAIL_RFC7230_HPP
|
||||
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <beast/core/string.hpp>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
@@ -45,8 +45,8 @@ is_alpha(char c)
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return tab[static_cast<std::uint8_t>(c)];
|
||||
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
inline
|
||||
@@ -72,8 +72,8 @@ is_text(char c)
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return tab[static_cast<std::uint8_t>(c)];
|
||||
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
inline
|
||||
@@ -104,8 +104,8 @@ is_tchar(char c)
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return tab[static_cast<std::uint8_t>(c)];
|
||||
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
inline
|
||||
@@ -133,8 +133,8 @@ is_qdchar(char c)
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return tab[static_cast<std::uint8_t>(c)];
|
||||
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
inline
|
||||
@@ -163,44 +163,8 @@ is_qpchar(char c)
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return tab[static_cast<std::uint8_t>(c)];
|
||||
}
|
||||
|
||||
// converts to lower case,
|
||||
// returns 0 if not a valid token char
|
||||
//
|
||||
inline
|
||||
char
|
||||
to_field_char(char c)
|
||||
{
|
||||
/* token = 1*<any CHAR except CTLs or separators>
|
||||
CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||
sep = "(" | ")" | "<" | ">" | "@"
|
||||
| "," | ";" | ":" | "\" | <">
|
||||
| "/" | "[" | "]" | "?" | "="
|
||||
| "{" | "}" | SP | HT
|
||||
*/
|
||||
static char constexpr tab[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, '!', 0, '#', '$', '%', '&', '\'', 0, 0, '*', '+', 0, '-', '.', 0,
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
|
||||
0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
||||
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, '^', '_',
|
||||
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
||||
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, '|', 0, '~', 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return tab[static_cast<std::uint8_t>(c)];
|
||||
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
// converts to lower case,
|
||||
@@ -229,15 +193,16 @@ to_value_char(char c)
|
||||
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 224
|
||||
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 // 240
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return static_cast<char>(tab[static_cast<std::uint8_t>(c)]);
|
||||
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
|
||||
return static_cast<char>(tab[static_cast<unsigned char>(c)]);
|
||||
}
|
||||
|
||||
// VFALCO TODO Make this return unsigned?
|
||||
inline
|
||||
std::int8_t
|
||||
unhex(char c)
|
||||
{
|
||||
static char constexpr tab[] = {
|
||||
static signed char constexpr tab[] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32
|
||||
@@ -255,23 +220,69 @@ unhex(char c)
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return tab[static_cast<std::uint8_t>(c)];
|
||||
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
template<class FwdIt>
|
||||
inline
|
||||
void
|
||||
skip_ows(FwdIt& it, FwdIt const& end)
|
||||
{
|
||||
while(it != end)
|
||||
{
|
||||
auto const c = *it;
|
||||
if(c != ' ' && c != '\t')
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
template<class RanIt>
|
||||
inline
|
||||
void
|
||||
skip_ows_rev(
|
||||
RanIt& it, RanIt const& first)
|
||||
{
|
||||
while(it != first)
|
||||
{
|
||||
auto const c = it[-1];
|
||||
if(c != ' ' && c != '\t')
|
||||
break;
|
||||
--it;
|
||||
}
|
||||
}
|
||||
|
||||
// obs-fold = CRLF 1*( SP / HTAB )
|
||||
// return `false` on parse error
|
||||
//
|
||||
template<class FwdIt>
|
||||
inline
|
||||
bool
|
||||
skip_obs_fold(
|
||||
FwdIt& it, FwdIt const& last)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
if(*it != '\r')
|
||||
return true;
|
||||
if(++it == last)
|
||||
return false;
|
||||
if(*it != '\n')
|
||||
return false;
|
||||
if(++it == last)
|
||||
return false;
|
||||
if(*it != ' ' && *it != '\t')
|
||||
return false;
|
||||
for(;;)
|
||||
{
|
||||
if(++it == last)
|
||||
return true;
|
||||
if(*it != ' ' && *it != '\t')
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class FwdIt>
|
||||
void
|
||||
skip_token(FwdIt& it, FwdIt const& last)
|
||||
@@ -281,8 +292,8 @@ skip_token(FwdIt& it, FwdIt const& last)
|
||||
}
|
||||
|
||||
inline
|
||||
boost::string_ref
|
||||
trim(boost::string_ref const& s)
|
||||
string_view
|
||||
trim(string_view s)
|
||||
{
|
||||
auto first = s.begin();
|
||||
auto last = s.end();
|
||||
@@ -302,12 +313,12 @@ trim(boost::string_ref const& s)
|
||||
|
||||
struct param_iter
|
||||
{
|
||||
using iter_type = boost::string_ref::const_iterator;
|
||||
using iter_type = string_view::const_iterator;
|
||||
|
||||
iter_type it;
|
||||
iter_type first;
|
||||
iter_type last;
|
||||
std::pair<boost::string_ref, boost::string_ref> v;
|
||||
std::pair<string_view, string_view> v;
|
||||
|
||||
bool
|
||||
empty() const
|
||||
@@ -403,6 +414,53 @@ increment()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#token = [ ( "," / token ) *( OWS "," [ OWS token ] ) ]
|
||||
*/
|
||||
struct opt_token_list_policy
|
||||
{
|
||||
using value_type = string_view;
|
||||
|
||||
bool
|
||||
operator()(value_type& v,
|
||||
char const*& it, string_view s) const
|
||||
{
|
||||
v = {};
|
||||
auto need_comma = it != s.begin();
|
||||
for(;;)
|
||||
{
|
||||
detail::skip_ows(it, s.end());
|
||||
if(it == s.end())
|
||||
{
|
||||
it = nullptr;
|
||||
return true;
|
||||
}
|
||||
auto const c = *it;
|
||||
if(detail::is_tchar(c))
|
||||
{
|
||||
if(need_comma)
|
||||
return false;
|
||||
auto const p0 = it;
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == s.end())
|
||||
break;
|
||||
if(! detail::is_tchar(*it))
|
||||
break;
|
||||
}
|
||||
v = string_view{&*p0,
|
||||
static_cast<std::size_t>(it - p0)};
|
||||
return true;
|
||||
}
|
||||
if(c != ',')
|
||||
return false;
|
||||
need_comma = false;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
188
include/beast/http/detail/type_traits.hpp
Normal file
188
include/beast/http/detail/type_traits.hpp
Normal file
@@ -0,0 +1,188 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_DETAIL_TYPE_TRAITS_HPP
|
||||
#define BEAST_HTTP_DETAIL_TYPE_TRAITS_HPP
|
||||
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <cstdint>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
struct header;
|
||||
|
||||
template<bool, class, class>
|
||||
struct message;
|
||||
|
||||
template<bool isRequest,class Body, class Fields>
|
||||
class parser;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class T>
|
||||
class is_header_impl
|
||||
{
|
||||
template<bool b, class F>
|
||||
static std::true_type check(
|
||||
header<b, F> const*);
|
||||
static std::false_type check(...);
|
||||
public:
|
||||
using type = decltype(check((T*)0));
|
||||
};
|
||||
|
||||
template<class T>
|
||||
using is_header = typename is_header_impl<T>::type;
|
||||
|
||||
template<class T>
|
||||
struct is_parser : std::false_type {};
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
struct is_parser<parser<isRequest, Body, Fields>> : std::true_type {};
|
||||
|
||||
struct fields_model
|
||||
{
|
||||
string_view method() const;
|
||||
string_view reason() const;
|
||||
string_view target() const;
|
||||
|
||||
protected:
|
||||
string_view get_method_impl() const;
|
||||
string_view get_target_impl() const;
|
||||
string_view get_reason_impl() const;
|
||||
bool get_chunked_impl() const;
|
||||
bool get_keep_alive_impl(unsigned) const;
|
||||
void set_method_impl(string_view);
|
||||
void set_target_impl(string_view);
|
||||
void set_reason_impl(string_view);
|
||||
void set_chunked_impl(bool);
|
||||
void set_content_length_impl(boost::optional<std::uint64_t>);
|
||||
void set_keep_alive_impl(unsigned, bool);
|
||||
};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct has_value_type : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct has_value_type<T, beast::detail::void_t<
|
||||
typename T::value_type
|
||||
> > : std::true_type {};
|
||||
|
||||
/** Determine if a @b Body type has a size
|
||||
|
||||
This metafunction is equivalent to `std::true_type` if
|
||||
Body contains a static member function called `size`.
|
||||
*/
|
||||
template<class T, class = void>
|
||||
struct is_body_sized : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_body_sized<T, beast::detail::void_t<
|
||||
typename T::value_type,
|
||||
decltype(
|
||||
std::declval<std::uint64_t&>() =
|
||||
T::size(std::declval<typename T::value_type const&>()),
|
||||
(void)0)>> : std::true_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_fields_helper : T
|
||||
{
|
||||
template<class U = is_fields_helper>
|
||||
static auto f1(int) -> decltype(
|
||||
std::declval<string_view&>() = std::declval<U const&>().get_method_impl(),
|
||||
std::true_type());
|
||||
static auto f1(...) -> std::false_type;
|
||||
using t1 = decltype(f1(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f2(int) -> decltype(
|
||||
std::declval<string_view&>() = std::declval<U const&>().get_target_impl(),
|
||||
std::true_type());
|
||||
static auto f2(...) -> std::false_type;
|
||||
using t2 = decltype(f2(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f3(int) -> decltype(
|
||||
std::declval<string_view&>() = std::declval<U const&>().get_reason_impl(),
|
||||
std::true_type());
|
||||
static auto f3(...) -> std::false_type;
|
||||
using t3 = decltype(f3(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f4(int) -> decltype(
|
||||
std::declval<bool&>() = std::declval<U const&>().get_chunked_impl(),
|
||||
std::true_type());
|
||||
static auto f4(...) -> std::false_type;
|
||||
using t4 = decltype(f4(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f5(int) -> decltype(
|
||||
std::declval<bool&>() = std::declval<U const&>().get_keep_alive_impl(
|
||||
std::declval<unsigned>()),
|
||||
std::true_type());
|
||||
static auto f5(...) -> std::false_type;
|
||||
using t5 = decltype(f5(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f6(int) -> decltype(
|
||||
void(std::declval<U&>().set_method_impl(std::declval<string_view>())),
|
||||
std::true_type());
|
||||
static auto f6(...) -> std::false_type;
|
||||
using t6 = decltype(f6(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f7(int) -> decltype(
|
||||
void(std::declval<U&>().set_target_impl(std::declval<string_view>())),
|
||||
std::true_type());
|
||||
static auto f7(...) -> std::false_type;
|
||||
using t7 = decltype(f7(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f8(int) -> decltype(
|
||||
void(std::declval<U&>().set_reason_impl(std::declval<string_view>())),
|
||||
std::true_type());
|
||||
static auto f8(...) -> std::false_type;
|
||||
using t8 = decltype(f8(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f9(int) -> decltype(
|
||||
void(std::declval<U&>().set_chunked_impl(std::declval<bool>())),
|
||||
std::true_type());
|
||||
static auto f9(...) -> std::false_type;
|
||||
using t9 = decltype(f9(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f10(int) -> decltype(
|
||||
void(std::declval<U&>().set_content_length_impl(
|
||||
std::declval<boost::optional<std::uint64_t>>())),
|
||||
std::true_type());
|
||||
static auto f10(...) -> std::false_type;
|
||||
using t10 = decltype(f10(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f11(int) -> decltype(
|
||||
void(std::declval<U&>().set_keep_alive_impl(
|
||||
std::declval<unsigned>(),
|
||||
std::declval<bool>())),
|
||||
std::true_type());
|
||||
static auto f11(...) -> std::false_type;
|
||||
using t11 = decltype(f11(0));
|
||||
|
||||
using type = std::integral_constant<bool,
|
||||
t1::value && t2::value && t3::value &&
|
||||
t4::value && t5::value && t6::value &&
|
||||
t7::value && t8::value && t9::value &&
|
||||
t10::value && t11::value>;
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
168
include/beast/http/dynamic_body.hpp
Normal file
168
include/beast/http/dynamic_body.hpp
Normal file
@@ -0,0 +1,168 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_DYNAMIC_BODY_HPP
|
||||
#define BEAST_HTTP_DYNAMIC_BODY_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/multi_buffer.hpp>
|
||||
#include <beast/core/type_traits.hpp>
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A @b Body using a @b DynamicBuffer
|
||||
|
||||
This body uses a @b DynamicBuffer as a memory-based container
|
||||
for holding message payloads. Messages using this body type
|
||||
may be serialized and parsed.
|
||||
*/
|
||||
template<class DynamicBuffer>
|
||||
struct basic_dynamic_body
|
||||
{
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
|
||||
/** The type of container used for the body
|
||||
|
||||
This determines the type of @ref message::body
|
||||
when this body type is used with a message container.
|
||||
*/
|
||||
using value_type = DynamicBuffer;
|
||||
|
||||
/** Returns the payload size of the body
|
||||
|
||||
When this body is used with @ref message::prepare_payload,
|
||||
the Content-Length will be set to the payload size, and
|
||||
any chunked Transfer-Encoding will be removed.
|
||||
*/
|
||||
static
|
||||
std::uint64_t
|
||||
size(value_type const& v)
|
||||
{
|
||||
return v.size();
|
||||
}
|
||||
|
||||
/** The algorithm for serializing the body
|
||||
|
||||
Meets the requirements of @b BodyReader.
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
using reader = implementation_defined;
|
||||
#else
|
||||
class reader
|
||||
{
|
||||
DynamicBuffer const& body_;
|
||||
|
||||
public:
|
||||
using const_buffers_type =
|
||||
typename DynamicBuffer::const_buffers_type;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
reader(message<isRequest,
|
||||
basic_dynamic_body, Fields> const& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return {{body_.data(), false}};
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** The algorithm for parsing the body
|
||||
|
||||
Meets the requirements of @b BodyReader.
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
using writer = implementation_defined;
|
||||
#else
|
||||
class writer
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest, basic_dynamic_body, Fields>& msg)
|
||||
: body_(msg.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<std::uint64_t> const&, error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
auto const n = buffer_size(buffers);
|
||||
if(body_.size() > body_.max_size() - n)
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
return 0;
|
||||
}
|
||||
boost::optional<typename
|
||||
DynamicBuffer::mutable_buffers_type> b;
|
||||
try
|
||||
{
|
||||
b.emplace(body_.prepare((std::min)(n,
|
||||
body_.max_size() - body_.size())));
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
return 0;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
auto const bytes_transferred =
|
||||
buffer_copy(*b, buffers);
|
||||
body_.commit(bytes_transferred);
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
/** A dynamic message body represented by a @ref multi_buffer
|
||||
|
||||
Meets the requirements of @b Body.
|
||||
*/
|
||||
using dynamic_body = basic_dynamic_body<multi_buffer>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -9,62 +9,117 @@
|
||||
#define BEAST_HTTP_EMPTY_BODY_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** An empty content-body.
|
||||
/** An empty @b Body
|
||||
|
||||
Meets the requirements of @b `Body`.
|
||||
This body is used to represent messages which do not have a
|
||||
message body. If this body is used with a parser, and the
|
||||
parser encounters octets corresponding to a message body,
|
||||
the parser will fail with the error @ref http::unexpected_body.
|
||||
|
||||
The Content-Length of this body is always 0.
|
||||
*/
|
||||
struct empty_body
|
||||
{
|
||||
#if GENERATING_DOCS
|
||||
/// The type of the `message::body` member
|
||||
using value_type = void;
|
||||
/** The type of container used for the body
|
||||
|
||||
This determines the type of @ref message::body
|
||||
when this body type is used with a message container.
|
||||
*/
|
||||
struct value_type
|
||||
{
|
||||
};
|
||||
|
||||
/** Returns the payload size of the body
|
||||
|
||||
When this body is used with @ref message::prepare_payload,
|
||||
the Content-Length will be set to the payload size, and
|
||||
any chunked Transfer-Encoding will be removed.
|
||||
*/
|
||||
static
|
||||
std::uint64_t
|
||||
size(value_type)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** The algorithm for serializing the body
|
||||
|
||||
Meets the requirements of @b BodyReader.
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
using reader = implementation_defined;
|
||||
#else
|
||||
struct value_type {};
|
||||
struct reader
|
||||
{
|
||||
using const_buffers_type =
|
||||
boost::asio::null_buffers;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
reader(message<isRequest,
|
||||
empty_body, Fields> const&)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return boost::none;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if GENERATING_DOCS
|
||||
private:
|
||||
#endif
|
||||
/** The algorithm for parsing the body
|
||||
|
||||
Meets the requirements of @b BodyReader.
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
using writer = implementation_defined;
|
||||
#else
|
||||
struct writer
|
||||
{
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest, empty_body, Fields> const& m) noexcept
|
||||
writer(message<isRequest, empty_body, Fields>&)
|
||||
{
|
||||
beast::detail::ignore_unused(m);
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec) noexcept
|
||||
init(boost::optional<std::uint64_t> const&, error_code& ec)
|
||||
{
|
||||
beast::detail::ignore_unused(ec);
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
std::uint64_t
|
||||
content_length() const noexcept
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const&,
|
||||
error_code& ec)
|
||||
{
|
||||
ec = error::unexpected_body;
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<class WriteFunction>
|
||||
bool
|
||||
write(error_code&, WriteFunction&& wf) noexcept
|
||||
void
|
||||
finish(error_code& ec)
|
||||
{
|
||||
wf(boost::asio::null_buffers{});
|
||||
return true;
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // http
|
||||
|
||||
150
include/beast/http/error.hpp
Normal file
150
include/beast/http/error.hpp
Normal file
@@ -0,0 +1,150 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_ERROR_HPP
|
||||
#define BEAST_HTTP_ERROR_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/// Error codes returned from HTTP algorithms and operations.
|
||||
enum class error
|
||||
{
|
||||
/** The end of the stream was reached.
|
||||
|
||||
This error is returned under the following conditions:
|
||||
|
||||
@li When attempting to read HTTP data from a stream and the stream
|
||||
read returns the error `boost::asio::error::eof` before any new octets
|
||||
have been received.
|
||||
|
||||
@li When sending a complete HTTP message at once and the semantics of
|
||||
the message are that the connection should be closed to indicate the
|
||||
end of the message.
|
||||
*/
|
||||
end_of_stream = 1,
|
||||
|
||||
/** The incoming message is incomplete.
|
||||
|
||||
This happens when the end of stream is reached during
|
||||
parsing and some octets have been received, but not the
|
||||
entire message.
|
||||
*/
|
||||
partial_message,
|
||||
|
||||
/** Additional buffers are required.
|
||||
|
||||
This error is returned during parsing when additional
|
||||
octets are needed. The caller should append more data
|
||||
to the existing buffer and retry the parse operaetion.
|
||||
*/
|
||||
need_more,
|
||||
|
||||
/** An unexpected body was encountered during parsing.
|
||||
|
||||
This error is returned when attempting to parse body
|
||||
octets into a message container which has the
|
||||
@ref empty_body body type.
|
||||
|
||||
@see @ref empty_body
|
||||
*/
|
||||
unexpected_body,
|
||||
|
||||
/** Additional buffers are required.
|
||||
|
||||
This error is returned under the following conditions:
|
||||
|
||||
@li During serialization when using @ref buffer_body.
|
||||
The caller should update the body to point to a new
|
||||
buffer or indicate that there are no more octets in
|
||||
the body.
|
||||
|
||||
@li During parsing when using @ref buffer_body.
|
||||
The caller should update the body to point to a new
|
||||
storage area to receive additional body octets.
|
||||
*/
|
||||
need_buffer,
|
||||
|
||||
/** Buffer maximum exceeded.
|
||||
|
||||
This error is returned when reading HTTP content
|
||||
into a dynamic buffer, and the operation would
|
||||
exceed the maximum size of the buffer.
|
||||
*/
|
||||
buffer_overflow,
|
||||
|
||||
/** Header limit exceeded.
|
||||
|
||||
The parser detected an incoming message header which
|
||||
exceeded a configured limit.
|
||||
*/
|
||||
header_limit,
|
||||
|
||||
/** Body limit exceeded.
|
||||
|
||||
The parser detected an incoming message body which
|
||||
exceeded a configured limit.
|
||||
*/
|
||||
body_limit,
|
||||
|
||||
/** A memory allocation failed.
|
||||
|
||||
When basic_fields throws std::bad_alloc, it is
|
||||
converted into this error by @ref parser.
|
||||
*/
|
||||
bad_alloc,
|
||||
|
||||
//
|
||||
// (parser errors)
|
||||
//
|
||||
|
||||
/// The line ending was malformed
|
||||
bad_line_ending,
|
||||
|
||||
/// The method is invalid.
|
||||
bad_method,
|
||||
|
||||
/// The request-target is invalid.
|
||||
bad_target,
|
||||
|
||||
/// The HTTP-version is invalid.
|
||||
bad_version,
|
||||
|
||||
/// The status-code is invalid.
|
||||
bad_status,
|
||||
|
||||
/// The reason-phrase is invalid.
|
||||
bad_reason,
|
||||
|
||||
/// The field name is invalid.
|
||||
bad_field,
|
||||
|
||||
/// The field value is invalid.
|
||||
bad_value,
|
||||
|
||||
/// The Content-Length is invalid.
|
||||
bad_content_length,
|
||||
|
||||
/// The Transfer-Encoding is invalid.
|
||||
bad_transfer_encoding,
|
||||
|
||||
/// The chunk syntax is invalid.
|
||||
bad_chunk,
|
||||
|
||||
/// An obs-fold exceeded an internal limit.
|
||||
bad_obs_fold
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/error.ipp>
|
||||
|
||||
#endif
|
||||
405
include/beast/http/field.hpp
Normal file
405
include/beast/http/field.hpp
Normal file
@@ -0,0 +1,405 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_FIELD_HPP
|
||||
#define BEAST_HTTP_FIELD_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/string.hpp>
|
||||
#include <iosfwd>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
enum class field : unsigned short
|
||||
{
|
||||
unknown = 0,
|
||||
|
||||
a_im,
|
||||
accept,
|
||||
accept_additions,
|
||||
accept_charset,
|
||||
accept_datetime,
|
||||
accept_encoding,
|
||||
accept_features,
|
||||
accept_language,
|
||||
accept_patch,
|
||||
accept_post,
|
||||
accept_ranges,
|
||||
access_control,
|
||||
access_control_allow_credentials,
|
||||
access_control_allow_headers,
|
||||
access_control_allow_methods,
|
||||
access_control_allow_origin,
|
||||
access_control_max_age,
|
||||
access_control_request_headers,
|
||||
access_control_request_method,
|
||||
age,
|
||||
allow,
|
||||
alpn,
|
||||
also_control,
|
||||
alt_svc,
|
||||
alt_used,
|
||||
alternate_recipient,
|
||||
alternates,
|
||||
apparently_to,
|
||||
apply_to_redirect_ref,
|
||||
approved,
|
||||
archive,
|
||||
archived_at,
|
||||
article_names,
|
||||
article_updates,
|
||||
authentication_control,
|
||||
authentication_info,
|
||||
authentication_results,
|
||||
authorization,
|
||||
auto_submitted,
|
||||
autoforwarded,
|
||||
autosubmitted,
|
||||
base,
|
||||
bcc,
|
||||
body,
|
||||
c_ext,
|
||||
c_man,
|
||||
c_opt,
|
||||
c_pep,
|
||||
c_pep_info,
|
||||
cache_control,
|
||||
caldav_timezones,
|
||||
cancel_key,
|
||||
cancel_lock,
|
||||
cc,
|
||||
close,
|
||||
comments,
|
||||
compliance,
|
||||
connection,
|
||||
content_alternative,
|
||||
content_base,
|
||||
content_description,
|
||||
content_disposition,
|
||||
content_duration,
|
||||
content_encoding,
|
||||
content_features,
|
||||
content_id,
|
||||
content_identifier,
|
||||
content_language,
|
||||
content_length,
|
||||
content_location,
|
||||
content_md5,
|
||||
content_range,
|
||||
content_return,
|
||||
content_script_type,
|
||||
content_style_type,
|
||||
content_transfer_encoding,
|
||||
content_type,
|
||||
content_version,
|
||||
control,
|
||||
conversion,
|
||||
conversion_with_loss,
|
||||
cookie,
|
||||
cookie2,
|
||||
cost,
|
||||
dasl,
|
||||
date,
|
||||
date_received,
|
||||
dav,
|
||||
default_style,
|
||||
deferred_delivery,
|
||||
delivery_date,
|
||||
delta_base,
|
||||
depth,
|
||||
derived_from,
|
||||
destination,
|
||||
differential_id,
|
||||
digest,
|
||||
discarded_x400_ipms_extensions,
|
||||
discarded_x400_mts_extensions,
|
||||
disclose_recipients,
|
||||
disposition_notification_options,
|
||||
disposition_notification_to,
|
||||
distribution,
|
||||
dkim_signature,
|
||||
dl_expansion_history,
|
||||
downgraded_bcc,
|
||||
downgraded_cc,
|
||||
downgraded_disposition_notification_to,
|
||||
downgraded_final_recipient,
|
||||
downgraded_from,
|
||||
downgraded_in_reply_to,
|
||||
downgraded_mail_from,
|
||||
downgraded_message_id,
|
||||
downgraded_original_recipient,
|
||||
downgraded_rcpt_to,
|
||||
downgraded_references,
|
||||
downgraded_reply_to,
|
||||
downgraded_resent_bcc,
|
||||
downgraded_resent_cc,
|
||||
downgraded_resent_from,
|
||||
downgraded_resent_reply_to,
|
||||
downgraded_resent_sender,
|
||||
downgraded_resent_to,
|
||||
downgraded_return_path,
|
||||
downgraded_sender,
|
||||
downgraded_to,
|
||||
ediint_features,
|
||||
eesst_version,
|
||||
encoding,
|
||||
encrypted,
|
||||
errors_to,
|
||||
etag,
|
||||
expect,
|
||||
expires,
|
||||
expiry_date,
|
||||
ext,
|
||||
followup_to,
|
||||
forwarded,
|
||||
from,
|
||||
generate_delivery_report,
|
||||
getprofile,
|
||||
hobareg,
|
||||
host,
|
||||
http2_settings,
|
||||
if_,
|
||||
if_match,
|
||||
if_modified_since,
|
||||
if_none_match,
|
||||
if_range,
|
||||
if_schedule_tag_match,
|
||||
if_unmodified_since,
|
||||
im,
|
||||
importance,
|
||||
in_reply_to,
|
||||
incomplete_copy,
|
||||
injection_date,
|
||||
injection_info,
|
||||
jabber_id,
|
||||
keep_alive,
|
||||
keywords,
|
||||
label,
|
||||
language,
|
||||
last_modified,
|
||||
latest_delivery_time,
|
||||
lines,
|
||||
link,
|
||||
list_archive,
|
||||
list_help,
|
||||
list_id,
|
||||
list_owner,
|
||||
list_post,
|
||||
list_subscribe,
|
||||
list_unsubscribe,
|
||||
list_unsubscribe_post,
|
||||
location,
|
||||
lock_token,
|
||||
man,
|
||||
max_forwards,
|
||||
memento_datetime,
|
||||
message_context,
|
||||
message_id,
|
||||
message_type,
|
||||
meter,
|
||||
method_check,
|
||||
method_check_expires,
|
||||
mime_version,
|
||||
mmhs_acp127_message_identifier,
|
||||
mmhs_authorizing_users,
|
||||
mmhs_codress_message_indicator,
|
||||
mmhs_copy_precedence,
|
||||
mmhs_exempted_address,
|
||||
mmhs_extended_authorisation_info,
|
||||
mmhs_handling_instructions,
|
||||
mmhs_message_instructions,
|
||||
mmhs_message_type,
|
||||
mmhs_originator_plad,
|
||||
mmhs_originator_reference,
|
||||
mmhs_other_recipients_indicator_cc,
|
||||
mmhs_other_recipients_indicator_to,
|
||||
mmhs_primary_precedence,
|
||||
mmhs_subject_indicator_codes,
|
||||
mt_priority,
|
||||
negotiate,
|
||||
newsgroups,
|
||||
nntp_posting_date,
|
||||
nntp_posting_host,
|
||||
non_compliance,
|
||||
obsoletes,
|
||||
opt,
|
||||
optional,
|
||||
optional_www_authenticate,
|
||||
ordering_type,
|
||||
organization,
|
||||
origin,
|
||||
original_encoded_information_types,
|
||||
original_from,
|
||||
original_message_id,
|
||||
original_recipient,
|
||||
original_sender,
|
||||
original_subject,
|
||||
originator_return_address,
|
||||
overwrite,
|
||||
p3p,
|
||||
path,
|
||||
pep,
|
||||
pep_info,
|
||||
pics_label,
|
||||
position,
|
||||
posting_version,
|
||||
pragma,
|
||||
prefer,
|
||||
preference_applied,
|
||||
prevent_nondelivery_report,
|
||||
priority,
|
||||
privicon,
|
||||
profileobject,
|
||||
protocol,
|
||||
protocol_info,
|
||||
protocol_query,
|
||||
protocol_request,
|
||||
proxy_authenticate,
|
||||
proxy_authentication_info,
|
||||
proxy_authorization,
|
||||
proxy_connection,
|
||||
proxy_features,
|
||||
proxy_instruction,
|
||||
public_,
|
||||
public_key_pins,
|
||||
public_key_pins_report_only,
|
||||
range,
|
||||
received,
|
||||
received_spf,
|
||||
redirect_ref,
|
||||
references,
|
||||
referer,
|
||||
referer_root,
|
||||
relay_version,
|
||||
reply_by,
|
||||
reply_to,
|
||||
require_recipient_valid_since,
|
||||
resent_bcc,
|
||||
resent_cc,
|
||||
resent_date,
|
||||
resent_from,
|
||||
resent_message_id,
|
||||
resent_reply_to,
|
||||
resent_sender,
|
||||
resent_to,
|
||||
resolution_hint,
|
||||
resolver_location,
|
||||
retry_after,
|
||||
return_path,
|
||||
safe,
|
||||
schedule_reply,
|
||||
schedule_tag,
|
||||
sec_websocket_accept,
|
||||
sec_websocket_extensions,
|
||||
sec_websocket_key,
|
||||
sec_websocket_protocol,
|
||||
sec_websocket_version,
|
||||
security_scheme,
|
||||
see_also,
|
||||
sender,
|
||||
sensitivity,
|
||||
server,
|
||||
set_cookie,
|
||||
set_cookie2,
|
||||
setprofile,
|
||||
sio_label,
|
||||
sio_label_history,
|
||||
slug,
|
||||
soapaction,
|
||||
solicitation,
|
||||
status_uri,
|
||||
strict_transport_security,
|
||||
subject,
|
||||
subok,
|
||||
subst,
|
||||
summary,
|
||||
supersedes,
|
||||
surrogate_capability,
|
||||
surrogate_control,
|
||||
tcn,
|
||||
te,
|
||||
timeout,
|
||||
title,
|
||||
to,
|
||||
topic,
|
||||
trailer,
|
||||
transfer_encoding,
|
||||
ttl,
|
||||
ua_color,
|
||||
ua_media,
|
||||
ua_pixels,
|
||||
ua_resolution,
|
||||
ua_windowpixels,
|
||||
upgrade,
|
||||
urgency,
|
||||
uri,
|
||||
user_agent,
|
||||
variant_vary,
|
||||
vary,
|
||||
vbr_info,
|
||||
version,
|
||||
via,
|
||||
want_digest,
|
||||
warning,
|
||||
www_authenticate,
|
||||
x_archived_at,
|
||||
x_device_accept,
|
||||
x_device_accept_charset,
|
||||
x_device_accept_encoding,
|
||||
x_device_accept_language,
|
||||
x_device_user_agent,
|
||||
x_frame_options,
|
||||
x_mittente,
|
||||
x_pgp_sig,
|
||||
x_ricevuta,
|
||||
x_riferimento_message_id,
|
||||
x_tiporicevuta,
|
||||
x_trasporto,
|
||||
x_verificasicurezza,
|
||||
x400_content_identifier,
|
||||
x400_content_return,
|
||||
x400_content_type,
|
||||
x400_mts_identifier,
|
||||
x400_originator,
|
||||
x400_received,
|
||||
x400_recipients,
|
||||
x400_trace,
|
||||
xref,
|
||||
};
|
||||
|
||||
/** Convert a field enum to a string.
|
||||
|
||||
@param f The field to convert
|
||||
*/
|
||||
string_view
|
||||
to_string(field f);
|
||||
|
||||
/** Attempt to convert a string to a field enum.
|
||||
|
||||
The string comparison is case-insensitive.
|
||||
|
||||
@return The corresponding field, or @ref field::unknown
|
||||
if no known field matches.
|
||||
*/
|
||||
field
|
||||
string_to_field(string_view s);
|
||||
|
||||
/// Write the text for a field name to an output stream.
|
||||
inline
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, field f)
|
||||
{
|
||||
return os << to_string(f);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/field.ipp>
|
||||
|
||||
#endif
|
||||
@@ -9,17 +9,709 @@
|
||||
#define BEAST_HTTP_FIELDS_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/http/basic_fields.hpp>
|
||||
#include <beast/core/string_param.hpp>
|
||||
#include <beast/core/string.hpp>
|
||||
#include <beast/http/field.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <boost/intrusive/set.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A container for storing HTTP header fields.
|
||||
|
||||
This container is designed to store the field value pairs that make
|
||||
up the fields and trailers in an HTTP message. Objects of this type
|
||||
are iterable, with each element holding the field name and field
|
||||
value.
|
||||
|
||||
Field names are stored as-is, but comparisons are case-insensitive.
|
||||
The container behaves as a `std::multiset`; there will be a separate
|
||||
value for each occurrence of the same field name. When the container
|
||||
is iterated the fields are presented in the order of insertion, with
|
||||
fields having the same name following each other consecutively.
|
||||
|
||||
Meets the requirements of @b Fields
|
||||
|
||||
@tparam Allocator The allocator to use. This must meet the
|
||||
requirements of @b Allocator.
|
||||
*/
|
||||
template<class Allocator>
|
||||
class basic_fields
|
||||
{
|
||||
static std::size_t constexpr max_static_buffer = 4096;
|
||||
|
||||
using off_t = std::uint16_t;
|
||||
|
||||
public:
|
||||
/// The type of allocator used.
|
||||
using allocator_type = Allocator;
|
||||
|
||||
/// The type of element used to represent a field
|
||||
class value_type
|
||||
{
|
||||
friend class basic_fields;
|
||||
|
||||
boost::asio::const_buffer
|
||||
buffer() const;
|
||||
|
||||
value_type(field name,
|
||||
string_view sname, string_view value);
|
||||
|
||||
boost::intrusive::list_member_hook<
|
||||
boost::intrusive::link_mode<
|
||||
boost::intrusive::normal_link>>
|
||||
list_hook_;
|
||||
boost::intrusive::set_member_hook<
|
||||
boost::intrusive::link_mode<
|
||||
boost::intrusive::normal_link>>
|
||||
set_hook_;
|
||||
off_t off_;
|
||||
off_t len_;
|
||||
field f_;
|
||||
|
||||
public:
|
||||
/// Returns the field enum, which can be @ref field::unknown
|
||||
field
|
||||
name() const;
|
||||
|
||||
/// Returns the field name as a string
|
||||
string_view
|
||||
name_string() const;
|
||||
|
||||
/// Returns the value of the field
|
||||
string_view
|
||||
value() const;
|
||||
};
|
||||
|
||||
/** A strictly less predicate for comparing keys, using a case-insensitive comparison.
|
||||
|
||||
The case-comparison operation is defined only for low-ASCII characters.
|
||||
*/
|
||||
struct key_compare : beast::iless
|
||||
{
|
||||
/// Returns `true` if lhs is less than rhs using a strict ordering
|
||||
template<class String>
|
||||
bool
|
||||
operator()(String const& lhs, value_type const& rhs) const
|
||||
{
|
||||
if(lhs.size() < rhs.name_string().size())
|
||||
return true;
|
||||
if(lhs.size() > rhs.name_string().size())
|
||||
return false;
|
||||
return iless::operator()(lhs, rhs.name_string());
|
||||
}
|
||||
|
||||
/// Returns `true` if lhs is less than rhs using a strict ordering
|
||||
template<class String>
|
||||
bool
|
||||
operator()(value_type const& lhs, String const& rhs) const
|
||||
{
|
||||
if(lhs.name_string().size() < rhs.size())
|
||||
return true;
|
||||
if(lhs.name_string().size() > rhs.size())
|
||||
return false;
|
||||
return iless::operator()(lhs.name_string(), rhs);
|
||||
}
|
||||
|
||||
/// Returns `true` if lhs is less than rhs using a strict ordering
|
||||
bool
|
||||
operator()(value_type const& lhs, value_type const& rhs) const
|
||||
{
|
||||
if(lhs.name_string().size() < rhs.name_string().size())
|
||||
return true;
|
||||
if(lhs.name_string().size() > rhs.name_string().size())
|
||||
return false;
|
||||
return iless::operator()(lhs.name_string(), rhs.name_string());
|
||||
}
|
||||
};
|
||||
|
||||
/// The algorithm used to serialize the header
|
||||
#if BEAST_DOXYGEN
|
||||
using reader = implementation_defined;
|
||||
#else
|
||||
class reader;
|
||||
#endif
|
||||
|
||||
private:
|
||||
using list_t = typename boost::intrusive::make_list<
|
||||
value_type, boost::intrusive::member_hook<
|
||||
value_type, boost::intrusive::list_member_hook<
|
||||
boost::intrusive::link_mode<
|
||||
boost::intrusive::normal_link>>,
|
||||
&value_type::list_hook_>,
|
||||
boost::intrusive::constant_time_size<
|
||||
false>>::type;
|
||||
|
||||
using set_t = typename boost::intrusive::make_multiset<
|
||||
value_type, boost::intrusive::member_hook<value_type,
|
||||
boost::intrusive::set_member_hook<
|
||||
boost::intrusive::link_mode<
|
||||
boost::intrusive::normal_link>>,
|
||||
&value_type::set_hook_>,
|
||||
boost::intrusive::constant_time_size<true>,
|
||||
boost::intrusive::compare<key_compare>>::type;
|
||||
|
||||
|
||||
protected:
|
||||
friend class fields_test; // for `header`
|
||||
|
||||
/// Destructor
|
||||
~basic_fields();
|
||||
|
||||
/// Constructor.
|
||||
basic_fields() = default;
|
||||
|
||||
/** Constructor.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
explicit
|
||||
basic_fields(Allocator const& alloc);
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
The state of the moved-from object is
|
||||
as if constructed using the same allocator.
|
||||
*/
|
||||
basic_fields(basic_fields&&);
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
The state of the moved-from object is
|
||||
as if constructed using the same allocator.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
basic_fields(basic_fields&&, Allocator const& alloc);
|
||||
|
||||
/// Copy constructor.
|
||||
basic_fields(basic_fields const&);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
basic_fields(basic_fields const&, Allocator const& alloc);
|
||||
|
||||
/// Copy constructor.
|
||||
template<class OtherAlloc>
|
||||
basic_fields(basic_fields<OtherAlloc> const&);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_fields(basic_fields<OtherAlloc> const&,
|
||||
Allocator const& alloc);
|
||||
|
||||
/** Move assignment.
|
||||
|
||||
The state of the moved-from object is
|
||||
as if constructed using the same allocator.
|
||||
*/
|
||||
basic_fields& operator=(basic_fields&&);
|
||||
|
||||
/// Copy assignment.
|
||||
basic_fields& operator=(basic_fields const&);
|
||||
|
||||
/// Copy assignment.
|
||||
template<class OtherAlloc>
|
||||
basic_fields& operator=(basic_fields<OtherAlloc> const&);
|
||||
|
||||
public:
|
||||
/// A constant iterator to the field sequence.
|
||||
#if BEAST_DOXYGEN
|
||||
using const_iterator = implementation_defined;
|
||||
#else
|
||||
using const_iterator = typename list_t::const_iterator;
|
||||
#endif
|
||||
|
||||
/// A constant iterator to the field sequence.
|
||||
using iterator = const_iterator;
|
||||
|
||||
/// Return a copy of the allocator associated with the container.
|
||||
allocator_type
|
||||
get_allocator() const
|
||||
{
|
||||
return typename std::allocator_traits<
|
||||
Allocator>::template rebind_alloc<
|
||||
value_type>(alloc_);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Element access
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/** Returns the value for a field, or throws an exception.
|
||||
|
||||
@param name The name of the field.
|
||||
|
||||
@return The field value.
|
||||
|
||||
@throws std::out_of_range if the field is not found.
|
||||
*/
|
||||
string_view
|
||||
at(field name) const;
|
||||
|
||||
/** Returns the value for a field, or throws an exception.
|
||||
|
||||
@param name The name of the field.
|
||||
|
||||
@return The field value.
|
||||
|
||||
@throws std::out_of_range if the field is not found.
|
||||
*/
|
||||
string_view
|
||||
at(string_view name) const;
|
||||
|
||||
/** Returns the value for a field, or `""` if it does not exist.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
|
||||
@param name The name of the field.
|
||||
*/
|
||||
string_view
|
||||
operator[](field name) const;
|
||||
|
||||
/** Returns the value for a case-insensitive matching header, or `""` if it does not exist.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
|
||||
@param name The name of the field.
|
||||
*/
|
||||
string_view
|
||||
operator[](string_view name) const;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Iterators
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Return a const iterator to the beginning of the field sequence.
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return list_.cbegin();
|
||||
}
|
||||
|
||||
/// Return a const iterator to the end of the field sequence.
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return list_.cend();
|
||||
}
|
||||
|
||||
/// Return a const iterator to the beginning of the field sequence.
|
||||
const_iterator
|
||||
cbegin() const
|
||||
{
|
||||
return list_.cbegin();
|
||||
}
|
||||
|
||||
/// Return a const iterator to the end of the field sequence.
|
||||
const_iterator
|
||||
cend() const
|
||||
{
|
||||
return list_.cend();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Capacity
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
private:
|
||||
// VFALCO Since the header and message derive from Fields,
|
||||
// what does the expression m.empty() mean? Its confusing.
|
||||
bool
|
||||
empty() const
|
||||
{
|
||||
return list_.empty();
|
||||
}
|
||||
public:
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Modifiers
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
private:
|
||||
// VFALCO But this leaves behind the method, target, and reason!
|
||||
/** Remove all fields from the container
|
||||
|
||||
All references, pointers, or iterators referring to contained
|
||||
elements are invalidated. All past-the-end iterators are also
|
||||
invalidated.
|
||||
*/
|
||||
void
|
||||
clear();
|
||||
public:
|
||||
|
||||
/** Insert a field.
|
||||
|
||||
If one or more fields with the same name already exist,
|
||||
the new field will be inserted after the last field with
|
||||
the matching name, in serialization order.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@param value The value of the field, as a @ref string_param
|
||||
*/
|
||||
void
|
||||
insert(field name, string_param const& value);
|
||||
|
||||
/** Insert a field.
|
||||
|
||||
If one or more fields with the same name already exist,
|
||||
the new field will be inserted after the last field with
|
||||
the matching name, in serialization order.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@param value The value of the field, as a @ref string_param
|
||||
*/
|
||||
void
|
||||
insert(string_view name, string_param const& value);
|
||||
|
||||
/** Insert a field.
|
||||
|
||||
If one or more fields with the same name already exist,
|
||||
the new field will be inserted after the last field with
|
||||
the matching name, in serialization order.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@param name_string The literal text corresponding to the
|
||||
field name. If `name != field::unknown`, then this value
|
||||
must be equal to `to_string(name)` using a case-insensitive
|
||||
comparison, otherwise the behavior is undefined.
|
||||
|
||||
@param value The value of the field, as a @ref string_param
|
||||
*/
|
||||
void
|
||||
insert(field name, string_view name_string,
|
||||
string_param const& value);
|
||||
|
||||
/** Set a field value, removing any other instances of that field.
|
||||
|
||||
First removes any values with matching field names, then
|
||||
inserts the new field value.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@param value The value of the field, as a @ref string_param
|
||||
|
||||
@return The field value.
|
||||
*/
|
||||
void
|
||||
set(field name, string_param const& value);
|
||||
|
||||
/** Set a field value, removing any other instances of that field.
|
||||
|
||||
First removes any values with matching field names, then
|
||||
inserts the new field value.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@param value The value of the field, as a @ref string_param
|
||||
*/
|
||||
void
|
||||
set(string_view name, string_param const& value);
|
||||
|
||||
/** Remove a field.
|
||||
|
||||
References and iterators to the erased elements are
|
||||
invalidated. Other references and iterators are not
|
||||
affected.
|
||||
|
||||
@param pos An iterator to the element to remove.
|
||||
|
||||
@return An iterator following the last removed element.
|
||||
If the iterator refers to the last element, the end()
|
||||
iterator is returned.
|
||||
*/
|
||||
const_iterator
|
||||
erase(const_iterator pos);
|
||||
|
||||
/** Remove all fields with the specified name.
|
||||
|
||||
All fields with the same field name are erased from the
|
||||
container.
|
||||
References and iterators to the erased elements are
|
||||
invalidated. Other references and iterators are not
|
||||
affected.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@return The number of fields removed.
|
||||
*/
|
||||
std::size_t
|
||||
erase(field name);
|
||||
|
||||
/** Remove all fields with the specified name.
|
||||
|
||||
All fields with the same field name are erased from the
|
||||
container.
|
||||
References and iterators to the erased elements are
|
||||
invalidated. Other references and iterators are not
|
||||
affected.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@return The number of fields removed.
|
||||
*/
|
||||
std::size_t
|
||||
erase(string_view name);
|
||||
|
||||
/// Swap this container with another
|
||||
void
|
||||
swap(basic_fields& other);
|
||||
|
||||
/// Swap two field containers
|
||||
template<class Alloc>
|
||||
friend
|
||||
void
|
||||
swap(basic_fields<Alloc>& lhs, basic_fields<Alloc>& rhs);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Lookup
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/** Return the number of fields with the specified name.
|
||||
|
||||
@param name The field name.
|
||||
*/
|
||||
std::size_t
|
||||
count(field name) const;
|
||||
|
||||
/** Return the number of fields with the specified name.
|
||||
|
||||
@param name The field name.
|
||||
*/
|
||||
std::size_t
|
||||
count(string_view name) const;
|
||||
|
||||
/** Returns an iterator to the case-insensitive matching field.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@return An iterator to the matching field, or `end()` if
|
||||
no match was found.
|
||||
*/
|
||||
const_iterator
|
||||
find(field name) const;
|
||||
|
||||
/** Returns an iterator to the case-insensitive matching field name.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@return An iterator to the matching field, or `end()` if
|
||||
no match was found.
|
||||
*/
|
||||
const_iterator
|
||||
find(string_view name) const;
|
||||
|
||||
/** Returns a range of iterators to the fields with the specified name.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@return A range of iterators to fields with the same name,
|
||||
otherwise an empty range.
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator>
|
||||
equal_range(field name) const;
|
||||
|
||||
/** Returns a range of iterators to the fields with the specified name.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@return A range of iterators to fields with the same name,
|
||||
otherwise an empty range.
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator>
|
||||
equal_range(string_view name) const;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Observers
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Returns a copy of the key comparison function
|
||||
key_compare
|
||||
key_comp() const
|
||||
{
|
||||
return key_compare{};
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Returns the request-method string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
string_view
|
||||
get_method_impl() const;
|
||||
|
||||
/** Returns the request-target string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
string_view
|
||||
get_target_impl() const;
|
||||
|
||||
/** Returns the response reason-phrase string.
|
||||
|
||||
@note Only called for responses.
|
||||
*/
|
||||
string_view
|
||||
get_reason_impl() const;
|
||||
|
||||
/** Returns the chunked Transfer-Encoding setting
|
||||
*/
|
||||
bool
|
||||
get_chunked_impl() const;
|
||||
|
||||
/** Returns the keep-alive setting
|
||||
*/
|
||||
bool
|
||||
get_keep_alive_impl(unsigned version) const;
|
||||
|
||||
/** Set or clear the method string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
void
|
||||
set_method_impl(string_view s);
|
||||
|
||||
/** Set or clear the target string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
void
|
||||
set_target_impl(string_view s);
|
||||
|
||||
/** Set or clear the reason string.
|
||||
|
||||
@note Only called for responses.
|
||||
*/
|
||||
void
|
||||
set_reason_impl(string_view s);
|
||||
|
||||
/** Adjusts the chunked Transfer-Encoding value
|
||||
*/
|
||||
void
|
||||
set_chunked_impl(bool value);
|
||||
|
||||
/** Sets or clears the Content-Length field
|
||||
*/
|
||||
void
|
||||
set_content_length_impl(
|
||||
boost::optional<std::uint64_t> const& value);
|
||||
|
||||
/** Adjusts the Connection field
|
||||
*/
|
||||
void
|
||||
set_keep_alive_impl(
|
||||
unsigned version, bool keep_alive);
|
||||
|
||||
private:
|
||||
template<class OtherAlloc>
|
||||
friend class basic_fields;
|
||||
|
||||
using alloc_type = typename
|
||||
std::allocator_traits<Allocator>::
|
||||
template rebind_alloc<value_type>;
|
||||
|
||||
using alloc_traits =
|
||||
std::allocator_traits<alloc_type>;
|
||||
|
||||
using size_type =
|
||||
typename std::allocator_traits<Allocator>::size_type;
|
||||
|
||||
value_type&
|
||||
new_element(field name,
|
||||
string_view sname, string_view value);
|
||||
|
||||
void
|
||||
delete_element(value_type& e);
|
||||
|
||||
void
|
||||
set_element(value_type& e);
|
||||
|
||||
void
|
||||
realloc_string(string_view& dest, string_view s);
|
||||
|
||||
void
|
||||
realloc_target(
|
||||
string_view& dest, string_view s);
|
||||
|
||||
template<class OtherAlloc>
|
||||
void
|
||||
copy_all(basic_fields<OtherAlloc> const&);
|
||||
|
||||
void
|
||||
clear_all();
|
||||
|
||||
void
|
||||
delete_list();
|
||||
|
||||
void
|
||||
move_assign(basic_fields&, std::true_type);
|
||||
|
||||
void
|
||||
move_assign(basic_fields&, std::false_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_fields const&, std::true_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_fields const&, std::false_type);
|
||||
|
||||
void
|
||||
swap(basic_fields& other, std::true_type);
|
||||
|
||||
void
|
||||
swap(basic_fields& other, std::false_type);
|
||||
|
||||
alloc_type alloc_;
|
||||
set_t set_;
|
||||
list_t list_;
|
||||
string_view method_;
|
||||
string_view target_or_reason_;
|
||||
};
|
||||
|
||||
/// A typical HTTP header fields container
|
||||
using fields =
|
||||
basic_fields<std::allocator<char>>;
|
||||
using fields = basic_fields<std::allocator<char>>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/fields.ipp>
|
||||
|
||||
#endif
|
||||
|
||||
522
include/beast/http/file_body.hpp
Normal file
522
include/beast/http/file_body.hpp
Normal file
@@ -0,0 +1,522 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_FILE_BODY_HPP
|
||||
#define BEAST_HTTP_FILE_BODY_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/file.hpp>
|
||||
#include <beast/core/type_traits.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
//[example_http_file_body_1
|
||||
|
||||
/** A message body represented by a file on the filesystem.
|
||||
|
||||
Messages with this type have bodies represented by a
|
||||
file on the file system. When parsing a message using
|
||||
this body type, the data is stored in the file pointed
|
||||
to by the path, which must be writable. When serializing,
|
||||
the implementation will read the file and present those
|
||||
octets as the body content. This may be used to serve
|
||||
content from a directory as part of a web service.
|
||||
|
||||
@tparam File The implementation to use for accessing files.
|
||||
This type must meet the requirements of @b File.
|
||||
*/
|
||||
template<class File>
|
||||
struct basic_file_body
|
||||
{
|
||||
static_assert(is_file<File>::value,
|
||||
"File requirements not met");
|
||||
|
||||
/// The type of File this body uses
|
||||
using file_type = File;
|
||||
|
||||
/** Algorithm for retrieving buffers when serializing.
|
||||
|
||||
Objects of this type are created during serialization
|
||||
to extract the buffers representing the body.
|
||||
*/
|
||||
class reader;
|
||||
|
||||
/** Algorithm for storing buffers when parsing.
|
||||
|
||||
Objects of this type are created during parsing
|
||||
to store incoming buffers representing the body.
|
||||
*/
|
||||
class writer;
|
||||
|
||||
/** The type of the @ref message::body member.
|
||||
|
||||
Messages declared using `basic_file_body` will have this
|
||||
type for the body member. This rich class interface
|
||||
allow the file to be opened with the file handle
|
||||
maintained directly in the object, which is attached
|
||||
to the message.
|
||||
*/
|
||||
class value_type;
|
||||
|
||||
/** Returns the size of the body
|
||||
|
||||
@param v The file body to use
|
||||
*/
|
||||
static
|
||||
std::uint64_t
|
||||
size(value_type const& v);
|
||||
};
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_2
|
||||
|
||||
// The body container holds a handle to the file when
|
||||
// it is open, and also caches the size when set.
|
||||
//
|
||||
template<class File>
|
||||
class basic_file_body<File>::value_type
|
||||
{
|
||||
friend class reader;
|
||||
friend class writer;
|
||||
friend struct basic_file_body;
|
||||
|
||||
// This represents the open file
|
||||
File file_;
|
||||
|
||||
// The cached file size
|
||||
std::uint64_t file_size_ = 0;
|
||||
|
||||
public:
|
||||
/** Destructor.
|
||||
|
||||
If the file is open, it is closed first.
|
||||
*/
|
||||
~value_type() = default;
|
||||
|
||||
/// Constructor
|
||||
value_type() = default;
|
||||
|
||||
/// Constructor
|
||||
value_type(value_type&& other) = default;
|
||||
|
||||
/// Move assignment
|
||||
value_type& operator=(value_type&& other) = default;
|
||||
|
||||
/// Returns `true` if the file is open
|
||||
bool
|
||||
is_open() const
|
||||
{
|
||||
return file_.is_open();
|
||||
}
|
||||
|
||||
/// Returns the size of the file if open
|
||||
std::uint64_t
|
||||
size() const
|
||||
{
|
||||
return file_size_;
|
||||
}
|
||||
|
||||
/// Close the file if open
|
||||
void
|
||||
close();
|
||||
|
||||
/** Open a file at the given path with the specified mode
|
||||
|
||||
@param path The utf-8 encoded path to the file
|
||||
|
||||
@param mode The file mode to use
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
void
|
||||
open(char const* path, file_mode mode, error_code& ec);
|
||||
|
||||
/** Set the open file
|
||||
|
||||
This function is used to set the open
|
||||
*/
|
||||
void
|
||||
reset(File&& file, error_code& ec);
|
||||
};
|
||||
|
||||
template<class File>
|
||||
void
|
||||
basic_file_body<File>::
|
||||
value_type::
|
||||
close()
|
||||
{
|
||||
error_code ignored;
|
||||
file_.close(ignored);
|
||||
}
|
||||
|
||||
template<class File>
|
||||
void
|
||||
basic_file_body<File>::
|
||||
value_type::
|
||||
open(char const* path, file_mode mode, error_code& ec)
|
||||
{
|
||||
// Open the file
|
||||
file_.open(path, mode, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
// Cache the size
|
||||
file_size_ = file_.size(ec);
|
||||
if(ec)
|
||||
{
|
||||
close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template<class File>
|
||||
void
|
||||
basic_file_body<File>::
|
||||
value_type::
|
||||
reset(File&& file, error_code& ec)
|
||||
{
|
||||
// First close the file if open
|
||||
if(file_.is_open())
|
||||
{
|
||||
error_code ignored;
|
||||
file_.close(ignored);
|
||||
}
|
||||
|
||||
// Take ownership of the new file
|
||||
file_ = std::move(file);
|
||||
|
||||
// Cache the size
|
||||
file_size_ = file_.size(ec);
|
||||
}
|
||||
|
||||
// This is called from message::payload_size
|
||||
template<class File>
|
||||
std::uint64_t
|
||||
basic_file_body<File>::
|
||||
size(value_type const& v)
|
||||
{
|
||||
// Forward the call to the body
|
||||
return v.size();
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_3
|
||||
|
||||
template<class File>
|
||||
class basic_file_body<File>::reader
|
||||
{
|
||||
value_type& body_; // The body we are reading from
|
||||
std::uint64_t remain_; // The number of unread bytes
|
||||
char buf_[4096]; // Small buffer for reading
|
||||
|
||||
public:
|
||||
// The type of buffer sequence returned by `get`.
|
||||
//
|
||||
using const_buffers_type =
|
||||
boost::asio::const_buffers_1;
|
||||
|
||||
// Constructor.
|
||||
//
|
||||
// `m` holds the message we are sending, which will
|
||||
// always have the `file_body` as the body type.
|
||||
//
|
||||
// Note that the message is passed by non-const reference.
|
||||
// This is intentional, because reading from the file
|
||||
// changes its "current position" which counts makes the
|
||||
// operation logically not-const (although it is bitwise
|
||||
// const).
|
||||
//
|
||||
// The BodyReader concept allows the reader to choose
|
||||
// whether to take the message by const reference or
|
||||
// non-const reference. Depending on the choice, a
|
||||
// serializer constructed using that body type will
|
||||
// require the same const or non-const reference to
|
||||
// construct.
|
||||
//
|
||||
// Readers which accept const messages usually allow
|
||||
// the same body to be serialized by multiple threads
|
||||
// concurrently, while readers accepting non-const
|
||||
// messages may only be serialized by one thread at
|
||||
// a time.
|
||||
//
|
||||
template<bool isRequest, class Fields>
|
||||
reader(message<
|
||||
isRequest, basic_file_body, Fields>& m);
|
||||
|
||||
// Initializer
|
||||
//
|
||||
// This is called before the body is serialized and
|
||||
// gives the reader a chance to do something that might
|
||||
// need to return an error code.
|
||||
//
|
||||
void
|
||||
init(error_code& ec);
|
||||
|
||||
// This function is called zero or more times to
|
||||
// retrieve buffers. A return value of `boost::none`
|
||||
// means there are no more buffers. Otherwise,
|
||||
// the contained pair will have the next buffer
|
||||
// to serialize, and a `bool` indicating whether
|
||||
// or not there may be additional buffers.
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec);
|
||||
};
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_4
|
||||
|
||||
// Here we just stash a reference to the path for later.
|
||||
// Rather than dealing with messy constructor exceptions,
|
||||
// we save the things that might fail for the call to `init`.
|
||||
//
|
||||
template<class File>
|
||||
template<bool isRequest, class Fields>
|
||||
basic_file_body<File>::
|
||||
reader::
|
||||
reader(message<isRequest, basic_file_body, Fields>& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
// The file must already be open
|
||||
BOOST_ASSERT(body_.file_.is_open());
|
||||
|
||||
// Get the size of the file
|
||||
remain_ = body_.file_size_;
|
||||
}
|
||||
|
||||
// Initializer
|
||||
template<class File>
|
||||
void
|
||||
basic_file_body<File>::
|
||||
reader::
|
||||
init(error_code& ec)
|
||||
{
|
||||
// The error_code specification requires that we
|
||||
// either set the error to some value, or set it
|
||||
// to indicate no error.
|
||||
//
|
||||
// We don't do anything fancy so set "no error"
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
// This function is called repeatedly by the serializer to
|
||||
// retrieve the buffers representing the body. Our strategy
|
||||
// is to read into our buffer and return it until we have
|
||||
// read through the whole file.
|
||||
//
|
||||
template<class File>
|
||||
auto
|
||||
basic_file_body<File>::
|
||||
reader::
|
||||
get(error_code& ec) ->
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
{
|
||||
// Calculate the smaller of our buffer size,
|
||||
// or the amount of unread data in the file.
|
||||
auto const amount = remain_ > sizeof(buf_) ?
|
||||
sizeof(buf_) : static_cast<std::size_t>(remain_);
|
||||
|
||||
// Handle the case where the file is zero length
|
||||
if(amount == 0)
|
||||
{
|
||||
// Modify the error code to indicate success
|
||||
// This is required by the error_code specification.
|
||||
//
|
||||
// NOTE We use the existing category instead of calling
|
||||
// into the library to get the generic category because
|
||||
// that saves us a possibly expensive atomic operation.
|
||||
//
|
||||
ec.assign(0, ec.category());
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
// Now read the next buffer
|
||||
auto const nread = body_.file_.read(buf_, amount, ec);
|
||||
if(ec)
|
||||
return boost::none;
|
||||
|
||||
// Make sure there is forward progress
|
||||
BOOST_ASSERT(nread != 0);
|
||||
BOOST_ASSERT(nread <= remain_);
|
||||
|
||||
// Update the amount remaining based on what we got
|
||||
remain_ -= nread;
|
||||
|
||||
// Return the buffer to the caller.
|
||||
//
|
||||
// The second element of the pair indicates whether or
|
||||
// not there is more data. As long as there is some
|
||||
// unread bytes, there will be more data. Otherwise,
|
||||
// we set this bool to `false` so we will not be called
|
||||
// again.
|
||||
//
|
||||
ec.assign(0, ec.category());
|
||||
return {{
|
||||
const_buffers_type{buf_, nread}, // buffer to return.
|
||||
remain_ > 0 // `true` if there are more buffers.
|
||||
}};
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_5
|
||||
|
||||
template<class File>
|
||||
class basic_file_body<File>::writer
|
||||
{
|
||||
value_type& body_; // The body we are writing to
|
||||
|
||||
public:
|
||||
// Constructor.
|
||||
//
|
||||
// This is called after the header is parsed and
|
||||
// indicates that a non-zero sized body may be present.
|
||||
// `m` holds the message we are receiving, which will
|
||||
// always have the `file_body` as the body type.
|
||||
//
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(
|
||||
message<isRequest, basic_file_body, Fields>& m);
|
||||
|
||||
// Initializer
|
||||
//
|
||||
// This is called before the body is parsed and
|
||||
// gives the writer a chance to do something that might
|
||||
// need to return an error code. It informs us of
|
||||
// the payload size (`content_length`) which we can
|
||||
// optionally use for optimization.
|
||||
//
|
||||
void
|
||||
init(boost::optional<std::uint64_t> const&, error_code& ec);
|
||||
|
||||
// This function is called one or more times to store
|
||||
// buffer sequences corresponding to the incoming body.
|
||||
//
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec);
|
||||
|
||||
// This function is called when writing is complete.
|
||||
// It is an opportunity to perform any final actions
|
||||
// which might fail, in order to return an error code.
|
||||
// Operations that might fail should not be attemped in
|
||||
// destructors, since an exception thrown from there
|
||||
// would terminate the program.
|
||||
//
|
||||
void
|
||||
finish(error_code& ec);
|
||||
};
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_6
|
||||
|
||||
// We don't do much in the writer constructor since the
|
||||
// file is already open.
|
||||
//
|
||||
template<class File>
|
||||
template<bool isRequest, class Fields>
|
||||
basic_file_body<File>::
|
||||
writer::
|
||||
writer(message<isRequest, basic_file_body, Fields>& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
template<class File>
|
||||
void
|
||||
basic_file_body<File>::
|
||||
writer::
|
||||
init(
|
||||
boost::optional<std::uint64_t> const& content_length,
|
||||
error_code& ec)
|
||||
{
|
||||
// The file must already be open for writing
|
||||
BOOST_ASSERT(body_.file_.is_open());
|
||||
|
||||
// We don't do anything with this but a sophisticated
|
||||
// application might check available space on the device
|
||||
// to see if there is enough room to store the body.
|
||||
boost::ignore_unused(content_length);
|
||||
|
||||
// The error_code specification requires that we
|
||||
// either set the error to some value, or set it
|
||||
// to indicate no error.
|
||||
//
|
||||
// We don't do anything fancy so set "no error"
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
// This will get called one or more times with body buffers
|
||||
//
|
||||
template<class File>
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
basic_file_body<File>::
|
||||
writer::
|
||||
put(ConstBufferSequence const& buffers, error_code& ec)
|
||||
{
|
||||
// This function must return the total number of
|
||||
// bytes transferred from the input buffers.
|
||||
std::size_t nwritten = 0;
|
||||
|
||||
// Loop over all the buffers in the sequence,
|
||||
// and write each one to the file.
|
||||
for(boost::asio::const_buffer buffer : buffers)
|
||||
{
|
||||
// Write this buffer to the file
|
||||
nwritten += body_.file_.write(
|
||||
boost::asio::buffer_cast<void const*>(buffer),
|
||||
boost::asio::buffer_size(buffer),
|
||||
ec);
|
||||
if(ec)
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
// Indicate success
|
||||
// This is required by the error_code specification
|
||||
ec.assign(0, ec.category());
|
||||
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
// Called after writing is done when there's no error.
|
||||
template<class File>
|
||||
void
|
||||
basic_file_body<File>::
|
||||
writer::
|
||||
finish(error_code& ec)
|
||||
{
|
||||
// This has to be cleared before returning, to
|
||||
// indicate no error. The specification requires it.
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
/// A message body represented by a file on the filesystem.
|
||||
using file_body = basic_file_body<file>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/file_body_win32.ipp>
|
||||
|
||||
#endif
|
||||
@@ -1,233 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_HEADERS_PARSER_V1_HPP
|
||||
#define BEAST_HTTP_HEADERS_PARSER_V1_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/http/basic_parser_v1.hpp>
|
||||
#include <beast/http/concepts.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct request_parser_base
|
||||
{
|
||||
std::string method_;
|
||||
std::string uri_;
|
||||
};
|
||||
|
||||
struct response_parser_base
|
||||
{
|
||||
std::string reason_;
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
/** A parser for a HTTP/1 request or response header.
|
||||
|
||||
This class uses the HTTP/1 wire format parser to
|
||||
convert a series of octets into a request or
|
||||
response @ref header.
|
||||
|
||||
@note A new instance of the parser is required for each message.
|
||||
*/
|
||||
template<bool isRequest, class Fields>
|
||||
class header_parser_v1
|
||||
: public basic_parser_v1<isRequest,
|
||||
header_parser_v1<isRequest, Fields>>
|
||||
, private std::conditional<isRequest,
|
||||
detail::request_parser_base,
|
||||
detail::response_parser_base>::type
|
||||
{
|
||||
public:
|
||||
/// The type of the header this parser produces.
|
||||
using header_type = header<isRequest, Fields>;
|
||||
|
||||
private:
|
||||
// VFALCO Check Fields requirements?
|
||||
|
||||
std::string field_;
|
||||
std::string value_;
|
||||
header_type h_;
|
||||
bool flush_ = false;
|
||||
|
||||
public:
|
||||
/// Default constructor
|
||||
header_parser_v1() = default;
|
||||
|
||||
/// Move constructor
|
||||
header_parser_v1(header_parser_v1&&) = default;
|
||||
|
||||
/// Copy constructor (disallowed)
|
||||
header_parser_v1(header_parser_v1 const&) = delete;
|
||||
|
||||
/// Move assignment (disallowed)
|
||||
header_parser_v1& operator=(header_parser_v1&&) = delete;
|
||||
|
||||
/// Copy assignment (disallowed)
|
||||
header_parser_v1& operator=(header_parser_v1 const&) = delete;
|
||||
|
||||
/** Construct the parser.
|
||||
|
||||
@param args Forwarded to the header constructor.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
template<class... Args>
|
||||
explicit
|
||||
header_parser_v1(Args&&... args);
|
||||
#else
|
||||
template<class Arg1, class... ArgN,
|
||||
class = typename std::enable_if<! std::is_same<
|
||||
typename std::decay<Arg1>::type, header_parser_v1>::value>>
|
||||
explicit
|
||||
header_parser_v1(Arg1&& arg1, ArgN&&... argn)
|
||||
: h_(std::forward<Arg1>(arg1),
|
||||
std::forward<ArgN>(argn)...)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Returns the parsed header
|
||||
|
||||
Only valid if @ref complete would return `true`.
|
||||
*/
|
||||
header_type const&
|
||||
get() const
|
||||
{
|
||||
return h_;
|
||||
}
|
||||
|
||||
/** Returns the parsed header.
|
||||
|
||||
Only valid if @ref complete would return `true`.
|
||||
*/
|
||||
header_type&
|
||||
get()
|
||||
{
|
||||
return h_;
|
||||
}
|
||||
|
||||
/** Returns ownership of the parsed header.
|
||||
|
||||
Ownership is transferred to the caller. Only
|
||||
valid if @ref complete would return `true`.
|
||||
|
||||
Requires:
|
||||
@ref header_type is @b MoveConstructible
|
||||
*/
|
||||
header_type
|
||||
release()
|
||||
{
|
||||
static_assert(std::is_move_constructible<decltype(h_)>::value,
|
||||
"MoveConstructible requirements not met");
|
||||
return std::move(h_);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class basic_parser_v1<isRequest, header_parser_v1>;
|
||||
|
||||
void flush()
|
||||
{
|
||||
if(! flush_)
|
||||
return;
|
||||
flush_ = false;
|
||||
BOOST_ASSERT(! field_.empty());
|
||||
h_.fields.insert(field_, value_);
|
||||
field_.clear();
|
||||
value_.clear();
|
||||
}
|
||||
|
||||
void on_start(error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
void on_method(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->method_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_uri(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->uri_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_reason(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->reason_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_request_or_response(std::true_type)
|
||||
{
|
||||
h_.method = std::move(this->method_);
|
||||
h_.url = std::move(this->uri_);
|
||||
}
|
||||
|
||||
void on_request_or_response(std::false_type)
|
||||
{
|
||||
h_.status = this->status_code();
|
||||
h_.reason = std::move(this->reason_);
|
||||
}
|
||||
|
||||
void on_request(error_code& ec)
|
||||
{
|
||||
on_request_or_response(
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void on_response(error_code& ec)
|
||||
{
|
||||
on_request_or_response(
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void on_field(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
flush();
|
||||
field_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_value(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
value_.append(s.data(), s.size());
|
||||
flush_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
on_header(std::uint64_t, error_code&)
|
||||
{
|
||||
flush();
|
||||
h_.version = 10 * this->http_major() + this->http_minor();
|
||||
}
|
||||
|
||||
body_what
|
||||
on_body_what(std::uint64_t, error_code&)
|
||||
{
|
||||
return body_what::pause;
|
||||
}
|
||||
|
||||
void on_body(boost::string_ref const&, error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
void on_complete(error_code&)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -1,266 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_IMPL_BASIC_FIELDS_IPP
|
||||
#define BEAST_HTTP_IMPL_BASIC_FIELDS_IPP
|
||||
|
||||
#include <beast/http/detail/rfc7230.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
delete_all()
|
||||
{
|
||||
for(auto it = list_.begin(); it != list_.end();)
|
||||
{
|
||||
auto& e = *it++;
|
||||
alloc_traits::destroy(this->member(), &e);
|
||||
alloc_traits::deallocate(
|
||||
this->member(), &e, 1);
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
move_assign(basic_fields& other, std::false_type)
|
||||
{
|
||||
if(this->member() != other.member())
|
||||
{
|
||||
copy_from(other);
|
||||
other.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
set_ = std::move(other.set_);
|
||||
list_ = std::move(other.list_);
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
move_assign(basic_fields& other, std::true_type)
|
||||
{
|
||||
this->member() = std::move(other.member());
|
||||
set_ = std::move(other.set_);
|
||||
list_ = std::move(other.list_);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
copy_assign(basic_fields const& other, std::false_type)
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
copy_assign(basic_fields const& other, std::true_type)
|
||||
{
|
||||
this->member() = other.member();
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
basic_fields<Allocator>::
|
||||
~basic_fields()
|
||||
{
|
||||
delete_all();
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_fields<Allocator>::
|
||||
basic_fields(Allocator const& alloc)
|
||||
: beast::detail::empty_base_optimization<
|
||||
alloc_type>(alloc)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_fields<Allocator>::
|
||||
basic_fields(basic_fields&& other)
|
||||
: beast::detail::empty_base_optimization<alloc_type>(
|
||||
std::move(other.member()))
|
||||
, detail::basic_fields_base(
|
||||
std::move(other.set_), std::move(other.list_))
|
||||
{
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_fields<Allocator>::
|
||||
operator=(basic_fields&& other) ->
|
||||
basic_fields&
|
||||
{
|
||||
if(this == &other)
|
||||
return *this;
|
||||
clear();
|
||||
move_assign(other, std::integral_constant<bool,
|
||||
alloc_traits::propagate_on_container_move_assignment::value>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_fields<Allocator>::
|
||||
basic_fields(basic_fields const& other)
|
||||
: basic_fields(alloc_traits::
|
||||
select_on_container_copy_construction(other.member()))
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_fields<Allocator>::
|
||||
operator=(basic_fields const& other) ->
|
||||
basic_fields&
|
||||
{
|
||||
clear();
|
||||
copy_assign(other, std::integral_constant<bool,
|
||||
alloc_traits::propagate_on_container_copy_assignment::value>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
basic_fields<Allocator>::
|
||||
basic_fields(basic_fields<OtherAlloc> const& other)
|
||||
{
|
||||
copy_from(other);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class OtherAlloc>
|
||||
auto
|
||||
basic_fields<Allocator>::
|
||||
operator=(basic_fields<OtherAlloc> const& other) ->
|
||||
basic_fields&
|
||||
{
|
||||
clear();
|
||||
copy_from(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
template<class FwdIt>
|
||||
basic_fields<Allocator>::
|
||||
basic_fields(FwdIt first, FwdIt last)
|
||||
{
|
||||
for(;first != last; ++first)
|
||||
insert(first->name(), first->value());
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
std::size_t
|
||||
basic_fields<Allocator>::
|
||||
count(boost::string_ref const& name) const
|
||||
{
|
||||
auto const it = set_.find(name, less{});
|
||||
if(it == set_.end())
|
||||
return 0;
|
||||
auto const last = set_.upper_bound(name, less{});
|
||||
return static_cast<std::size_t>(std::distance(it, last));
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_fields<Allocator>::
|
||||
find(boost::string_ref const& name) const ->
|
||||
iterator
|
||||
{
|
||||
auto const it = set_.find(name, less{});
|
||||
if(it == set_.end())
|
||||
return list_.end();
|
||||
return list_.iterator_to(*it);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
boost::string_ref
|
||||
basic_fields<Allocator>::
|
||||
operator[](boost::string_ref const& name) const
|
||||
{
|
||||
auto const it = find(name);
|
||||
if(it == end())
|
||||
return {};
|
||||
return it->second;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
clear() noexcept
|
||||
{
|
||||
delete_all();
|
||||
list_.clear();
|
||||
set_.clear();
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
std::size_t
|
||||
basic_fields<Allocator>::
|
||||
erase(boost::string_ref const& name)
|
||||
{
|
||||
auto it = set_.find(name, less{});
|
||||
if(it == set_.end())
|
||||
return 0;
|
||||
auto const last = set_.upper_bound(name, less{});
|
||||
std::size_t n = 1;
|
||||
for(;;)
|
||||
{
|
||||
auto& e = *it++;
|
||||
set_.erase(set_.iterator_to(e));
|
||||
list_.erase(list_.iterator_to(e));
|
||||
alloc_traits::destroy(this->member(), &e);
|
||||
alloc_traits::deallocate(this->member(), &e, 1);
|
||||
if(it == last)
|
||||
break;
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
insert(boost::string_ref const& name,
|
||||
boost::string_ref value)
|
||||
{
|
||||
value = detail::trim(value);
|
||||
auto const p = alloc_traits::allocate(this->member(), 1);
|
||||
alloc_traits::construct(this->member(), p, name, value);
|
||||
set_.insert_before(set_.upper_bound(name, less{}), *p);
|
||||
list_.push_back(*p);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
replace(boost::string_ref const& name,
|
||||
boost::string_ref value)
|
||||
{
|
||||
value = detail::trim(value);
|
||||
erase(name);
|
||||
insert(name, value);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
932
include/beast/http/impl/basic_parser.ipp
Normal file
932
include/beast/http/impl/basic_parser.ipp
Normal file
@@ -0,0 +1,932 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_IMPL_BASIC_PARSER_IPP
|
||||
#define BEAST_HTTP_IMPL_BASIC_PARSER_IPP
|
||||
|
||||
#include <beast/core/static_string.hpp>
|
||||
#include <beast/core/type_traits.hpp>
|
||||
#include <beast/core/detail/clamp.hpp>
|
||||
#include <beast/core/detail/config.hpp>
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/rfc7230.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/make_unique.hpp>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
basic_parser<isRequest, Derived>::
|
||||
basic_parser()
|
||||
: body_limit_(
|
||||
default_body_limit(is_request{}))
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
template<class OtherDerived>
|
||||
basic_parser<isRequest, Derived>::
|
||||
basic_parser(basic_parser<
|
||||
isRequest, OtherDerived>&& other)
|
||||
: body_limit_(other.body_limit_)
|
||||
, len_(other.len_)
|
||||
, buf_(std::move(other.buf_))
|
||||
, buf_len_(other.buf_len_)
|
||||
, skip_(other.skip_)
|
||||
, state_(other.state_)
|
||||
, f_(other.f_)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
bool
|
||||
basic_parser<isRequest, Derived>::
|
||||
is_keep_alive() const
|
||||
{
|
||||
BOOST_ASSERT(is_header_done());
|
||||
if(f_ & flagHTTP11)
|
||||
{
|
||||
if(f_ & flagConnectionClose)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(! (f_ & flagConnectionKeepAlive))
|
||||
return false;
|
||||
}
|
||||
return (f_ & flagNeedEOF) == 0;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
boost::optional<std::uint64_t>
|
||||
basic_parser<isRequest, Derived>::
|
||||
content_length() const
|
||||
{
|
||||
BOOST_ASSERT(is_header_done());
|
||||
if(! (f_ & flagContentLength))
|
||||
return boost::none;
|
||||
return len_;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
skip(bool v)
|
||||
{
|
||||
BOOST_ASSERT(! got_some());
|
||||
if(v)
|
||||
f_ |= flagSkipBody;
|
||||
else
|
||||
f_ &= ~flagSkipBody;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
basic_parser<isRequest, Derived>::
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
auto const p = buffers.begin();
|
||||
auto const last = buffers.end();
|
||||
if(p == last)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
return 0;
|
||||
}
|
||||
if(std::next(p) == last)
|
||||
{
|
||||
// single buffer
|
||||
auto const b = *p;
|
||||
return put(boost::asio::const_buffers_1{
|
||||
buffer_cast<char const*>(b),
|
||||
buffer_size(b)}, ec);
|
||||
}
|
||||
auto const size = buffer_size(buffers);
|
||||
if(size <= max_stack_buffer)
|
||||
return put_from_stack(size, buffers, ec);
|
||||
if(size > buf_len_)
|
||||
{
|
||||
// reallocate
|
||||
buf_ = boost::make_unique_noinit<char[]>(size);
|
||||
buf_len_ = size;
|
||||
}
|
||||
// flatten
|
||||
buffer_copy(boost::asio::buffer(
|
||||
buf_.get(), buf_len_), buffers);
|
||||
return put(boost::asio::const_buffers_1{
|
||||
buf_.get(), buf_len_}, ec);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
std::size_t
|
||||
basic_parser<isRequest, Derived>::
|
||||
put(boost::asio::const_buffers_1 const& buffer,
|
||||
error_code& ec)
|
||||
{
|
||||
BOOST_ASSERT(state_ != state::complete);
|
||||
using boost::asio::buffer_size;
|
||||
auto p = boost::asio::buffer_cast<
|
||||
char const*>(*buffer.begin());
|
||||
auto n = buffer_size(*buffer.begin());
|
||||
auto const p0 = p;
|
||||
auto const p1 = p0 + n;
|
||||
ec.assign(0, ec.category());
|
||||
loop:
|
||||
switch(state_)
|
||||
{
|
||||
case state::nothing_yet:
|
||||
if(n == 0)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return 0;
|
||||
}
|
||||
state_ = state::start_line;
|
||||
BEAST_FALLTHROUGH;
|
||||
|
||||
case state::start_line:
|
||||
{
|
||||
maybe_need_more(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
parse_start_line(p, p + std::min<std::size_t>(
|
||||
header_limit_, n), ec, is_request{});
|
||||
if(ec)
|
||||
{
|
||||
if(ec == error::need_more)
|
||||
{
|
||||
if(n >= header_limit_)
|
||||
{
|
||||
ec = error::header_limit;
|
||||
goto done;
|
||||
}
|
||||
if(p + 3 <= p1)
|
||||
skip_ = static_cast<
|
||||
std::size_t>(p1 - p - 3);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
BOOST_ASSERT(! is_done());
|
||||
n = static_cast<std::size_t>(p1 - p);
|
||||
if(p >= p1)
|
||||
{
|
||||
ec = error::need_more;
|
||||
goto done;
|
||||
}
|
||||
BEAST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case state::fields:
|
||||
maybe_need_more(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
parse_fields(p, p + std::min<std::size_t>(
|
||||
header_limit_, n), ec);
|
||||
if(ec)
|
||||
{
|
||||
if(ec == error::need_more)
|
||||
{
|
||||
if(n >= header_limit_)
|
||||
{
|
||||
ec = error::header_limit;
|
||||
goto done;
|
||||
}
|
||||
if(p + 3 <= p1)
|
||||
skip_ = static_cast<
|
||||
std::size_t>(p1 - p - 3);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
finish_header(ec, is_request{});
|
||||
break;
|
||||
|
||||
case state::body0:
|
||||
BOOST_ASSERT(! skip_);
|
||||
impl().on_body(content_length(), ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
state_ = state::body;
|
||||
BEAST_FALLTHROUGH;
|
||||
|
||||
case state::body:
|
||||
BOOST_ASSERT(! skip_);
|
||||
parse_body(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
break;
|
||||
|
||||
case state::body_to_eof0:
|
||||
BOOST_ASSERT(! skip_);
|
||||
impl().on_body(content_length(), ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
state_ = state::body_to_eof;
|
||||
BEAST_FALLTHROUGH;
|
||||
|
||||
case state::body_to_eof:
|
||||
BOOST_ASSERT(! skip_);
|
||||
parse_body_to_eof(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
break;
|
||||
|
||||
case state::chunk_header0:
|
||||
impl().on_body(content_length(), ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
state_ = state::chunk_header;
|
||||
BEAST_FALLTHROUGH;
|
||||
|
||||
case state::chunk_header:
|
||||
parse_chunk_header(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
break;
|
||||
|
||||
case state::chunk_body:
|
||||
parse_chunk_body(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
break;
|
||||
|
||||
case state::complete:
|
||||
ec.assign(0, ec.category());
|
||||
goto done;
|
||||
}
|
||||
if(p < p1 && ! is_done() && eager())
|
||||
{
|
||||
n = static_cast<std::size_t>(p1 - p);
|
||||
goto loop;
|
||||
}
|
||||
done:
|
||||
return static_cast<std::size_t>(p - p0);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
put_eof(error_code& ec)
|
||||
{
|
||||
BOOST_ASSERT(got_some());
|
||||
if( state_ == state::start_line ||
|
||||
state_ == state::fields)
|
||||
{
|
||||
ec = error::partial_message;
|
||||
return;
|
||||
}
|
||||
if(f_ & (flagContentLength | flagChunked))
|
||||
{
|
||||
if(state_ != state::complete)
|
||||
{
|
||||
ec = error::partial_message;
|
||||
return;
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return;
|
||||
}
|
||||
impl().on_complete(ec);
|
||||
if(ec)
|
||||
return;
|
||||
state_ = state::complete;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
basic_parser<isRequest, Derived>::
|
||||
put_from_stack(std::size_t size,
|
||||
ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
char buf[max_stack_buffer];
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
buffer_copy(buffer(buf, sizeof(buf)), buffers);
|
||||
return put(boost::asio::const_buffers_1{
|
||||
buf, size}, ec);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
maybe_need_more(
|
||||
char const* p, std::size_t n,
|
||||
error_code& ec)
|
||||
{
|
||||
if(skip_ == 0)
|
||||
return;
|
||||
if( n > header_limit_)
|
||||
n = header_limit_;
|
||||
if(n < skip_ + 4)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
auto const term =
|
||||
find_eom(p + skip_, p + n);
|
||||
if(! term)
|
||||
{
|
||||
skip_ = n - 3;
|
||||
if(skip_ + 4 > header_limit_)
|
||||
{
|
||||
ec = error::header_limit;
|
||||
return;
|
||||
}
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
skip_ = 0;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
parse_start_line(
|
||||
char const*& in, char const* last,
|
||||
error_code& ec, std::true_type)
|
||||
{
|
||||
/*
|
||||
request-line = method SP request-target SP HTTP-version CRLF
|
||||
method = token
|
||||
*/
|
||||
auto p = in;
|
||||
|
||||
string_view method;
|
||||
parse_method(p, last, method, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
string_view target;
|
||||
parse_target(p, last, target, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
int version = 0;
|
||||
parse_version(p, last, version, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(version < 10 || version > 11)
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
|
||||
if(p + 2 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(p[0] != '\r' || p[1] != '\n')
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
p += 2;
|
||||
|
||||
if(version >= 11)
|
||||
f_ |= flagHTTP11;
|
||||
|
||||
impl().on_request(string_to_verb(method),
|
||||
method, target, version, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
in = p;
|
||||
state_ = state::fields;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
parse_start_line(
|
||||
char const*& in, char const* last,
|
||||
error_code& ec, std::false_type)
|
||||
{
|
||||
/*
|
||||
status-line = HTTP-version SP status-code SP reason-phrase CRLF
|
||||
status-code = 3*DIGIT
|
||||
reason-phrase = *( HTAB / SP / VCHAR / obs-text )
|
||||
*/
|
||||
auto p = in;
|
||||
|
||||
int version = 0;
|
||||
parse_version(p, last, version, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(version < 10 || version > 11)
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
|
||||
// SP
|
||||
if(p + 1 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(*p++ != ' ')
|
||||
{
|
||||
ec = error::bad_version;
|
||||
return;
|
||||
}
|
||||
|
||||
parse_status(p, last, status_, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
// parse reason CRLF
|
||||
string_view reason;
|
||||
parse_reason(p, last, reason, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
if(version >= 11)
|
||||
f_ |= flagHTTP11;
|
||||
|
||||
impl().on_response(
|
||||
status_, reason, version, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
in = p;
|
||||
state_ = state::fields;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
parse_fields(char const*& in,
|
||||
char const* last, error_code& ec)
|
||||
{
|
||||
string_view name;
|
||||
string_view value;
|
||||
// https://stackoverflow.com/questions/686217/maximum-on-http-header-values
|
||||
static_string<max_obs_fold> buf;
|
||||
auto p = in;
|
||||
for(;;)
|
||||
{
|
||||
if(p + 2 > last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(p[0] == '\r')
|
||||
{
|
||||
if(p[1] != '\n')
|
||||
ec = error::bad_line_ending;
|
||||
in = p + 2;
|
||||
return;
|
||||
}
|
||||
parse_field(p, last, name, value, buf, ec);
|
||||
if(ec)
|
||||
return;
|
||||
auto const f = string_to_field(name);
|
||||
do_field(f, value, ec);
|
||||
if(ec)
|
||||
return;
|
||||
impl().on_field(f, name, value, ec);
|
||||
if(ec)
|
||||
return;
|
||||
in = p;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
finish_header(error_code& ec, std::true_type)
|
||||
{
|
||||
// RFC 7230 section 3.3
|
||||
// https://tools.ietf.org/html/rfc7230#section-3.3
|
||||
|
||||
if(f_ & flagSkipBody)
|
||||
{
|
||||
state_ = state::complete;
|
||||
}
|
||||
else if(f_ & flagContentLength)
|
||||
{
|
||||
if(len_ > 0)
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
state_ = state::body0;
|
||||
}
|
||||
else
|
||||
{
|
||||
state_ = state::complete;
|
||||
}
|
||||
}
|
||||
else if(f_ & flagChunked)
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
state_ = state::chunk_header0;
|
||||
}
|
||||
else
|
||||
{
|
||||
len_ = 0;
|
||||
state_ = state::complete;
|
||||
}
|
||||
|
||||
impl().on_header(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(state_ == state::complete)
|
||||
{
|
||||
impl().on_complete(ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
finish_header(error_code& ec, std::false_type)
|
||||
{
|
||||
// RFC 7230 section 3.3
|
||||
// https://tools.ietf.org/html/rfc7230#section-3.3
|
||||
|
||||
if( (f_ & flagSkipBody) || // e.g. response to a HEAD request
|
||||
status_ / 100 == 1 || // 1xx e.g. Continue
|
||||
status_ == 204 || // No Content
|
||||
status_ == 304) // Not Modified
|
||||
{
|
||||
state_ = state::complete;
|
||||
return;
|
||||
}
|
||||
|
||||
if(f_ & flagContentLength)
|
||||
{
|
||||
if(len_ > 0)
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
state_ = state::body0;
|
||||
}
|
||||
else
|
||||
{
|
||||
state_ = state::complete;
|
||||
}
|
||||
}
|
||||
else if(f_ & flagChunked)
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
state_ = state::chunk_header0;
|
||||
}
|
||||
else
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
f_ |= flagNeedEOF;
|
||||
state_ = state::body_to_eof0;
|
||||
}
|
||||
|
||||
impl().on_header(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(state_ == state::complete)
|
||||
{
|
||||
impl().on_complete(ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
parse_body(char const*& p,
|
||||
std::size_t n, error_code& ec)
|
||||
{
|
||||
n = impl().on_data(string_view{p,
|
||||
beast::detail::clamp(len_, n)}, ec);
|
||||
p += n;
|
||||
len_ -= n;
|
||||
if(ec)
|
||||
return;
|
||||
if(len_ > 0)
|
||||
return;
|
||||
impl().on_complete(ec);
|
||||
if(ec)
|
||||
return;
|
||||
state_ = state::complete;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
parse_body_to_eof(char const*& p,
|
||||
std::size_t n, error_code& ec)
|
||||
{
|
||||
if(n > body_limit_)
|
||||
{
|
||||
ec = error::body_limit;
|
||||
return;
|
||||
}
|
||||
body_limit_ = body_limit_ - n;
|
||||
n = impl().on_data(string_view{p, n}, ec);
|
||||
p += n;
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
parse_chunk_header(char const*& p0,
|
||||
std::size_t n, error_code& ec)
|
||||
{
|
||||
/*
|
||||
chunked-body = *chunk last-chunk trailer-part CRLF
|
||||
|
||||
chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
|
||||
last-chunk = 1*("0") [ chunk-ext ] CRLF
|
||||
trailer-part = *( header-field CRLF )
|
||||
|
||||
chunk-size = 1*HEXDIG
|
||||
chunk-data = 1*OCTET ; a sequence of chunk-size octets
|
||||
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
|
||||
chunk-ext-name = token
|
||||
chunk-ext-val = token / quoted-string
|
||||
*/
|
||||
|
||||
auto p = p0;
|
||||
auto const pend = p + n;
|
||||
char const* eol;
|
||||
|
||||
if(! (f_ & flagFinalChunk))
|
||||
{
|
||||
if(n < skip_ + 2)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(f_ & flagExpectCRLF)
|
||||
{
|
||||
// Treat the last CRLF in a chunk as
|
||||
// part of the next chunk, so p can
|
||||
// be parsed in one call instead of two.
|
||||
if(! parse_crlf(p))
|
||||
{
|
||||
ec = error::bad_chunk;
|
||||
return;
|
||||
}
|
||||
}
|
||||
eol = find_eol(p0 + skip_, pend, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! eol)
|
||||
{
|
||||
ec = error::need_more;
|
||||
skip_ = n - 1;
|
||||
return;
|
||||
}
|
||||
skip_ = static_cast<
|
||||
std::size_t>(eol - 2 - p0);
|
||||
|
||||
std::uint64_t v;
|
||||
if(! parse_hex(p, v))
|
||||
{
|
||||
ec = error::bad_chunk;
|
||||
return;
|
||||
}
|
||||
if(v != 0)
|
||||
{
|
||||
if(v > body_limit_)
|
||||
{
|
||||
ec = error::body_limit;
|
||||
return;
|
||||
}
|
||||
body_limit_ -= v;
|
||||
if(*p == ';')
|
||||
{
|
||||
// VFALCO TODO Validate extension
|
||||
impl().on_chunk(v, make_string(
|
||||
p, eol - 2), ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
else if(p == eol - 2)
|
||||
{
|
||||
impl().on_chunk(v, {}, ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
ec = error::bad_chunk;
|
||||
return;
|
||||
}
|
||||
len_ = v;
|
||||
skip_ = 2;
|
||||
p0 = eol;
|
||||
f_ |= flagExpectCRLF;
|
||||
state_ = state::chunk_body;
|
||||
return;
|
||||
}
|
||||
|
||||
f_ |= flagFinalChunk;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(n >= 5);
|
||||
if(f_ & flagExpectCRLF)
|
||||
BOOST_VERIFY(parse_crlf(p));
|
||||
std::uint64_t v;
|
||||
BOOST_VERIFY(parse_hex(p, v));
|
||||
eol = find_eol(p, pend, ec);
|
||||
BOOST_ASSERT(! ec);
|
||||
}
|
||||
|
||||
auto eom = find_eom(p0 + skip_, pend);
|
||||
if(! eom)
|
||||
{
|
||||
BOOST_ASSERT(n >= 3);
|
||||
skip_ = n - 3;
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
|
||||
if(*p == ';')
|
||||
{
|
||||
// VFALCO TODO Validate extension
|
||||
impl().on_chunk(0, make_string(
|
||||
p, eol - 2), ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
p = eol;
|
||||
parse_fields(p, eom, ec);
|
||||
if(ec)
|
||||
return;
|
||||
BOOST_ASSERT(p == eom);
|
||||
p0 = eom;
|
||||
|
||||
impl().on_complete(ec);
|
||||
if(ec)
|
||||
return;
|
||||
state_ = state::complete;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
inline
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
parse_chunk_body(char const*& p,
|
||||
std::size_t n, error_code& ec)
|
||||
{
|
||||
n = impl().on_data(string_view{p,
|
||||
beast::detail::clamp(len_, n)}, ec);
|
||||
p += n;
|
||||
len_ -= n;
|
||||
if(ec)
|
||||
return;
|
||||
if(len_ > 0)
|
||||
return;
|
||||
state_ = state::chunk_header;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
void
|
||||
basic_parser<isRequest, Derived>::
|
||||
do_field(field f,
|
||||
string_view value, error_code& ec)
|
||||
{
|
||||
// Connection
|
||||
if(f == field::connection ||
|
||||
f == field::proxy_connection)
|
||||
{
|
||||
auto const list = opt_token_list{value};
|
||||
if(! validate_list(list))
|
||||
{
|
||||
// VFALCO Should this be a field specific error?
|
||||
ec = error::bad_value;
|
||||
return;
|
||||
}
|
||||
for(auto const& s : list)
|
||||
{
|
||||
if(iequals({"close", 5}, s))
|
||||
{
|
||||
f_ |= flagConnectionClose;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(iequals({"keep-alive", 10}, s))
|
||||
{
|
||||
f_ |= flagConnectionKeepAlive;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(iequals({"upgrade", 7}, s))
|
||||
{
|
||||
f_ |= flagConnectionUpgrade;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ec.assign(0, ec.category());
|
||||
return;
|
||||
}
|
||||
|
||||
// Content-Length
|
||||
if(f == field::content_length)
|
||||
{
|
||||
if(f_ & flagContentLength)
|
||||
{
|
||||
// duplicate
|
||||
ec = error::bad_content_length;
|
||||
return;
|
||||
}
|
||||
|
||||
if(f_ & flagChunked)
|
||||
{
|
||||
// conflicting field
|
||||
ec = error::bad_content_length;
|
||||
return;
|
||||
}
|
||||
|
||||
std::uint64_t v;
|
||||
if(! parse_dec(
|
||||
value.begin(), value.end(), v))
|
||||
{
|
||||
ec = error::bad_content_length;
|
||||
return;
|
||||
}
|
||||
|
||||
if(v > body_limit_)
|
||||
{
|
||||
ec = error::body_limit;
|
||||
return;
|
||||
}
|
||||
|
||||
ec.assign(0, ec.category());
|
||||
len_ = v;
|
||||
f_ |= flagContentLength;
|
||||
return;
|
||||
}
|
||||
|
||||
// Transfer-Encoding
|
||||
if(f == field::transfer_encoding)
|
||||
{
|
||||
if(f_ & flagChunked)
|
||||
{
|
||||
// duplicate
|
||||
ec = error::bad_transfer_encoding;
|
||||
return;
|
||||
}
|
||||
|
||||
if(f_ & flagContentLength)
|
||||
{
|
||||
// conflicting field
|
||||
ec = error::bad_transfer_encoding;
|
||||
return;
|
||||
}
|
||||
|
||||
ec.assign(0, ec.category());
|
||||
auto const v = token_list{value};
|
||||
auto const p = std::find_if(v.begin(), v.end(),
|
||||
[&](typename token_list::value_type const& s)
|
||||
{
|
||||
return iequals({"chunked", 7}, s);
|
||||
});
|
||||
if(p == v.end())
|
||||
return;
|
||||
if(std::next(p) != v.end())
|
||||
return;
|
||||
len_ = 0;
|
||||
f_ |= flagChunked;
|
||||
return;
|
||||
}
|
||||
|
||||
// Upgrade
|
||||
if(f == field::upgrade)
|
||||
{
|
||||
ec.assign(0, ec.category());
|
||||
f_ |= flagUpgrade;
|
||||
return;
|
||||
}
|
||||
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user