0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-06-02 10:08:56 +02:00

ircd: Add additional comments / documentation.

This commit is contained in:
Jason Volk 2017-10-15 21:35:25 -07:00
parent df4915dec4
commit 61eb6d34df
3 changed files with 192 additions and 8 deletions

View file

@ -26,6 +26,43 @@
#pragma once
#define HAVE_IRCD_ALLOCATOR_H
/// Suite of custom allocator templates for special behavior and optimization
///
/// These tools can be used as alternatives to the standard allocator. Most
/// templates implement the std::allocator concept and can be used with
/// std:: containers by specifying them in the container's template parameter.
///
/// The C++ standard library assumes the availability of dynamic memory. This
/// is a reasonable assumption to enjoy a huge simplification of the std::
/// interface. Standard dynamic allocation can also be fast and unintrusive for
/// a vast majority of cases. Unfortunately STL container's use of allocators
/// can lead to a huge number of allocations and frees and in many trivial
/// circumstances which won't be optimized as well as they ought to be.
/// Furthermore, the std::allocator using `new` requires synchronization in
/// multi-threaded environments. tcmalloc may be used for IRCd but its presence
/// and performance should not be blindly assumed either. The fact remains,
/// containers are very nice to work with but there is room for optimizing
/// their backend through the allocator template.
///
/// The ircd::allocator::fixed is the prototypical justification for these
/// tools. It allows you to build a memory pool with a size known at compile-
/// time at the place of your choosing (i.e the stack) for any STL container. A
/// simple std::vector constructed on the stack with a fixed number of elements
/// known at construction time won't otherwise magically optimize away the
/// allocation for the elements; worse, a non-contiguous container like
/// std::list, std::map or std::set will conduct allocations for each modifying
/// operation. Having the fixed pool on the stack plugged into these containers
/// trivializes those requests and releases of memory.
///
/// The ircd::allocator::dynamic performs a single allocation of a contiguous
/// memory block with a size specified at runtime. This block can then be used
/// by a container.
///
/// The ircd::allocator::node is an interface for allowing one to manually deal
/// with the elements of an STL container similar to a C style container where
/// a "node" is constructed at the user's discretion and then inserted and
/// removed from the container.
///
namespace ircd::allocator
{
struct state;
@ -34,6 +71,16 @@ namespace ircd::allocator
template<class T> struct node;
};
/// Internal state structure for some of these tools. This is a very small and
/// simple interface to a bit array representing the availability of an element
/// in a pool of elements. The actual array of the proper number of bits must
/// be supplied by the user of the state. The allocator using this interface
/// can use any strategy to flip these bits but the default next()/allocate()
/// functions scan for the next available contiguous block of zero bits and
/// then wrap around when reaching the end of the array. Once a full iteration
/// of the array is made without finding satisfaction, an std::bad_alloc is
/// thrown.
///
struct ircd::allocator::state
{
using word_t = unsigned long long;
@ -63,6 +110,15 @@ struct ircd::allocator::state
{}
};
/// The fixed allocator creates a block of data with a size known at compile-
/// time. This structure itself is the state object for the actual allocator
/// instance used in the container. Create an instance of this structure,
/// perhaps on your stack. Then specify the ircd::allocator::fixed::allocator
/// in the template for the container. Then pass a reference to the state
/// object as an argument to the container when constructing. STL containers
/// have an overloaded constructor for this when specializing the allocator
/// template as we are here.
///
template<class T,
size_t max>
struct ircd::allocator::fixed
@ -82,6 +138,14 @@ struct ircd::allocator::fixed
{}
};
/// The actual allocator template as used by the container.
///
/// This has to be a very light, small and copyable object which cannot hold
/// our actual memory or state (lest we just use dynamic allocation for that!)
/// which means we have to pass this a reference to our ircd::allocator::fixed
/// instance. We can do that through the container's custom-allocator overload
/// at its construction.
///
template<class T,
size_t size>
struct ircd::allocator::fixed<T, size>::allocator
@ -157,6 +221,14 @@ allocator()
return { *this };
}
/// The dynamic allocator provides a pool of a fixed size known at runtime.
///
/// This allocator conducts a single new and delete for a pool allowing an STL
/// container to operate without interacting with the rest of the system and
/// without fragmentation. This is not as useful as the allocator::fixed in
/// practice as the standard allocator is as good as this in many cases. This
/// is still available as an analog to the fixed allocator in this suite.
///
template<class T>
struct ircd::allocator::dynamic
:state
@ -188,6 +260,10 @@ struct ircd::allocator::dynamic
}
};
/// The actual template passed to containers for using the dynamic allocator.
///
/// See the notes for ircd::allocator::fixed::allocator for details.
///
template<class T>
struct ircd::allocator::dynamic<T>::allocator
{
@ -260,6 +336,23 @@ allocator()
return { *this };
}
/// Allows elements of an STL container to be manually handled by the user.
///
/// C library containers usually allow the user to manually construct a node
/// and then insert it and remove it from the container. With STL containers
/// we can use devices like allocator::fixed, but what if we don't want to have
/// a bound on the allocator's size either at compile time or at runtime? What
/// if we simply want to manually handle the container's elements, like on the
/// stack, and in different frames, and then manipulate the container -- or
/// even better and safer: have the elements add and remove themselves while
/// storing the container's node data too?
///
/// This device helps the user achieve that by simply providing a variable
/// set by the user indicating where the 'next' block of memory is when the
/// container requests it. Whether the container is requesting memory which
/// should be fulfilled by that 'next' block must be ensured and asserted by
/// the user, but this is likely the case.
///
template<class T>
struct ircd::allocator::node
{
@ -271,6 +364,10 @@ struct ircd::allocator::node
node() = default;
};
/// The actual template passed to containers for using the allocator.
///
/// See the notes for ircd::allocator::fixed::allocator for details.
///
template<class T>
struct ircd::allocator::node<T>::allocator
{

View file

@ -86,8 +86,20 @@ class ircd::fmt::specifier
// User API
//
// * The arguments are not restricted by stdarg limitations. You can pass a real std::string.
// * The function participates in the custom protocol-safe ruleset.
/// Typesafe snprintf() from formal grammar and RTTI.
///
/// This function accepts a format string and a variable number of arguments
/// composing formatted null-terminated output in the provided output buffer.
/// The format string is compliant with standard snprintf() (TODO: not yet).
/// The type information of the arguments is grabbed from the variadic template
/// and references are passed to the formal output grammars. This means you can
/// pass an std::string directly without calling c_str(), as well as pass a
/// non-null-terminated string_view safely.
///
/// Furthermore, other features of ircd::fmt enable custom format specifiers
/// and handling of types not recognized by existing grammars through this
/// function.
///
class ircd::fmt::snprintf
{
const char *fstart; // Current running position in the fmtstr
@ -137,6 +149,17 @@ struct ircd::fmt::sprintf
}{}
};
/// A complement to fmt::snprintf() accepting an already-made va_rtti.
///
/// This function has no variadic template; instead it accepts the type
/// which would be composed by such a variadic template called
/// ircd::va_rtti directly.
///
/// ircd::va_rtti is a lightweight pairing of argument pointers to runtime
/// type indexes. ircd::va_rtti is not a template itself because its purpose
/// is to bring this type information out of the header files to where the
/// grammar is instantiated.
///
struct ircd::fmt::vsnprintf
:snprintf
{

View file

@ -95,6 +95,7 @@ enum ircd::http::code
INSUFFICIENT_STORAGE = 507,
};
/// Root exception for HTTP.
struct ircd::http::error
:ircd::error
{
@ -104,6 +105,13 @@ struct ircd::http::error
error(const enum code &, std::string content = {});
};
/// Represents a single \r\n delimited line used in HTTP.
///
/// This object is just a string_view of that line. The actual data backing
/// that view is the responsibility of the user. This object is constructed
/// with an ircd::parse::capstan argument which is used by the formal grammar
/// in the constructor.
///
struct ircd::http::line
:string_view
{
@ -120,6 +128,14 @@ namespace ircd::http
using header = line::header;
}
/// Represents a 'request line' or the first line a client sends to a server.
///
/// This is a dual-use class. For HTTP clients, one may simply connect the
/// members to the proper strings and then pass this structure to a function
/// making a client request. For HTTP servers, pass an http::line to the ctor
/// and the formal grammar will set the members appropriately. The actual data
/// behind these members is the responsibility of the user.
///
struct ircd::http::line::request
{
string_view method;
@ -132,6 +148,13 @@ struct ircd::http::line::request
request() = default;
};
/// Represents a 'response line' or the first line a server sends to a client.
///
/// This is a dual-use class and symmetric to the http::line::request class.
/// Servers may set the members and then use this object to respond to a client
/// while clients should provide an http::line to the constructor which will
/// fill in the members.
///
struct ircd::http::line::response
{
string_view version;
@ -142,6 +165,11 @@ struct ircd::http::line::response
response() = default;
};
/// Represents a single key/value pair in a query string.
///
/// This is used by the ircd::http::query::string object when parsing query
/// strings.
///
struct ircd::http::query
:std::pair<string_view, string_view>
{
@ -154,8 +182,14 @@ struct ircd::http::query
query() = default;
};
// Query string is read as a complete string off the tape (into request.query) and
// not parsed further. To make queries into that string use this class to view it.
/// Tool for parsing an HTTP query string.
///
/// Query string is read as a complete string off the tape (into request.query)
/// and not parsed further. To make queries into that string use this class to
/// view it. Once this object is constructed by viewing the whole query string,
/// the member functions invoke the formal grammar to get individual key/value
/// pairs.
///
struct ircd::http::query::string
:string_view
{
@ -168,6 +202,12 @@ struct ircd::http::query::string
using string_view::string_view;
};
/// Represents an HTTP header key/value pair.
///
/// This is a dual-use class. Those sending headers will simply fill in the
/// components of the std::pair. Those receiving headers can pass the ctor an
/// ircd::http::line which will construct the pair using the formal grammars.
///
struct ircd::http::line::header
:std::pair<string_view, string_view>
{
@ -179,7 +219,12 @@ struct ircd::http::line::header
header() = default;
};
// HTTP headers are read once off the tape and proffered to the closure.
/// This device allows parsing HTTP headers directly off the wire without state
///
/// The constructor of this object contains the grammar to read HTTP headers
/// from the capstan and then proffer them one by one to the provided closure,
/// that's all it does.
///
struct ircd::http::headers
:string_view
{
@ -189,9 +234,16 @@ struct ircd::http::headers
headers(parse::capstan &, const closure & = {});
};
// Use the request::content / response::content wrappers. They ensure the proper amount
// of content is read and the tape is in the right position for the next request
// with exception safety.
/// Represents the content of an HTTP request after the head.
///
/// Use the request::content / response::content wrappers. They ensure the
/// proper amount of content is read and the tape is in the right position
/// for the next request with exception safety. In other words, this object
/// ensures the capstan is in the proper place for the next request no matter
/// what happens; whether an exception happened, or whether the user simply
/// didn't care to read the content. The capstan MUST advance Content-Length
/// bytes in any case.
///
struct ircd::http::content
:string_view
{
@ -204,6 +256,8 @@ struct ircd::http::content
content() = default;
};
/// HTTP response suite. Functionality to send and receive responses.
///
struct ircd::http::response
{
struct head;
@ -247,6 +301,8 @@ struct ircd::http::response::chunked::chunk
chunk(chunked &, const const_buffer &);
};
/// Represents an HTTP response head. This is for receiving responses only.
///
struct ircd::http::response::head
:line::response
{
@ -258,6 +314,8 @@ struct ircd::http::response::head
head(parse::capstan &pc, const headers::closure &c = {});
};
/// Represents an HTTP response content. This is for receiving only.
///
struct ircd::http::response::content
:http::content
{
@ -276,6 +334,8 @@ struct ircd::http::response::content
content() = default;
};
/// HTTP request suite. Functionality to send and receive requests.
///
struct ircd::http::request
{
struct head;
@ -300,6 +360,8 @@ struct ircd::http::request
const headers::closure & = {});
};
/// Represents an HTTP request head. This is only for receiving requests.
///
struct ircd::http::request::head
:line::request
{
@ -314,6 +376,8 @@ struct ircd::http::request::head
head(parse::capstan &pc, const headers::closure &c = {});
};
/// Represents an HTTP request content. This is only for receiving content.
///
struct ircd::http::request::content
:http::content
{