diff options
Diffstat (limited to 'web/server/h2o/libh2o/deps/libyrmcds')
26 files changed, 7284 insertions, 0 deletions
diff --git a/web/server/h2o/libh2o/deps/libyrmcds/.gitignore b/web/server/h2o/libh2o/deps/libyrmcds/.gitignore new file mode 100644 index 000000000..12c3d2512 --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/.gitignore @@ -0,0 +1,13 @@ +# C/C++ +*.[oa] + +# Editors +*~ +.*.swp + +# misc +yc +yc-cnt +html +lz4 +t/*.exe diff --git a/web/server/h2o/libh2o/deps/libyrmcds/.travis.yml b/web/server/h2o/libh2o/deps/libyrmcds/.travis.yml new file mode 100644 index 000000000..a1fa4fac6 --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/.travis.yml @@ -0,0 +1,16 @@ +language: c +os: + - linux + - osx +compiler: + - gcc + - clang +services: + - memcached +before_install: + - if [ "$TRAVIS_OS_NAME" = osx ]; then brew update; fi + - if [ "$TRAVIS_OS_NAME" = osx ]; then brew install memcached; fi + - if [ "$TRAVIS_OS_NAME" = osx ]; then /usr/local/opt/memcached/bin/memcached -d; fi +script: + - make + - env YRMCDS_HOST=localhost make test diff --git a/web/server/h2o/libh2o/deps/libyrmcds/COPYING b/web/server/h2o/libh2o/deps/libyrmcds/COPYING new file mode 100644 index 000000000..c3869aaae --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/COPYING @@ -0,0 +1,25 @@ +Copyright (c) 2013-2015, Cybozu et al. +Copyright belongs to the authors or to the authors' employers of +each contribution. Refer to https://github.com/cybozu/libyrmcds/commits/ + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/web/server/h2o/libh2o/deps/libyrmcds/Doxyfile b/web/server/h2o/libh2o/deps/libyrmcds/Doxyfile new file mode 100644 index 000000000..4c8617ccf --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/Doxyfile @@ -0,0 +1,1748 @@ +# Doxyfile 1.7.6.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = "libyrmcds" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "memcached/yrmcds client library for C" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = YES + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be +# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given +# their name and scope. Since this can be an expensive process and often the +# same symbol appear multiple times in the code, doxygen keeps a cache of +# pre-resolved symbols. If the cache is too small doxygen will become slower. +# If the cache is too large, memory is wasted. The cache size is given by this +# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command <command> <input-file>, where <command> is the value of +# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = yrmcds.h + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# style sheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters"> +# Qt Help Project / Custom Filters</a>. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes"> +# Qt Help Project / Filter Attributes</a>. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the +# mathjax.org site, so you can quickly see the result without installing +# MathJax, but it is strongly recommended to install a local copy of MathJax +# before deployment. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = YES + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/web/server/h2o/libh2o/deps/libyrmcds/Makefile b/web/server/h2o/libh2o/deps/libyrmcds/Makefile new file mode 100644 index 000000000..d8614c047 --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/Makefile @@ -0,0 +1,87 @@ +# Makefile for libyrmcds + +PREFIX = /usr/local + +CC = gcc +CXX = g++ -std=gnu++11 +CPPFLAGS = -D_GNU_SOURCE + +# Uncomment the next line to remove the internal lock used to +# serialize sending commands. +# +#CPPFLAGS += -DLIBYRMCDS_NO_INTERNAL_LOCK + +OPTFLAGS = -gdwarf-3 -O2 +CFLAGS = -Wall -Wconversion $(OPTFLAGS) +CXXFLAGS = $(CFLAGS) -Wnon-virtual-dtor -Woverloaded-virtual +LDFLAGS = -L. +LDLIBS = -lyrmcds -lpthread + +EXE = yc yc-cnt +LIB = libyrmcds.a +PACKAGES = build-essential subversion doxygen + +CHEADERS = $(wildcard *.h) +CSOURCES = $(wildcard *.c) +COBJECTS = $(patsubst %.c,%.o,$(CSOURCES)) +LIB_OBJECTS = $(filter-out yc.o yc-cnt.o,$(COBJECTS)) +TEST_SOURCES = $(wildcard t/*.c) +TESTS = $(patsubst %.c,%,$(TEST_SOURCES)) + +all: lib $(EXE) +lib: $(LIB) + +# LZ4 is optional. Run "make lz4; make" to build LZ4 enabled library. +LZ4_TAG = r127 +WGET = wget -q -P lz4/lib +lz4: + mkdir -p lz4/lib + $(WGET) https://raw.githubusercontent.com/Cyan4973/lz4/$(LZ4_TAG)/lib/lz4.c + $(WGET) https://raw.githubusercontent.com/Cyan4973/lz4/$(LZ4_TAG)/lib/lz4.h + +ifeq ($(wildcard lz4), lz4) +$(info LZ4 transparent compression is *enabled*) +CPPFLAGS += -DLIBYRMCDS_USE_LZ4 +LZ4_CFLAGS = -std=c99 -O3 +lz4/lib/lz4.o: lz4/lib/lz4.c + $(CC) $(LZ4_CFLAGS) -Ilz4/lib -c -o $@ $< +LIB_OBJECTS += lz4/lib/lz4.o +endif + +yc: yc.o + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LDLIBS) + +yc-cnt: yc-cnt.o + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LDLIBS) + +$(COBJECTS): $(CHEADERS) +$(EXE): $(LIB) + +$(LIB): $(LIB_OBJECTS) + $(AR) rcs $@ $^ + +t/%.exe: t/%.c $(LIB) + $(CC) -I$(shell pwd) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LDLIBS) + +$(TESTS): $(LIB) + @$(MAKE) -s $@.exe + @echo Running ./$@.exe + @./$@.exe + @echo + +test: $(TESTS) + +html: + rm -rf html + doxygen + +serve: html + @cd html; python -m SimpleHTTPServer 8888 || true + +clean: + rm -rf *.o t/*.exe html $(EXE) $(LIB) + +setup: + sudo apt-get install -y $(PACKAGES) + +.PHONY: all lib test html serve clean setup diff --git a/web/server/h2o/libh2o/deps/libyrmcds/README.md b/web/server/h2o/libh2o/deps/libyrmcds/README.md new file mode 100644 index 000000000..c6683fdfa --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/README.md @@ -0,0 +1,78 @@ +[![Build Status](https://travis-ci.org/cybozu/libyrmcds.png)](https://travis-ci.org/cybozu/libyrmcds) +libyrmcds +========= + +libyrmcds is a [memcached][] client library written in C. +This is a companion to [yrmcds][], a memcached compatible KVS. + +In addition to the library itself, a client program called `yc` is included. + +Features +-------- + +* Minimalistic. + + libyrmcds does *not* provide any rich features like consistent hashing. + Instead, it can be used as a base library to implement such rich + features. + +* Designed for binary protocol. + + In order to access the true power of the binary protocol, libyrmcds + is designed primarily for binary protocol. Limited support for the + text protocol is provided, though. + +* Support for [yrmcds][] extensions. + + Specifically, [the server-side locking][locking] and [the counter extension][counter] is supported. + +* Separated send / recv operations. + + Although the socket used in libyrmcds is blocking, receiving results + from the server is separated from the sending operations. You can + even use a different thread to receive results asynchronously. + +* Optional compression with [LZ4][]. + + Large objects can be transparently compressed/uncompressed with + [LZ4][] compression algorithm. + +Build +----- + +Just run `make`. + +To support [transparent LZ4 compression][compress], obtain LZ4 source +code and rebuild the library as follows: + +``` +$ make lz4 +$ make clean; make +``` + +Install +------- + +Place `yrmcds.h` and `libyrmcds.a` to appropriate directories. + +Usage +----- + +See [USAGE.md](USAGE.md). + +Authors & Contributors +---------------------- + +* Yamamoto, Hirotaka [@ymmt2005](https://github.com/ymmt2005) +* Nojima, Yusuke [@nojima](https://github.com/nojima) +* Tanuma, Shuhei [@chobie](https://github.com/chobie) +* Oku, Kazuho [@kazuho](https://github.com/kazuho) +* Fazal Majid [@fazalmajid](https://github.com/fazalmajid) + +[memcached]: http://memcached.org/ +[yrmcds]: http://cybozu.github.io/yrmcds/ +[binprot]: https://code.google.com/p/memcached/wiki/BinaryProtocolRevamped +[locking]: https://github.com/cybozu/yrmcds/blob/master/docs/locking.md +[counter]: https://github.com/cybozu/yrmcds/blob/master/docs/counter.md +[LZ4]: https://code.google.com/p/lz4/ +[compress]: USAGE.md#transparent-compression diff --git a/web/server/h2o/libh2o/deps/libyrmcds/USAGE.md b/web/server/h2o/libh2o/deps/libyrmcds/USAGE.md new file mode 100644 index 000000000..408711937 --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/USAGE.md @@ -0,0 +1,221 @@ +Usage +===== + +`yc` +---- + +`yc` is a memcached/yrmcds client program. It provides access to all +libyrmcds library functions therefore access to all server functions. + +`yc` prints out its usage when invoked without command-line arguments. + +Minimal example +--------------- + +```c +#include <yrmcds.h> + +#include <errno.h> +#include <error.h> +#include <stdlib.h> +#include <stdio.h> + +void check_error(yrmcds_error e) { + if( e != 0 ) { + if( e == YRMCDS_SYSTEM_ERROR ) { + error(0, errno, "system error"); + } else { + fprintf(stderr, "yrmcds error: %s\n", yrmcds_strerror(e)); + } + exit(2); + } +} + +void check_response(const yrmcds_response* r) { + if( r->status != YRMCDS_STATUS_OK ) { + fprintf(stderr, "Command failed: 0x%04x %.*s\n", + r->status, (int)r->data_len, r->data); + exit(3); + } +} + +int main(int argc, char** argv) { + yrmcds c; + yrmcds_response r; + + check_error( yrmcds_connect(&c, "localhost", 11211) ); + check_error( yrmcds_noop(&c, NULL) ); + check_error( yrmcds_recv(&c, &r) ); + check_response(&r); + check_error( yrmcds_close(&c) ); + + return 0; +} +``` + +Compilation and linking +----------------------- + +Link with `-lyrmcds -lpthread` as follows: + +``` +gcc -g -O2 -o foo foo.c -lyrmcds -lpthread +``` + +Request serial number +--------------------- + +All command sending functions can issue a unique serial number of the +command. This can be used when you receive responses asynchronously +as described in the next section. + +```c +uint32_t async_cas(yrmcds* c) { + uint32_t serial; + // try compare-and-swap + check_error( yrmcds_set(&c, "abc", 3, "12345", 5, 0, 0, 5, 0, &serial) ); + return serial; +} +``` + +Multi-threading +--------------- + +All functions in libyrmcds are thread-safe as long as different `yrmcds` +structs are used. Additionally, any number of threads can use command +sending functions such as `yrmcds_get()` or `yrmcds_set()` even when +they share the same `yrmcds` struct. + +Further, `yrmcds_recv()` can be used in parallel with command sending +functions. You can create a dedicated thread to receive server responses +asynchronously. Use request serial numbers to identify a response's +request. + +```c +void async_recv(yrmcds* c, void (*notify)(uint32_t, yrmcds_response* r)) { + yrmcds_respone r; + while( 1 ) { + check_error( yrmcds_recv(&c, &r) ); + (*notify)(r.serial, &r); + } +} +``` + +You can use `yrmcds_shutdown()` to interrupt the thread blocking in +`yrmcds_recv()` for graceful exit. + + +Transparent compression +----------------------- + +libyrmcds provides optional transparent data compression by [LZ4][]. + +To use this feature, the library must be built with LZ4 as follows: + +``` +$ make lz4 +$ make +``` + +If the library supports LZ4 compression, you can enable transparent +LZ4 (de)compression for large objects. The threshold for compression +can be set by `yrmcds_set_compression()`. The compression is disabled +by default. + +Note that all clients must support and enable the compression to +properly handle compressed data. + +Text protocol support +--------------------- + +Although libyrmcds is designed for binary protocol, yet it provides +limited access to text protocol commands. Use `yrmcds_text_mode()` +to put the connection into the text protocol mode: + +```c + check_error( yrmcds_connect(&c, "localhost", 11211) ); + check_error( yrmcds_text_mode(&c) ); + + // Use the library API just in the same way as binary protocol. +``` + +Limitations: + +* Only subset of commands can be used. + + `yrmcds_get_touch`, lock related functions, `yrmcds_incr2` and + `yrmcds_decr2`, stat functions, key dump extension are not supported. + +* Quiet mutation cannot be used. + + `quiet` arguments must be 0. You need to receive a response for + every command. + +* `yrmcds_response::cas_unique` is always 0 for storage commands. + + The response for `yrmcds_set`, `yrmcds_add`, or `yrmcds_replace` + will not bring the correct CAS value. Use `yrmcds_get` or + `yrmcds_getk` to retrieve the correct value. + +* `yrmcds_response::command` value is dummy. + + Because `command` is one of binary protocol command numbers. + +Counter extension +----------------- + +yrmcds has a distributed counter extension for resource management. +If the extension is enabled in server, you can access counters by `yrmcds_cnt_*` functions. +The usage of each function is very similar to the corresponding `yrmcds_` function. + +The minimum example is: + +```c +#include <yrmcds.h> + +#include <errno.h> +#include <error.h> +#include <stdio.h> +#include <stdlib.h> + +void check_error(yrmcds_error e) { + if( e != YRMCDS_OK ) { + if( e == YRMCDS_SYSTEM_ERROR ) { + error(0, errno, "system error"); + } else { + fprintf(stderr, "yrmcds error: %s\n", yrmcds_strerror(e)); + } + exit(2); + } +} + +void check_response(const yrmcds_cnt_response* r) { + if( r->status != YRMCDS_STATUS_OK ) { + fprintf(stderr, "Command failed: 0x%02x %.*s\n", + r->status, (int)r->body_length, r->body); + exit(3); + } +} + +int main(void) { + yrmcds_cnt c; + yrmcds_cnt_response r; + + check_error( yrmcds_cnt_connect(&c, "localhost", 11215) ); + check_error( yrmcds_cnt_noop(&c, NULL) ); + check_error( yrmcds_cnt_recv(&c, &r) ); + check_response(&r); + check_error( yrmcds_cnt_close(&c) ); + + return 0; +} +``` + +API documents +------------- + +HTML documents generated with Doxygen is available [here][api]. + + +[api]: http://cybozu.github.io/libyrmcds/html/ +[LZ4]: https://code.google.com/p/lz4/ diff --git a/web/server/h2o/libh2o/deps/libyrmcds/close.c b/web/server/h2o/libh2o/deps/libyrmcds/close.c new file mode 100644 index 000000000..d19ae7659 --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/close.c @@ -0,0 +1,24 @@ +// (C) 2013 Cybozu. + +#include "yrmcds.h" + +#include <stdlib.h> +#include <unistd.h> + +yrmcds_error yrmcds_close(yrmcds* c) { + if( c == NULL ) + return YRMCDS_BAD_ARGUMENT; + if( c->sock == -1 ) + return YRMCDS_OK; + + close(c->sock); + c->sock = -1; +#ifndef LIBYRMCDS_NO_INTERNAL_LOCK + pthread_mutex_destroy(&(c->lock)); +#endif + free(c->recvbuf); + c->recvbuf = NULL; + free(c->decompressed); + c->decompressed = NULL; + return YRMCDS_OK; +} diff --git a/web/server/h2o/libh2o/deps/libyrmcds/connect.c b/web/server/h2o/libh2o/deps/libyrmcds/connect.c new file mode 100644 index 000000000..8ddf770c6 --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/connect.c @@ -0,0 +1,199 @@ +// (C) 2013, 2016 Cybozu. + +#include "yrmcds.h" + +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <poll.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +// workaround for a known-bug in NetBSD, from https://lists.gnu.org/archive/html/bug-gnulib/2010-02/msg00071.html +#ifndef AI_V4MAPPED +#define AI_V4MAPPED 0 +#endif + +static yrmcds_error connect_to_server(const char* node, uint16_t port, int* server_fd) { + if( node == NULL ) + return YRMCDS_BAD_ARGUMENT; + + long fl; + char sport[8]; + snprintf(sport, sizeof(sport), "%u", (unsigned int)port); + + struct addrinfo hint, *res; + memset(&hint, 0, sizeof(hint)); + hint.ai_family = AF_INET; // prefer IPv4 + hint.ai_socktype = SOCK_STREAM; + hint.ai_flags = AI_NUMERICSERV|AI_ADDRCONFIG; + int e = getaddrinfo(node, sport, &hint, &res); + if( e == EAI_FAMILY || e == EAI_NONAME +#ifdef EAI_ADDRFAMILY + || e == EAI_ADDRFAMILY +#endif +#ifdef EAI_NODATA + || e == EAI_NODATA +#endif + ) { + hint.ai_family = AF_INET6; + // intentionally drop AI_ADDRCONFIG to support IPv6 link-local address. + // see https://github.com/cybozu/yrmcds/issues/40 + hint.ai_flags = AI_NUMERICSERV|AI_V4MAPPED; + e = getaddrinfo(node, sport, &hint, &res); + } + if( e == EAI_SYSTEM ) { + return YRMCDS_SYSTEM_ERROR; + } else if( e != 0 ) { + return YRMCDS_NOT_RESOLVED; + } + + int s = socket(res->ai_family, + res->ai_socktype +#ifdef __linux__ + | SOCK_NONBLOCK | SOCK_CLOEXEC +#endif + , res->ai_protocol); + if( s == -1 ) { + e = errno; + freeaddrinfo(res); + errno = e; + return YRMCDS_SYSTEM_ERROR; + } +#ifndef __linux__ + fl = fcntl(s, F_GETFD, 0); + fcntl(s, F_SETFD, fl | FD_CLOEXEC); + fl = fcntl(s, F_GETFL, 0); + fcntl(s, F_SETFL, fl | O_NONBLOCK); +#endif + e = connect(s, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + if( e == -1 && errno != EINPROGRESS ) { + e = errno; + close(s); + errno = e; + return YRMCDS_SYSTEM_ERROR; + } + + if( e != 0 ) { + struct pollfd fds; + fds.fd = s; + fds.events = POLLOUT; + int n = poll(&fds, 1, 5000); + if( n == 0 ) { // timeout + close(s); + return YRMCDS_TIMEOUT; + } + if( n == -1 ) { + e = errno; + close(s); + errno = e; + return YRMCDS_SYSTEM_ERROR; + } + + if( fds.revents & (POLLERR|POLLHUP|POLLNVAL) ) { + close(s); + return YRMCDS_DISCONNECTED; + } + socklen_t l = sizeof(e); + if( getsockopt(s, SOL_SOCKET, SO_ERROR, &e, &l) == -1 ) { + close(s); + return YRMCDS_SYSTEM_ERROR; + } + if( e != 0 ) { + close(s); + errno = e; + return YRMCDS_SYSTEM_ERROR; + } + } + fl = fcntl(s, F_GETFL, 0); + if( fcntl(s, F_SETFL, fl & ~O_NONBLOCK) == -1 ) { + e = errno; + close(s); + errno = e; + return YRMCDS_SYSTEM_ERROR; + } + int ok = 1; + if( setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &ok, sizeof(ok)) == -1 ) { + e = errno; + close(s); + errno = e; + return YRMCDS_SYSTEM_ERROR; + } + *server_fd = s; + return YRMCDS_OK; +} + +yrmcds_error yrmcds_connect(yrmcds* c, const char* node, uint16_t port) { + if( c == NULL ) + return YRMCDS_BAD_ARGUMENT; +#ifndef LIBYRMCDS_NO_INTERNAL_LOCK + int e = pthread_mutex_init(&(c->lock), NULL); + if( e != 0 ) { + errno = e; + return YRMCDS_SYSTEM_ERROR; + } +#endif // ! LIBYRMCDS_NO_INTERNAL_LOCK + int server_fd; + yrmcds_error err = connect_to_server(node, port, &server_fd); + if( err != YRMCDS_OK ) + return err; + c->sock = server_fd; + c->serial = 0; + c->compress_size = 0; + c->recvbuf = (char*)malloc(1 << 20); + if( c->recvbuf == NULL ) { + close(server_fd); +#ifndef LIBYRMCDS_NO_INTERNAL_LOCK + pthread_mutex_destroy(&(c->lock)); +#endif + return YRMCDS_OUT_OF_MEMORY; + } + c->capacity = 1 << 20; + c->used = 0; + c->last_size = 0; + c->decompressed = NULL; + c->invalid = 0; + c->text_mode = 0; + c->rserial = 0; + return YRMCDS_OK; +} + +yrmcds_error yrmcds_cnt_connect(yrmcds_cnt* c, const char* node, uint16_t port) { + if( c == NULL ) + return YRMCDS_BAD_ARGUMENT; +#ifndef LIBYRMCDS_NO_INTERNAL_LOCK + int e = pthread_mutex_init(&(c->lock), NULL); + if( e != 0 ) { + errno = e; + return YRMCDS_SYSTEM_ERROR; + } +#endif // ! LIBYRMCDS_NO_INTERNAL_LOCK + int server_fd; + yrmcds_error err = connect_to_server(node, port, &server_fd); + if( err != YRMCDS_OK ) + return err; + c->sock = server_fd; + c->serial = 0; + c->recvbuf = (char*)malloc(4096); + if( c->recvbuf == NULL ) { + close(server_fd); +#ifndef LIBYRMCDS_NO_INTERNAL_LOCK + pthread_mutex_destroy(&(c->lock)); +#endif + return YRMCDS_OUT_OF_MEMORY; + } + c->capacity = 4096; + c->used = 0; + c->last_size = 0; + c->invalid = 0; + c->stats.count = c->stats.capacity = 0; + c->stats.records = NULL; + return YRMCDS_OK; +} diff --git a/web/server/h2o/libh2o/deps/libyrmcds/counter.c b/web/server/h2o/libh2o/deps/libyrmcds/counter.c new file mode 100644 index 000000000..748a23108 --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/counter.c @@ -0,0 +1,408 @@ +// (C) 2013-2015 Cybozu et al. + +#include "yrmcds.h" +#include "yrmcds_portability.h" + +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <netdb.h> +#include <netinet/tcp.h> +#include <poll.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <unistd.h> + +static const size_t HEADER_SIZE = 12; +static const size_t RECV_SIZE = 4096; +static const size_t INITIAL_STATS_CAPACITY = 16; + +static inline void hton32(uint32_t i, char* p) { + uint32_t n = htobe32(i); + memcpy(p, &n, sizeof(n)); +} + +static inline void hton16(uint16_t i, char* p) { + uint16_t n = htobe16(i); + memcpy(p, &n, sizeof(n)); +} + +static inline uint32_t ntoh32(const char* p) { + uint32_t n; + memcpy(&n, p, sizeof(n)); + return be32toh(n); +} + +static inline uint16_t ntoh16(const char* p) { + uint16_t n; + memcpy(&n, p, sizeof(n)); + return be16toh(n); +} + +yrmcds_error +yrmcds_cnt_set_timeout(yrmcds_cnt* c, int timeout) { + if( c == NULL || timeout < 0 ) + return YRMCDS_BAD_ARGUMENT; + + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + + if( setsockopt(c->sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1 ) + return YRMCDS_SYSTEM_ERROR; + if( setsockopt(c->sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1 ) + return YRMCDS_SYSTEM_ERROR; + return YRMCDS_OK; +} + +yrmcds_error +yrmcds_cnt_close(yrmcds_cnt* c) { + if( c == NULL ) + return YRMCDS_BAD_ARGUMENT; + if( c->sock == -1 ) + return YRMCDS_OK; + + close(c->sock); + c->sock = -1; +#ifndef LIBYRMCDS_NO_INTERNAL_LOCK + pthread_mutex_destroy(&(c->lock)); +#endif + free(c->recvbuf); + c->recvbuf = NULL; + free(c->stats.records); + c->stats.records = NULL; + return YRMCDS_OK; +} + +yrmcds_error +yrmcds_cnt_shutdown(yrmcds_cnt* c) { + if( c == NULL ) + return YRMCDS_BAD_ARGUMENT; + if( shutdown(c->sock, SHUT_RD) == -1 ) + return YRMCDS_SYSTEM_ERROR; + return YRMCDS_OK; +} + +int +yrmcds_cnt_fileno(yrmcds_cnt* c) { + return c->sock; +} + +static yrmcds_error +recv_data(yrmcds_cnt* c) { + if( (c->capacity - c->used) < RECV_SIZE ) { + size_t new_capacity = c->capacity * 2; + char* new_buffer = (char*)realloc(c->recvbuf, new_capacity); + if( new_buffer == NULL ) + return YRMCDS_OUT_OF_MEMORY; + c->recvbuf = new_buffer; + c->capacity = new_capacity; + } + + ssize_t n; + AGAIN: + n = recv(c->sock, c->recvbuf + c->used, RECV_SIZE, 0); + if( n == -1 ) { + if( errno == EINTR ) goto AGAIN; + return YRMCDS_SYSTEM_ERROR; + } + if( n == 0 ) + return YRMCDS_DISCONNECTED; + c->used += (size_t)n; + return YRMCDS_OK; +} + +static yrmcds_error +append_stat(yrmcds_cnt_statistics* s, + uint16_t name_len, uint16_t value_len, + const char* name, const char* value) { + if( s->count == s->capacity ) { + size_t new_capacity = s->capacity * 2; + if( new_capacity < INITIAL_STATS_CAPACITY ) + new_capacity = INITIAL_STATS_CAPACITY; + yrmcds_cnt_stat* new_records = + realloc(s->records, sizeof(yrmcds_cnt_stat) * new_capacity); + if( new_records == NULL ) + return YRMCDS_OUT_OF_MEMORY; + s->capacity = new_capacity; + s->records = new_records; + } + + s->records[s->count].name_length = name_len; + s->records[s->count].value_length = value_len; + s->records[s->count].name = name; + s->records[s->count].value = value; + s->count += 1; + return YRMCDS_OK; +} + +static yrmcds_error +parse_statistics(yrmcds_cnt* c, const yrmcds_cnt_response* r) { + yrmcds_cnt_statistics* s = &c->stats; + s->count = 0; + + const char* p = r->body; + const char* end = r->body + r->body_length; + while( p < end ) { + if( p + 4 > end ) + return YRMCDS_PROTOCOL_ERROR; + uint16_t name_len = ntoh16(p); + uint16_t value_len = ntoh16(p + 2); + if( p + 4 + name_len + value_len > end ) + return YRMCDS_PROTOCOL_ERROR; + yrmcds_error err = + append_stat(s, name_len, value_len, p + 4, p + 4 + name_len); + if( err != YRMCDS_OK ) + return err; + p += 4 + name_len + value_len; + } + return YRMCDS_OK; +} + +static yrmcds_error +parse_dump_record(yrmcds_cnt* c, yrmcds_cnt_response* r) { + if( r->body_length == 0 ) { + // End of dump + return YRMCDS_OK; + } + if( r->body_length < 10 ) { + c->invalid = 1; + return YRMCDS_PROTOCOL_ERROR; + } + r->current_consumption = ntoh32(r->body); + r->max_consumption = ntoh32(r->body + 4); + r->name_length = ntoh16(r->body + 8); + if( r->body_length < 10 + r->name_length ) { + c->invalid = 1; + return YRMCDS_PROTOCOL_ERROR; + } + r->name = r->body + 10; + return YRMCDS_OK; +} + +yrmcds_error +yrmcds_cnt_recv(yrmcds_cnt* c, yrmcds_cnt_response* r) { + if( c == NULL || r == NULL ) + return YRMCDS_BAD_ARGUMENT; + if( c->invalid ) + return YRMCDS_PROTOCOL_ERROR; + + if( c->last_size > 0 ) { + size_t remain = c->used - c->last_size; + if( remain > 0 ) + memmove(c->recvbuf, c->recvbuf + c->last_size, remain); + c->used = remain; + c->last_size = 0; + } + + while( c->used < HEADER_SIZE ) { + yrmcds_error e = recv_data(c); + if( e != YRMCDS_OK ) return e; + } + + if( (uint8_t)c->recvbuf[0] != 0x91 ) { + c->invalid = 1; + return YRMCDS_PROTOCOL_ERROR; + } + + r->command = (yrmcds_cnt_command)c->recvbuf[1]; + r->status = (yrmcds_cnt_status)c->recvbuf[2]; + r->body_length = ntoh32(c->recvbuf + 4); + memcpy(&r->serial, c->recvbuf + 8, sizeof(r->serial)); + r->body = NULL; + r->resources = 0; + r->current_consumption = 0; + r->max_consumption = 0; + r->name_length = 0; + r->stats = NULL; + + if( r->body_length > 0 ) { + while( c->used < HEADER_SIZE + r->body_length ) { + yrmcds_error e = recv_data(c); + if( e != YRMCDS_OK ) return e; + } + r->body = c->recvbuf + HEADER_SIZE; + } + c->last_size = HEADER_SIZE + r->body_length; + + if( r->status != YRMCDS_STATUS_OK ) + return YRMCDS_OK; + + yrmcds_error err; + switch( r->command ) { + case YRMCDS_CNT_CMD_GET: + if( r->body_length < 4 ) { + c->invalid = 1; + return YRMCDS_PROTOCOL_ERROR; + } + r->current_consumption = ntoh32(r->body); + break; + + case YRMCDS_CNT_CMD_ACQUIRE: + if( r->body_length < 4 ) { + c->invalid = 1; + return YRMCDS_PROTOCOL_ERROR; + } + r->resources = ntoh32(r->body); + break; + + case YRMCDS_CNT_CMD_STATS: + err = parse_statistics(c, r); + if( err != YRMCDS_OK ) { + c->invalid = 1; + return err; + } + r->stats = &c->stats; + break; + + case YRMCDS_CNT_CMD_DUMP: + err = parse_dump_record(c, r); + if( err != YRMCDS_OK ) { + c->invalid = 1; + return err; + } + break; + + default: + // No body + break; + } + return YRMCDS_OK; +} + +static yrmcds_error +send_command(yrmcds_cnt* c, yrmcds_cnt_command cmd, uint32_t* serial, + size_t body1_len, const char* body1, + size_t body2_len, const char* body2) { + if( c == NULL || + body1_len > UINT32_MAX - body2_len || + (body1_len != 0 && body1 == NULL) || + (body2_len != 0 && body2 == NULL) ) + return YRMCDS_BAD_ARGUMENT; + +#ifndef LIBYRMCDS_NO_INTERNAL_LOCK + int e = pthread_mutex_lock(&c->lock); + if( e != 0 ) { + errno = e; + return YRMCDS_SYSTEM_ERROR; + } +#endif // ! LIBYRMCDS_NO_INTERNAL_LOCK + + c->serial += 1; + if( serial != NULL ) + *serial = c->serial; + + char header[HEADER_SIZE]; + header[0] = '\x90'; + header[1] = (char)cmd; + header[2] = 0; + header[3] = 0; + hton32((uint32_t)(body1_len + body2_len), header + 4); + memcpy(header + 8, &c->serial, 4); + + yrmcds_error ret = YRMCDS_OK; + + struct iovec iov[3]; + size_t iovcnt = 1; + + iov[0].iov_base = header; + iov[0].iov_len = HEADER_SIZE; + + if( body1_len != 0 ) { + iov[iovcnt].iov_base = (void*)body1; + iov[iovcnt].iov_len = body1_len; + ++iovcnt; + } + if( body2_len != 0 ) { + iov[iovcnt].iov_base = (void*)body2; + iov[iovcnt].iov_len = body2_len; + ++iovcnt; + } + + size_t i; + for( i = 0; i < iovcnt; ) { + ssize_t n = writev(c->sock, iov + i, (int)(iovcnt - i)); + size_t n2 = (size_t)n; + if( n == -1 ) { + if( errno == EINTR ) continue; + ret = YRMCDS_SYSTEM_ERROR; + break; + } + while( n2 > 0 ) { + if( n2 < iov[i].iov_len ) { + iov[i].iov_base = (char*)iov[i].iov_base + n2; + iov[i].iov_len -= n2; + break; + } + n2 -= iov[i].iov_len; + ++i; + } + } + +#ifndef LIBYRMCDS_NO_INTERNAL_LOCK + pthread_mutex_unlock(&c->lock); +#endif + return ret; +} + +yrmcds_error +yrmcds_cnt_noop(yrmcds_cnt* c, uint32_t* serial) { + return send_command(c, YRMCDS_CNT_CMD_NOOP, serial, + 0, NULL, 0, NULL); +} + +yrmcds_error +yrmcds_cnt_get(yrmcds_cnt* c, const char* name, size_t name_len, + uint32_t* serial) { + if( name == NULL || name_len == 0 || name_len > UINT16_MAX ) + return YRMCDS_BAD_ARGUMENT; + + char body[2]; + hton16((uint16_t)name_len, body); + return send_command(c, YRMCDS_CNT_CMD_GET, serial, + sizeof(body), body, name_len, name); +} + +yrmcds_error +yrmcds_cnt_acquire(yrmcds_cnt* c, const char* name, size_t name_len, + uint32_t resources, uint32_t initial, uint32_t* serial) { + if( name == NULL || name_len == 0 || name_len > UINT16_MAX || + resources == 0 || resources > initial ) + return YRMCDS_BAD_ARGUMENT; + + char body[10]; + hton32(resources, body); + hton32(initial, body + 4); + hton16((uint16_t)name_len, body + 8); + return send_command(c, YRMCDS_CNT_CMD_ACQUIRE, serial, + sizeof(body), body, name_len, name); +} + +yrmcds_error +yrmcds_cnt_release(yrmcds_cnt* c, const char* name, size_t name_len, + uint32_t resources, uint32_t* serial) { + if( name == NULL || name_len == 0 || name_len > UINT16_MAX ) + return YRMCDS_BAD_ARGUMENT; + + char body[6]; + hton32(resources, body); + hton16((uint16_t)name_len, body + 4); + return send_command(c, YRMCDS_CNT_CMD_RELEASE, serial, + sizeof(body), body, name_len, name); +} + +yrmcds_error +yrmcds_cnt_stats(yrmcds_cnt* c, uint32_t* serial) { + return send_command(c, YRMCDS_CNT_CMD_STATS, serial, + 0, NULL, 0, NULL); +} + +yrmcds_error +yrmcds_cnt_dump(yrmcds_cnt* c, uint32_t* serial) { + return send_command(c, YRMCDS_CNT_CMD_DUMP, serial, + 0, NULL, 0, NULL); +} diff --git a/web/server/h2o/libh2o/deps/libyrmcds/example/counter.c b/web/server/h2o/libh2o/deps/libyrmcds/example/counter.c new file mode 100644 index 000000000..f08a1b128 --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/example/counter.c @@ -0,0 +1,38 @@ +#include <yrmcds.h> + +#include <errno.h> +#include <error.h> +#include <stdio.h> +#include <stdlib.h> + +void check_error(yrmcds_error e) { + if( e != YRMCDS_OK ) { + if( e == YRMCDS_SYSTEM_ERROR ) { + error(0, errno, "system error"); + } else { + fprintf(stderr, "yrmcds error: %s\n", yrmcds_strerror(e)); + } + exit(2); + } +} + +void check_response(const yrmcds_cnt_response* r) { + if( r->status != YRMCDS_STATUS_OK ) { + fprintf(stderr, "Command failed: 0x%02x %.*s\n", + r->status, (int)r->body_length, r->body); + exit(3); + } +} + +int main(void) { + yrmcds_cnt c; + yrmcds_cnt_response r; + + check_error( yrmcds_cnt_connect(&c, "localhost", 11215) ); + check_error( yrmcds_cnt_noop(&c, NULL) ); + check_error( yrmcds_cnt_recv(&c, &r) ); + check_response(&r); + check_error( yrmcds_cnt_close(&c) ); + + return 0; +} diff --git a/web/server/h2o/libh2o/deps/libyrmcds/example/memcache.c b/web/server/h2o/libh2o/deps/libyrmcds/example/memcache.c new file mode 100644 index 000000000..eb8792867 --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/example/memcache.c @@ -0,0 +1,38 @@ +#include <yrmcds.h> + +#include <errno.h> +#include <error.h> +#include <stdlib.h> +#include <stdio.h> + +void check_error(yrmcds_error e) { + if( e != 0 ) { + if( e == YRMCDS_SYSTEM_ERROR ) { + error(0, errno, "system error"); + } else { + fprintf(stderr, "yrmcds error: %s\n", yrmcds_strerror(e)); + } + exit(2); + } +} + +void check_response(const yrmcds_response* r) { + if( r->status != YRMCDS_STATUS_OK ) { + fprintf(stderr, "Command failed: 0x%04x %.*s\n", + r->status, (int)r->data_len, r->data); + exit(3); + } +} + +int main(int argc, char** argv) { + yrmcds c; + yrmcds_response r; + + check_error( yrmcds_connect(&c, "localhost", 11211) ); + check_error( yrmcds_noop(&c, NULL) ); + check_error( yrmcds_recv(&c, &r) ); + check_response(&r); + check_error( yrmcds_close(&c) ); + + return 0; +} diff --git a/web/server/h2o/libh2o/deps/libyrmcds/recv.c b/web/server/h2o/libh2o/deps/libyrmcds/recv.c new file mode 100644 index 000000000..fe6cf94bc --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/recv.c @@ -0,0 +1,354 @@ +// (C) 2013 Cybozu et al. + +#include "yrmcds.h" +#include "yrmcds_portability.h" + +#ifdef LIBYRMCDS_USE_LZ4 +# include "lz4/lib/lz4.h" +#endif + +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/types.h> + +static const size_t BINARY_HEADER_SIZE = 24; +static const size_t RECV_SIZE = 256 << 10; +static const size_t MAX_CAPACITY = 50 << 20; // 50 MiB + +static inline yrmcds_error recv_data(yrmcds* c) { + if( (c->capacity - c->used) < RECV_SIZE ) { + size_t new_capacity = c->capacity * 2; + char* new_buffer = (char*)realloc(c->recvbuf, new_capacity); + if( new_buffer == NULL ) + return YRMCDS_OUT_OF_MEMORY; + c->recvbuf = new_buffer; + c->capacity = new_capacity; + } + + ssize_t n; + AGAIN: + n = recv(c->sock, c->recvbuf + c->used, RECV_SIZE, 0); + if( n == -1 ) { + if( errno == EINTR ) goto AGAIN; + return YRMCDS_SYSTEM_ERROR; + } + if( n == 0 ) + return YRMCDS_DISCONNECTED; + c->used += (size_t)n; + return YRMCDS_OK; +} + +static inline uint64_t ntoh64(const char* p) { + uint64_t n; + memcpy(&n, p, sizeof(n)); + return be64toh(n); +} + +static inline uint32_t ntoh32(const char* p) { + uint32_t n; + memcpy(&n, p, sizeof(n)); + return be32toh(n); +} + +static inline uint16_t ntoh16(const char* p) { + uint16_t n; + memcpy(&n, p, sizeof(n)); + return be16toh(n); +} + +static yrmcds_error text_recv(yrmcds* c, yrmcds_response* r); + +yrmcds_error yrmcds_recv(yrmcds* c, yrmcds_response* r) { + if( c == NULL || r == NULL ) + return YRMCDS_BAD_ARGUMENT; + if( c->invalid ) + return YRMCDS_PROTOCOL_ERROR; + + if( c->last_size > 0 ) { + size_t remain = c->used - c->last_size; + if( remain > 0 ) + memmove(c->recvbuf, c->recvbuf + c->last_size, remain); + c->used = remain; + c->last_size = 0; + free(c->decompressed); + c->decompressed = NULL; + } + + if( c->text_mode ) { + return text_recv(c, r); + } + + while( c->used < BINARY_HEADER_SIZE ) { + yrmcds_error e = recv_data(c); + if( e != 0 ) return e; + } + + if( *c->recvbuf != '\x81' ) { + c->invalid = 1; + return YRMCDS_PROTOCOL_ERROR; + } + uint32_t total_len = ntoh32(c->recvbuf + 8); + if( total_len > MAX_CAPACITY ) { + c->invalid = 1; + return YRMCDS_PROTOCOL_ERROR; + } + while( c->used < (BINARY_HEADER_SIZE + total_len) ) { + yrmcds_error e = recv_data(c); + if( e != 0 ) return e; + } + + uint16_t key_len = ntoh16(c->recvbuf + 2); + uint8_t extras_len = *(unsigned char*)(c->recvbuf + 4); + if( total_len < (key_len + extras_len) ) { + c->invalid = 1; + return YRMCDS_PROTOCOL_ERROR; + } + + const char* pkey = c->recvbuf + (BINARY_HEADER_SIZE + extras_len); + r->length = BINARY_HEADER_SIZE + total_len; + r->command = *(unsigned char*)(c->recvbuf + 1); + r->key = key_len ? pkey : NULL; + r->key_len = key_len; + r->status = ntoh16(c->recvbuf + 6); + memcpy(&(r->serial), c->recvbuf + 12, 4); + r->cas_unique = ntoh64(c->recvbuf + 16); + r->flags = 0; + if( extras_len > 0 ) { + if( extras_len != 4 ) { + c->invalid = 1; + return YRMCDS_PROTOCOL_ERROR; + } + r->flags = ntoh32(c->recvbuf + BINARY_HEADER_SIZE); + } + + size_t data_len = total_len - key_len - extras_len; + const char* pdata = pkey + key_len; + + if( (r->command == YRMCDS_CMD_INCREMENT || + r->command == YRMCDS_CMD_DECREMENT) && + (r->status == YRMCDS_STATUS_OK) ) { + r->data = NULL; + r->data_len = 0; + if( data_len != 8 ) { + c->invalid = 1; + return YRMCDS_PROTOCOL_ERROR; + } + r->value = ntoh64(pdata); + c->last_size = r->length; + return YRMCDS_OK; + } + r->value = 0; + r->data = data_len ? pdata : NULL; + r->data_len = data_len; + +#ifdef LIBYRMCDS_USE_LZ4 + if( c->compress_size && (r->flags & YRMCDS_FLAG_COMPRESS) ) { + if( data_len == 0 ) { + c->invalid = 1; + return YRMCDS_PROTOCOL_ERROR; + } + r->flags &= ~(uint32_t)YRMCDS_FLAG_COMPRESS; + uint32_t decompress_size = ntoh32(pdata); + if( UINT32_MAX > INT_MAX ) { + if( decompress_size > INT_MAX ) { + c->invalid = 1; + return YRMCDS_PROTOCOL_ERROR; + } + } + c->decompressed = (char*)malloc(decompress_size); + if( c->decompressed == NULL ) + return YRMCDS_OUT_OF_MEMORY; + int d = LZ4_decompress_safe(pdata + sizeof(uint32_t), + c->decompressed, + (int)(data_len - sizeof(uint32_t)), + (int)decompress_size); + if( d != decompress_size ) { + c->invalid = 1; + return YRMCDS_PROTOCOL_ERROR; + } + r->data = c->decompressed; + r->data_len = decompress_size; + } +#endif // LIBYRMCDS_USE_LZ4 + + c->last_size = r->length; + return YRMCDS_OK; +} + + +// text protocol +#define PARSE_UINT(name) \ + uint64_t name = 0; \ + while( *p == ' ' ) p++; \ + while( '0' <= *p && *p <= '9' ) { \ + name *= 10; \ + name += (uint64_t)(*p - '0'); \ + p++; \ + } + +static yrmcds_error text_recv(yrmcds* c, yrmcds_response* r) { + char* pos; + while( c->used == 0 || + (pos = (char*)memchr(c->recvbuf, '\n', c->used)) == NULL ) { + yrmcds_error e = recv_data(c); + if( e != 0 ) return e; + } + // make sure the buffer contains CRLF. + if( (pos - c->recvbuf) < 2 || *(pos-1) != '\r' ) { + c->invalid = 1; + return YRMCDS_PROTOCOL_ERROR; + } + pos--; + size_t resp_len = (size_t)(pos - c->recvbuf); + + memset(r, 0, sizeof(yrmcds_response)); + r->serial = ++c->rserial; + r->length = resp_len + 2; + r->status = YRMCDS_STATUS_OK; + r->command = YRMCDS_CMD_BOTTOM; // dummy for emulating binary protocol + + if( resp_len == 2 && memcmp(c->recvbuf, "OK", 2) == 0 ) { + // successful response for flush_all + goto FINISH; + } + if( resp_len == 3 && memcmp(c->recvbuf, "END", 3) == 0 ) { + // get failed for non-existing object. + r->status = YRMCDS_STATUS_NOTFOUND; + goto FINISH; + } + if( resp_len == 5 && memcmp(c->recvbuf, "ERROR", 5) == 0 ) { + r->status = YRMCDS_STATUS_UNKNOWNCOMMAND; + goto FINISH; + } + if( resp_len == 6 ) { + if( memcmp(c->recvbuf, "STORED", 6) == 0 ) { + // successful response for storage commands. + goto FINISH; + } + if( memcmp(c->recvbuf, "EXISTS", 6) == 0 ) { + // failure response for cas. + r->status = YRMCDS_STATUS_EXISTS; + goto FINISH; + } + } + if( resp_len == 7 ) { + if( memcmp(c->recvbuf, "DELETED", 7) == 0 ) + // successful response for delete. + goto FINISH; + if( memcmp(c->recvbuf, "TOUCHED", 7) == 0 ) + // successful response for touch. + goto FINISH; + } + if( resp_len == 9 && memcmp(c->recvbuf, "NOT_FOUND", 9) == 0 ) { + // failure response for cas, delete, incr, decr, or touch. + r->status = YRMCDS_STATUS_NOTFOUND; + goto FINISH; + } + if( resp_len == 10 && memcmp(c->recvbuf, "NOT_STORED", 10) == 0 ) { + // failure response for add, replace, append, or prepend. + r->status = YRMCDS_STATUS_NOTSTORED; + goto FINISH; + } + if( resp_len > 0 && '0' <= c->recvbuf[0] && c->recvbuf[0] <= '9' ) { + // successful response for incr or decr. + const char* p = c->recvbuf; + PARSE_UINT(value); + r->value = value; + goto FINISH; + } + if( resp_len > 8 && memcmp(c->recvbuf, "VERSION ", 8) == 0 ) { + // successful response for version. + r->data_len = resp_len - 8; + r->data = c->recvbuf + 8; + goto FINISH; + } + if( resp_len > 6 && memcmp(c->recvbuf, "VALUE ", 6) == 0 ) { + // successful response for gets. + const char* p = c->recvbuf + 6; + while( *p == ' ' ) p++; + if( p == pos ) goto UNKNOWN; + + const char* key_end = memchr(p, ' ', (size_t)(pos - p)); + if( key_end == NULL ) goto UNKNOWN; + r->key = p; + r->key_len = (size_t)(key_end - p); + + p = key_end; + PARSE_UINT(flags); + if( *p != ' ' ) goto UNKNOWN; + r->flags = (uint32_t)flags; + + PARSE_UINT(bytes); + if( bytes > MAX_CAPACITY ) { + c->invalid = 1; + return YRMCDS_PROTOCOL_ERROR; + } + size_t data_len = (size_t)bytes; + + while( *p == ' ' ) p++; + if( *p < '0' || '9' < *p ) goto UNKNOWN; + PARSE_UINT(cas); + + size_t required = resp_len + 2 + data_len + 7; // CRLF "END" CRLF + while( c->used < required ) { + yrmcds_error e = recv_data(c); + if( e != 0 ) return e; + } + + const char* data = c->recvbuf + (resp_len + 2); + if( memcmp(data + data_len, "\r\nEND\r\n", 7) != 0 ) { + c->invalid = 1; + return YRMCDS_PROTOCOL_ERROR; + } + r->length = required; + r->flags = (uint32_t)flags; + +#ifdef LIBYRMCDS_USE_LZ4 + if( c->compress_size && (r->flags & YRMCDS_FLAG_COMPRESS) ) { + if( data_len == 0 ) { + c->invalid = 1; + return YRMCDS_PROTOCOL_ERROR; + } + r->flags &= ~(uint32_t)YRMCDS_FLAG_COMPRESS; + uint32_t decompress_size = ntoh32(data); + if( UINT32_MAX > INT_MAX ) { + if( decompress_size > INT_MAX ) { + c->invalid = 1; + return YRMCDS_PROTOCOL_ERROR; + } + } + c->decompressed = (char*)malloc(decompress_size); + if( c->decompressed == NULL ) + return YRMCDS_OUT_OF_MEMORY; + int d = LZ4_decompress_safe(data + sizeof(uint32_t), + c->decompressed, + (int)(data_len - sizeof(uint32_t)), + (int)decompress_size); + if( d != decompress_size ) { + c->invalid = 1; + return YRMCDS_PROTOCOL_ERROR; + } + data = c->decompressed; + data_len = (size_t)decompress_size; + } +#endif // LIBYRMCDS_USE_LZ4 + r->data = data; + r->data_len = data_len; + r->cas_unique = cas; + goto FINISH; + } + + UNKNOWN: + r->status = YRMCDS_STATUS_OTHER; + fprintf(stderr, "[libyrmcds] unknown response: %.*s\n", + (int)resp_len, c->recvbuf); + + FINISH: + c->last_size = r->length; + return YRMCDS_OK; +} + diff --git a/web/server/h2o/libh2o/deps/libyrmcds/send.c b/web/server/h2o/libh2o/deps/libyrmcds/send.c new file mode 100644 index 000000000..b1eb48f7a --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/send.c @@ -0,0 +1,564 @@ +// (C) 2013-2016 Cybozu et al. + +#include "yrmcds.h" +#include "yrmcds_portability.h" +#include "yrmcds_text.h" + +#ifdef LIBYRMCDS_USE_LZ4 +# include "lz4/lib/lz4.h" +#endif + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/uio.h> + +static const size_t BINARY_HEADER_SIZE = 24; +static const size_t MAX_DATA_SIZE = ((size_t)1) << 30; + +static inline void hton64(uint64_t i, char* p) { + uint64_t n = htobe64(i); + memcpy(p, &n, sizeof(n)); +} + +static inline void hton32(uint32_t i, char* p) { + uint32_t n = htobe32(i); + memcpy(p, &n, sizeof(n)); +} + +static inline void hton16(uint16_t i, char* p) { + uint16_t n = htobe16(i); + memcpy(p, &n, sizeof(n)); +} + +static yrmcds_error send_command( + yrmcds* c, yrmcds_command cmd, uint64_t cas, uint32_t* serial, + size_t key_len, const char* key, + size_t extras_len, const char* extras, + size_t data_len, const char* data) { + if( cmd >= YRMCDS_CMD_BOTTOM || + key_len > 65535 || extras_len > 127 || data_len > MAX_DATA_SIZE || + (key_len != 0 && key == NULL) || + (extras_len != 0 && extras == NULL) || + (data_len != 0 && data == NULL) ) + return YRMCDS_BAD_ARGUMENT; + + char h[BINARY_HEADER_SIZE]; + memset(h, 0, sizeof(h)); + h[0] = '\x80'; + h[1] = (char)cmd; + hton16((uint16_t)key_len, &h[2]); + h[4] = (char)extras_len; + size_t total_len = (key_len + extras_len + data_len); + hton32((uint32_t)total_len, &h[8]); + hton64(cas, &h[16]); + +#ifndef LIBYRMCDS_NO_INTERNAL_LOCK + int e = pthread_mutex_lock(&c->lock); + if( e != 0 ) { + errno = e; + return YRMCDS_SYSTEM_ERROR; + } +#endif // ! LIBYRMCDS_NO_INTERNAL_LOCK + + yrmcds_error ret = YRMCDS_OK; + c->serial = c->serial + 1; + memcpy(&h[12], &c->serial, 4); + if( serial != NULL ) + *serial = c->serial; + + struct iovec iov[4]; + int iovcnt = 1; + iov[0].iov_base = h; + iov[0].iov_len = sizeof(h); + + if( extras_len > 0 ) { + iov[iovcnt].iov_base = (void*)extras; + iov[iovcnt].iov_len = extras_len; + iovcnt++; + } + if( key_len > 0 ) { + iov[iovcnt].iov_base = (void*)key; + iov[iovcnt].iov_len = key_len; + iovcnt++; + } + if( data_len > 0 ) { + iov[iovcnt].iov_base = (void*)data; + iov[iovcnt].iov_len = data_len; + iovcnt++; + } + + while( iovcnt > 0 ) { + ssize_t n = writev(c->sock, iov, iovcnt); + size_t n2 = (size_t)n; + if( n == -1 ) { + if( errno == EINTR ) continue; + ret = YRMCDS_SYSTEM_ERROR; + goto OUT; + } + while( n2 > 0 ) { + if( n2 < iov[0].iov_len ) { + iov[0].iov_base = (char*)iov[0].iov_base + n2; + iov[0].iov_len -= n2; + break; + } + n2 -= iov[0].iov_len; + iovcnt --; + if( iovcnt == 0 ) + break; + + int i; + for( i = 0; i < iovcnt; ++i ) + iov[i] = iov[i+1]; + } + } + + OUT: +#ifndef LIBYRMCDS_NO_INTERNAL_LOCK + pthread_mutex_unlock(&c->lock); +#endif + return ret; +} + +static yrmcds_error send_data( + yrmcds* c, yrmcds_command cmd, const char* key, size_t key_len, + const char* data, size_t data_len, uint32_t flags, uint32_t expire, + uint64_t cas, uint32_t* serial) { + if( c == NULL || key == NULL || key_len == 0 || + data == NULL || data_len == 0 ) + return YRMCDS_BAD_ARGUMENT; + + int compressed = 0; +#ifdef LIBYRMCDS_USE_LZ4 + if( (c->compress_size > 0) && (data_len > c->compress_size) ) { + if( flags & YRMCDS_FLAG_COMPRESS ) + return YRMCDS_BAD_ARGUMENT; + + size_t bound = (size_t)LZ4_compressBound((int)data_len); + char* new_data = (char*)malloc(bound + sizeof(uint32_t)); + if( new_data == NULL ) + return YRMCDS_OUT_OF_MEMORY; + uint32_t new_size = + (uint32_t)LZ4_compress(data, new_data + sizeof(uint32_t), + (int)data_len); + if( new_size == 0 ) { + free(new_data); + return YRMCDS_COMPRESS_FAILED; + } + hton32((uint32_t)data_len, new_data); + flags |= YRMCDS_FLAG_COMPRESS; + data_len = sizeof(uint32_t) + new_size; + data = new_data; + compressed = 1; + } +#endif // LIBYRMCDS_USE_LZ4 + + char extras[8]; + hton32(flags, extras); + hton32(expire, &extras[4]); + yrmcds_error e = send_command(c, cmd, cas, serial, key_len, key, + sizeof(extras), extras, data_len, data); + if( compressed ) + free((void*)data); + return e; +} + +yrmcds_error yrmcds_noop(yrmcds* c, uint32_t* serial) { + if( c == NULL ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return YRMCDS_NOT_IMPLEMENTED; + + return send_command(c, YRMCDS_CMD_NOOP, 0, serial, + 0, NULL, 0, NULL, 0, NULL); +} + +yrmcds_error yrmcds_get(yrmcds* c, const char* key, size_t key_len, + int quiet, uint32_t* serial) { + if( c == NULL || key == NULL || key_len == 0 ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return yrmcds_text_get(c, key, key_len, quiet, serial); + + return send_command(c, quiet ? YRMCDS_CMD_GETQ : YRMCDS_CMD_GET, + 0, serial, key_len, key, 0, NULL, 0, NULL); +} + +yrmcds_error yrmcds_getk(yrmcds* c, const char* key, size_t key_len, + int quiet, uint32_t* serial) { + if( c == NULL || key == NULL || key_len == 0 ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return yrmcds_text_get(c, key, key_len, quiet, serial); + + return send_command(c, quiet ? YRMCDS_CMD_GETKQ : YRMCDS_CMD_GETK, + 0, serial, key_len, key, 0, NULL, 0, NULL); +} + +yrmcds_error yrmcds_get_touch(yrmcds* c, const char* key, size_t key_len, + uint32_t expire, int quiet, uint32_t* serial) { + if( c == NULL || key == NULL || key_len == 0 ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return YRMCDS_NOT_IMPLEMENTED; + + char extras[4]; + hton32(expire, extras); + return send_command(c, quiet ? YRMCDS_CMD_GATQ : YRMCDS_CMD_GAT, + 0, serial, key_len, key, + sizeof(extras), extras, 0, NULL); +} + +yrmcds_error yrmcds_getk_touch(yrmcds* c, const char* key, size_t key_len, + uint32_t expire, int quiet, uint32_t* serial) { + if( c == NULL || key == NULL || key_len == 0 ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return YRMCDS_NOT_IMPLEMENTED; + + char extras[4]; + hton32(expire, extras); + return send_command(c, quiet ? YRMCDS_CMD_GATKQ : YRMCDS_CMD_GATK, + 0, serial, key_len, key, + sizeof(extras), extras, 0, NULL); +} + +yrmcds_error yrmcds_lock_get(yrmcds* c, const char* key, size_t key_len, + int quiet, uint32_t* serial) { + if( c == NULL || key == NULL || key_len == 0 ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return YRMCDS_NOT_IMPLEMENTED; + + return send_command(c, quiet ? YRMCDS_CMD_LAGQ : YRMCDS_CMD_LAG, + 0, serial, key_len, key, 0, NULL, 0, NULL); +} + +yrmcds_error yrmcds_lock_getk(yrmcds* c, const char* key, size_t key_len, + int quiet, uint32_t* serial) { + if( c == NULL || key == NULL || key_len == 0 ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return YRMCDS_NOT_IMPLEMENTED; + + return send_command(c, quiet ? YRMCDS_CMD_LAGKQ : YRMCDS_CMD_LAGK, + 0, serial, key_len, key, 0, NULL, 0, NULL); +} + +yrmcds_error yrmcds_touch(yrmcds* c, const char* key, size_t key_len, + uint32_t expire, int quiet, uint32_t* serial) { + if( c == NULL || key == NULL || key_len == 0 ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return yrmcds_text_touch(c, key, key_len, expire, quiet, serial); + + char extras[4]; + hton32(expire, extras); + return send_command(c, YRMCDS_CMD_TOUCH, 0, serial, key_len, key, + sizeof(extras), extras, 0, NULL); +} + +yrmcds_error yrmcds_set(yrmcds* c, const char* key, size_t key_len, + const char* data, size_t data_len, + uint32_t flags, uint32_t expire, uint64_t cas, + int quiet, uint32_t* serial) { + if( c && c->text_mode ) + return yrmcds_text_set(c, key, key_len, data, data_len, + flags, expire, cas, quiet, serial); + + return send_data(c, quiet ? YRMCDS_CMD_SETQ : YRMCDS_CMD_SET, + key, key_len, data, data_len, flags, expire, cas, serial); +} + +yrmcds_error yrmcds_replace(yrmcds* c, const char* key, size_t key_len, + const char* data, size_t data_len, + uint32_t flags, uint32_t expire, uint64_t cas, + int quiet, uint32_t* serial) { + if( c && c->text_mode ) + return yrmcds_text_replace(c, key, key_len, data, data_len, + flags, expire, cas, quiet, serial); + + return send_data(c, quiet ? YRMCDS_CMD_REPLACEQ : YRMCDS_CMD_REPLACE, + key, key_len, data, data_len, flags, expire, cas, serial); +} + +yrmcds_error yrmcds_add(yrmcds* c, const char* key, size_t key_len, + const char* data, size_t data_len, + uint32_t flags, uint32_t expire, uint64_t cas, + int quiet, uint32_t* serial) { + if( c && c->text_mode ) + return yrmcds_text_add(c, key, key_len, data, data_len, + flags, expire, cas, quiet, serial); + + return send_data(c, quiet ? YRMCDS_CMD_ADDQ : YRMCDS_CMD_ADD, + key, key_len, data, data_len, flags, expire, cas, serial); +} + +yrmcds_error yrmcds_replace_unlock(yrmcds* c, const char* key, size_t key_len, + const char* data, size_t data_len, + uint32_t flags, uint32_t expire, + int quiet, uint32_t* serial) { + if( c && c->text_mode ) + return YRMCDS_NOT_IMPLEMENTED; + + return send_data(c, quiet ? YRMCDS_CMD_RAUQ : YRMCDS_CMD_RAU, + key, key_len, data, data_len, flags, expire, 0, serial); +} + +yrmcds_error yrmcds_incr(yrmcds* c, const char* key, size_t key_len, + uint64_t value, int quiet, uint32_t* serial) { + if( c == NULL || key == NULL || key_len == 0 ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return yrmcds_text_incr(c, key, key_len, value, quiet, serial); + + char extras[20]; + hton64(value, extras); + hton64((uint64_t)0, &extras[8]); + hton32(~(uint32_t)0, &extras[16]); + return send_command(c, quiet ? YRMCDS_CMD_INCREMENTQ : YRMCDS_CMD_INCREMENT, + 0, serial, key_len, key, + sizeof(extras), extras, 0, NULL); +} + +yrmcds_error yrmcds_incr2(yrmcds* c, const char* key, size_t key_len, + uint64_t value, uint64_t initial, uint32_t expire, + int quiet, uint32_t* serial) { + if( c == NULL || key == NULL || key_len == 0 ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return YRMCDS_NOT_IMPLEMENTED; + + char extras[20]; + hton64(value, extras); + hton64(initial, &extras[8]); + hton32(expire, &extras[16]); + return send_command(c, quiet ? YRMCDS_CMD_INCREMENTQ : YRMCDS_CMD_INCREMENT, + 0, serial, key_len, key, + sizeof(extras), extras, 0, NULL); +} + +yrmcds_error yrmcds_decr(yrmcds* c, const char* key, size_t key_len, + uint64_t value, int quiet, uint32_t* serial) { + if( c == NULL || key == NULL || key_len == 0 ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return yrmcds_text_decr(c, key, key_len, value, quiet, serial); + + char extras[20]; + hton64(value, extras); + hton64((uint64_t)0, &extras[8]); + hton32(~(uint32_t)0, &extras[16]); + return send_command(c, quiet ? YRMCDS_CMD_DECREMENTQ : YRMCDS_CMD_DECREMENT, + 0, serial, key_len, key, + sizeof(extras), extras, 0, NULL); +} + +yrmcds_error yrmcds_decr2(yrmcds* c, const char* key, size_t key_len, + uint64_t value, uint64_t initial, uint32_t expire, + int quiet, uint32_t* serial) { + if( c == NULL || key == NULL || key_len == 0 ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return YRMCDS_NOT_IMPLEMENTED; + + char extras[20]; + hton64(value, extras); + hton64(initial, &extras[8]); + hton32(expire, &extras[16]); + return send_command(c, quiet ? YRMCDS_CMD_DECREMENTQ : YRMCDS_CMD_DECREMENT, + 0, serial, key_len, key, + sizeof(extras), extras, 0, NULL); +} + +yrmcds_error yrmcds_append(yrmcds* c, const char* key, size_t key_len, + const char* data, size_t data_len, + int quiet, uint32_t* serial) { + if( c == NULL || key == NULL || key_len == 0 || + data == NULL || data_len == 0 ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return yrmcds_text_append(c, key, key_len, data, data_len, + quiet, serial); + + return send_command(c, quiet ? YRMCDS_CMD_APPENDQ : YRMCDS_CMD_APPEND, + 0, serial, key_len, key, 0, NULL, data_len, data); +} + +yrmcds_error yrmcds_prepend(yrmcds* c, const char* key, size_t key_len, + const char* data, size_t data_len, + int quiet, uint32_t* serial) { + if( c == NULL || key == NULL || key_len == 0 || + data == NULL || data_len == 0 ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return yrmcds_text_prepend(c, key, key_len, data, data_len, + quiet, serial); + + return send_command(c, quiet ? YRMCDS_CMD_PREPENDQ : YRMCDS_CMD_PREPEND, + 0, serial, key_len, key, 0, NULL, data_len, data); +} + +yrmcds_error yrmcds_remove(yrmcds* c, const char* key, size_t key_len, + int quiet, uint32_t* serial) { + if( c == NULL || key == NULL || key_len == 0 ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return yrmcds_text_remove(c, key, key_len, quiet, serial); + + return send_command(c, quiet ? YRMCDS_CMD_DELETEQ : YRMCDS_CMD_DELETE, + 0, serial, key_len, key, 0, NULL, 0, NULL); +} + +yrmcds_error yrmcds_lock(yrmcds* c, const char* key, size_t key_len, + int quiet, uint32_t* serial) { + if( c == NULL || key == NULL || key_len == 0 ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return YRMCDS_NOT_IMPLEMENTED; + + return send_command(c, quiet ? YRMCDS_CMD_LOCKQ : YRMCDS_CMD_LOCK, + 0, serial, key_len, key, 0, NULL, 0, NULL); +} + +yrmcds_error yrmcds_unlock(yrmcds* c, const char* key, size_t key_len, + int quiet, uint32_t* serial) { + if( c == NULL || key == NULL || key_len == 0 ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return YRMCDS_NOT_IMPLEMENTED; + + return send_command(c, quiet ? YRMCDS_CMD_UNLOCKQ : YRMCDS_CMD_UNLOCK, + 0, serial, key_len, key, 0, NULL, 0, NULL); +} + +yrmcds_error yrmcds_unlockall(yrmcds* c, int quiet, uint32_t* serial) { + if( c == NULL ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return YRMCDS_NOT_IMPLEMENTED; + + return send_command(c, quiet ? YRMCDS_CMD_UNLOCKALLQ : YRMCDS_CMD_UNLOCKALL, + 0, serial, 0, NULL, 0, NULL, 0, NULL); +} + +yrmcds_error yrmcds_flush(yrmcds* c, uint32_t delay, + int quiet, uint32_t* serial) { + if( c == NULL ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return yrmcds_text_flush(c, delay, quiet, serial); + + if( delay == 0 ) + return send_command(c, quiet ? YRMCDS_CMD_FLUSHQ : YRMCDS_CMD_FLUSH, + 0, serial, 0, NULL, 0, NULL, 0, NULL); + + char extra[4]; + hton32(delay, extra); + return send_command(c, quiet ? YRMCDS_CMD_FLUSHQ : YRMCDS_CMD_FLUSH, + 0, serial, 0, NULL, sizeof(extra), extra, 0, NULL); +} + +yrmcds_error yrmcds_stat_general(yrmcds* c, uint32_t* serial) { + if( c == NULL ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return YRMCDS_NOT_IMPLEMENTED; + + return send_command(c, YRMCDS_CMD_STAT, + 0, serial, 0, NULL, 0, NULL, 0, NULL); +} + +yrmcds_error yrmcds_stat_settings(yrmcds* c, uint32_t* serial) { + if( c == NULL ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return YRMCDS_NOT_IMPLEMENTED; + + const char key[] = "settings"; + return send_command(c, YRMCDS_CMD_STAT, + 0, serial, sizeof(key) - 1, key, 0, NULL, 0, NULL); +} + +yrmcds_error yrmcds_stat_items(yrmcds* c, uint32_t* serial) { + if( c == NULL ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return YRMCDS_NOT_IMPLEMENTED; + + const char key[] = "items"; + return send_command(c, YRMCDS_CMD_STAT, + 0, serial, sizeof(key) - 1, key, 0, NULL, 0, NULL); +} + +yrmcds_error yrmcds_stat_sizes(yrmcds* c, uint32_t* serial) { + if( c == NULL ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return YRMCDS_NOT_IMPLEMENTED; + + const char key[] = "sizes"; + return send_command(c, YRMCDS_CMD_STAT, + 0, serial, sizeof(key) - 1, key, 0, NULL, 0, NULL); +} + +yrmcds_error yrmcds_keys(yrmcds* c, const char* prefix, size_t prefix_len, + uint32_t* serial) { + if( c == NULL || + (prefix == NULL && prefix_len != 0) || + (prefix != NULL && prefix_len == 0) ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return YRMCDS_NOT_IMPLEMENTED; + + return send_command(c, YRMCDS_CMD_KEYS, + 0, serial, prefix_len, prefix, 0, NULL, 0, NULL); +} + +yrmcds_error yrmcds_version(yrmcds* c, uint32_t* serial) { + if( c == NULL ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return yrmcds_text_version(c, serial); + + return send_command(c, YRMCDS_CMD_VERSION, + 0, serial, 0, NULL, 0, NULL, 0, NULL); +} + +yrmcds_error yrmcds_quit(yrmcds* c, int quiet, uint32_t* serial) { + if( c == NULL ) + return YRMCDS_BAD_ARGUMENT; + + if( c->text_mode ) + return yrmcds_text_quit(c, serial); + + return send_command(c, quiet ? YRMCDS_CMD_QUITQ : YRMCDS_CMD_QUIT, + 0, serial, 0, NULL, 0, NULL, 0, NULL); +} diff --git a/web/server/h2o/libh2o/deps/libyrmcds/send_text.c b/web/server/h2o/libh2o/deps/libyrmcds/send_text.c new file mode 100644 index 000000000..b84fdce0d --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/send_text.c @@ -0,0 +1,414 @@ +// (C) 2016 Cybozu + +#include "yrmcds_text.h" +#include "yrmcds_portability.h" + +#ifdef LIBYRMCDS_USE_LZ4 +# include "lz4/lib/lz4.h" +#endif + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <unistd.h> + +#define MAX_KEY_LENGTH 250 // from memcached spec. +#define TEXTBUF_SIZE 1000 // enough for any command & parameters. +#define EXPAND_STR(s) (s), (sizeof(s) - 1) +static const char CRLF[2] = {'\r', '\n'}; + +#ifdef LIBYRMCDS_USE_LZ4 +static inline void +hton32(uint32_t i, char* p) { + uint32_t n = htobe32(i); + memcpy(p, &n, sizeof(n)); +} +#endif + +static inline yrmcds_error +check_key(const char* key, size_t key_len) { + if( key_len > MAX_KEY_LENGTH ) + return YRMCDS_BAD_KEY; + + size_t i; + for( i = 0; i < key_len; i++ ) { + char c = key[i]; + if( c <= ' ' ) return YRMCDS_BAD_KEY; // SPC and control chars + if( c == 127 ) return YRMCDS_BAD_KEY; // DEL + } + + return YRMCDS_OK; +} + +typedef struct { + char* pos; + char buffer[TEXTBUF_SIZE]; +} textbuf_t; + +static inline size_t +textbuf_length(const textbuf_t* buf) { + return (size_t)(buf->pos - buf->buffer); +} + +static inline void +textbuf_init(textbuf_t* buf) { + buf->pos = buf->buffer; +} + +static inline void +textbuf_append_char(textbuf_t* buf, char c) { + *buf->pos = c; + ++buf->pos; +} + +static inline void +textbuf_append_string(textbuf_t* buf, const char* s, size_t len) { + memcpy(buf->pos, s, len); + buf->pos += len; +} + +#define textbuf_append_const_string(b, s) \ + textbuf_append_string(b, s, sizeof(s) - 1) + +static void +textbuf_append_uint64(textbuf_t* buf, uint64_t n) { + // UINT64_MAX = 18446744073709551615 -> char[20] + char nbuf[20]; + char* pos = (nbuf) + 20; + + do { + pos--; + uint64_t m = n % 10; + n /= 10; + *pos = (char)('0' + m); + } while( n != 0 ); + + textbuf_append_string(buf, pos, (size_t)(nbuf - pos + 20)); +} + + +static yrmcds_error +send_command(yrmcds* c, textbuf_t* buf, uint32_t* serial) { + memcpy(buf->pos, CRLF, sizeof(CRLF)); + buf->pos += sizeof(CRLF); + const char* p = buf->buffer; + size_t len = textbuf_length(buf); + +#ifndef LIBYRMCDS_NO_INTERNAL_LOCK + int e = pthread_mutex_lock(&c->lock); + if( e != 0 ) { + errno = e; + return YRMCDS_SYSTEM_ERROR; + } +#endif // ! LIBYRMCDS_NO_INTERNAL_LOCK + + c->serial = c->serial + 1; + if( serial != NULL ) + *serial = c->serial; + + yrmcds_error ret = YRMCDS_OK; + while( len > 0 ) { + ssize_t n = send(c->sock, p, len, 0); + if( n == -1 ) { + if( errno == EINTR ) continue; + ret = YRMCDS_SYSTEM_ERROR; + goto OUT; + } + size_t n2 = (size_t)n; + p += n2; + len -= n2; + } + + OUT: +#ifndef LIBYRMCDS_NO_INTERNAL_LOCK + pthread_mutex_unlock(&c->lock); +#endif + return ret; +} + +static yrmcds_error +send_data(yrmcds* c, const char* cmd, size_t cmd_len, + const char* key, size_t key_len, + const char* data, size_t data_len, + uint32_t flags, uint32_t expire, uint64_t cas, + int quiet, uint32_t* serial) { + if( key == NULL || key_len == 0 || data == NULL || data_len == 0 || quiet ) + return YRMCDS_BAD_ARGUMENT; + + yrmcds_error ret; + ret = check_key(key, key_len); + if( ret != YRMCDS_OK ) return ret; + + if( cas != 0 ) { + cmd = "cas"; + cmd_len = 3; + } + + int compressed = 0; +#ifdef LIBYRMCDS_USE_LZ4 + if( (c->compress_size > 0) && (data_len > c->compress_size) ) { + if( flags & YRMCDS_FLAG_COMPRESS ) + return YRMCDS_BAD_ARGUMENT; + + size_t bound = (size_t)LZ4_compressBound((int)data_len); + char* new_data = (char*)malloc(bound + sizeof(uint32_t)); + if( new_data == NULL ) + return YRMCDS_OUT_OF_MEMORY; + uint32_t new_size = + (uint32_t)LZ4_compress(data, new_data + sizeof(uint32_t), + (int)data_len); + if( new_size == 0 ) { + free(new_data); + return YRMCDS_COMPRESS_FAILED; + } + hton32((uint32_t)data_len, new_data); + flags |= YRMCDS_FLAG_COMPRESS; + data_len = sizeof(uint32_t) + new_size; + data = new_data; + compressed = 1; + } +#endif // LIBYRMCDS_USE_LZ4 + + textbuf_t buf[1]; + textbuf_init(buf); + + // "cmd key flags expire bytes (cas)" + textbuf_append_string(buf, cmd, cmd_len); + textbuf_append_char(buf, ' '); + textbuf_append_string(buf, key, key_len); + textbuf_append_char(buf, ' '); + textbuf_append_uint64(buf, flags); + textbuf_append_char(buf, ' '); + textbuf_append_uint64(buf, expire); + textbuf_append_char(buf, ' '); + textbuf_append_uint64(buf, (uint64_t)data_len); + if( cas != 0 ) { + textbuf_append_char(buf, ' '); + textbuf_append_uint64(buf, cas); + } + textbuf_append_string(buf, CRLF, sizeof(CRLF)); + + struct iovec iov[3]; + int iovcnt = 3; + iov[0].iov_base = buf[0].buffer; + iov[0].iov_len = textbuf_length(buf); + iov[1].iov_base = (void*)data; + iov[1].iov_len = data_len; + iov[2].iov_base = (void*)CRLF; + iov[2].iov_len = sizeof(CRLF); + +#ifndef LIBYRMCDS_NO_INTERNAL_LOCK + int e = pthread_mutex_lock(&c->lock); + if( e != 0 ) { + errno = e; + return YRMCDS_SYSTEM_ERROR; + } +#endif // ! LIBYRMCDS_NO_INTERNAL_LOCK + + c->serial = c->serial + 1; + if( serial != NULL ) + *serial = c->serial; + + while( iovcnt > 0 ) { + ssize_t n = writev(c->sock, iov, iovcnt); + if( n == -1 ) { + if( errno == EINTR ) continue; + ret = YRMCDS_SYSTEM_ERROR; + goto OUT; + } + size_t n2 = (size_t)n; + while( n2 > 0 ) { + if( n2 < iov[0].iov_len ) { + iov[0].iov_base = (char*)iov[0].iov_base + n2; + iov[0].iov_len -= n2; + break; + } + n2 -= iov[0].iov_len; + iovcnt --; + if( iovcnt == 0 ) + break; + + int i; + for( i = 0; i < iovcnt; ++i ) + iov[i] = iov[i+1]; + } + } + + OUT: +#ifndef LIBYRMCDS_NO_INTERNAL_LOCK + pthread_mutex_unlock(&c->lock); +#endif + if( compressed ) + free((void*)data); + return ret; +} + + +// public functions. +yrmcds_error yrmcds_text_get(yrmcds* c, const char* key, size_t key_len, + int quiet, uint32_t* serial) { + if( key == NULL || key_len == 0 || quiet ) + return YRMCDS_BAD_ARGUMENT; + + yrmcds_error ret; + ret = check_key(key, key_len); + if( ret != YRMCDS_OK ) return ret; + + textbuf_t buf[1]; + textbuf_init(buf); + + textbuf_append_const_string(buf, "gets "); + textbuf_append_string(buf, key, key_len); + + return send_command(c, buf, serial); +} + +yrmcds_error yrmcds_text_touch(yrmcds* c, const char* key, size_t key_len, + uint32_t expire, int quiet, uint32_t* serial) { + if( key == NULL || key_len == 0 || quiet ) + return YRMCDS_BAD_ARGUMENT; + + yrmcds_error ret; + ret = check_key(key, key_len); + if( ret != YRMCDS_OK ) return ret; + + textbuf_t buf[1]; + textbuf_init(buf); + + textbuf_append_const_string(buf, "touch "); + textbuf_append_string(buf, key, key_len); + textbuf_append_char(buf, ' '); + textbuf_append_uint64(buf, expire); + + return send_command(c, buf, serial); +} + +yrmcds_error yrmcds_text_set(yrmcds* c, const char* key, size_t key_len, + const char* data, size_t data_len, + uint32_t flags, uint32_t expire, uint64_t cas, + int quiet, uint32_t* serial) { + return send_data(c, EXPAND_STR("set"), key, key_len, data, data_len, + flags, expire, cas, quiet, serial); +} + +yrmcds_error yrmcds_text_replace(yrmcds* c, const char* key, size_t key_len, + const char* data, size_t data_len, + uint32_t flags, uint32_t expire, uint64_t cas, + int quiet, uint32_t* serial) { + return send_data(c, EXPAND_STR("replace"), key, key_len, data, data_len, + flags, expire, cas, quiet, serial); +} + +yrmcds_error yrmcds_text_add(yrmcds* c, const char* key, size_t key_len, + const char* data, size_t data_len, + uint32_t flags, uint32_t expire, uint64_t cas, + int quiet, uint32_t* serial) { + return send_data(c, EXPAND_STR("add"), key, key_len, data, data_len, + flags, expire, cas, quiet, serial); +} + +yrmcds_error yrmcds_text_append(yrmcds* c, const char* key, size_t key_len, + const char* data, size_t data_len, + int quiet, uint32_t* serial) { + return send_data(c, EXPAND_STR("append"), key, key_len, data, data_len, + 0, 0, 0, quiet, serial); +} + +yrmcds_error yrmcds_text_prepend(yrmcds* c, const char* key, size_t key_len, + const char* data, size_t data_len, + int quiet, uint32_t* serial) { + return send_data(c, EXPAND_STR("prepend"), key, key_len, data, data_len, + 0, 0, 0, quiet, serial); +} + +yrmcds_error yrmcds_text_incr(yrmcds* c, const char* key, size_t key_len, + uint64_t value, int quiet, uint32_t* serial) { + if( key == NULL || key_len == 0 || quiet ) + return YRMCDS_BAD_ARGUMENT; + + yrmcds_error ret; + ret = check_key(key, key_len); + if( ret != YRMCDS_OK ) return ret; + + textbuf_t buf[1]; + textbuf_init(buf); + + textbuf_append_const_string(buf, "incr "); + textbuf_append_string(buf, key, key_len); + textbuf_append_char(buf, ' '); + textbuf_append_uint64(buf, value); + + return send_command(c, buf, serial); +} + +yrmcds_error yrmcds_text_decr(yrmcds* c, const char* key, size_t key_len, + uint64_t value, int quiet, uint32_t* serial) { + if( key == NULL || key_len == 0 || quiet ) + return YRMCDS_BAD_ARGUMENT; + + yrmcds_error ret; + ret = check_key(key, key_len); + if( ret != YRMCDS_OK ) return ret; + + textbuf_t buf[1]; + textbuf_init(buf); + + textbuf_append_const_string(buf, "decr "); + textbuf_append_string(buf, key, key_len); + textbuf_append_char(buf, ' '); + textbuf_append_uint64(buf, value); + + return send_command(c, buf, serial); +} + +yrmcds_error yrmcds_text_remove(yrmcds* c, const char* key, size_t key_len, + int quiet, uint32_t* serial) { + if( key == NULL || key_len == 0 || quiet ) + return YRMCDS_BAD_ARGUMENT; + + yrmcds_error ret; + ret = check_key(key, key_len); + if( ret != YRMCDS_OK ) return ret; + + textbuf_t buf[1]; + textbuf_init(buf); + + textbuf_append_const_string(buf, "delete "); + textbuf_append_string(buf, key, key_len); + + return send_command(c, buf, serial); +} + +yrmcds_error yrmcds_text_flush(yrmcds* c, uint32_t delay, + int quiet, uint32_t* serial) { + if( quiet ) + return YRMCDS_BAD_ARGUMENT; + + textbuf_t buf[1]; + textbuf_init(buf); + + textbuf_append_const_string(buf, "flush_all"); + if( delay != 0 ) { + textbuf_append_char(buf, ' '); + textbuf_append_uint64(buf, delay); + } + + return send_command(c, buf, serial); +} + +yrmcds_error yrmcds_text_version(yrmcds* c, uint32_t* serial) { + textbuf_t buf[1]; + textbuf_init(buf); + textbuf_append_const_string(buf, "version"); + return send_command(c, buf, serial); +} + +yrmcds_error yrmcds_text_quit(yrmcds* c, uint32_t* serial) { + textbuf_t buf[1]; + textbuf_init(buf); + textbuf_append_const_string(buf, "quit"); + return send_command(c, buf, serial); +} diff --git a/web/server/h2o/libh2o/deps/libyrmcds/set_compression.c b/web/server/h2o/libh2o/deps/libyrmcds/set_compression.c new file mode 100644 index 000000000..f3d393707 --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/set_compression.c @@ -0,0 +1,14 @@ +// (C) 2013-2015 Cybozu. + +#include "yrmcds.h" + +yrmcds_error yrmcds_set_compression(yrmcds* c, size_t threshold) { +#ifdef LIBYRMCDS_USE_LZ4 + if( c == NULL ) + return YRMCDS_BAD_ARGUMENT; + c->compress_size = threshold; + return YRMCDS_OK; +#else + return YRMCDS_NOT_IMPLEMENTED; +#endif +} diff --git a/web/server/h2o/libh2o/deps/libyrmcds/socket.c b/web/server/h2o/libh2o/deps/libyrmcds/socket.c new file mode 100644 index 000000000..6d30ee16c --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/socket.c @@ -0,0 +1,35 @@ +// (C) 2013 Cybozu et al. + +#include "yrmcds.h" + +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/time.h> + +yrmcds_error yrmcds_shutdown(yrmcds* c) { + if( c == NULL ) + return YRMCDS_BAD_ARGUMENT; + if( shutdown(c->sock, SHUT_RD) == -1 ) { + return YRMCDS_SYSTEM_ERROR; + } + return YRMCDS_OK; +} + +int yrmcds_fileno(yrmcds* c) { + return c->sock; +} + +yrmcds_error yrmcds_set_timeout(yrmcds* c, int timeout) { + if( c == NULL || timeout < 0 ) + return YRMCDS_BAD_ARGUMENT; + + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + + if( setsockopt(c->sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1 ) + return YRMCDS_SYSTEM_ERROR; + if( setsockopt(c->sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1 ) + return YRMCDS_SYSTEM_ERROR; + return YRMCDS_OK; +} diff --git a/web/server/h2o/libh2o/deps/libyrmcds/strerror.c b/web/server/h2o/libh2o/deps/libyrmcds/strerror.c new file mode 100644 index 000000000..e9359b596 --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/strerror.c @@ -0,0 +1,34 @@ +// (C) 2013-2015 Cybozu. + +#include "yrmcds.h" + +const char* yrmcds_strerror(yrmcds_error e) { + switch( e ) { + case YRMCDS_OK: + return "OK"; + case YRMCDS_SYSTEM_ERROR: + return "Check errno for details"; + case YRMCDS_BAD_ARGUMENT: + return "Bad argument"; + case YRMCDS_NOT_RESOLVED: + return "Host not found"; + case YRMCDS_TIMEOUT: + return "Timeout"; + case YRMCDS_DISCONNECTED: + return "Connection was reset by peer"; + case YRMCDS_OUT_OF_MEMORY: + return "Failed to allocate memory"; + case YRMCDS_COMPRESS_FAILED: + return "Failed to compress data"; + case YRMCDS_PROTOCOL_ERROR: + return "Received malformed packet"; + case YRMCDS_NOT_IMPLEMENTED: + return "Not implemented"; + case YRMCDS_IN_BINARY: + return "Connection is fixed for binary protocol"; + case YRMCDS_BAD_KEY: + return "Bad key"; + default: + return "Unknown error"; + }; +} diff --git a/web/server/h2o/libh2o/deps/libyrmcds/t/t.h b/web/server/h2o/libh2o/deps/libyrmcds/t/t.h new file mode 100644 index 000000000..fd79d59cd --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/t/t.h @@ -0,0 +1,75 @@ +// test utilities + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <yrmcds.h> + +static int n_failures = 0; + +static void check_error(yrmcds_error e) { + if( e == YRMCDS_OK ) + return; + + fprintf(stderr, "yrmcds error: %s\n", yrmcds_strerror(e)); + exit(1); +} + +static yrmcds* get_yrmcds(yrmcds* c) { + const char* host = getenv("YRMCDS_HOST"); + if( host == NULL ) { + return NULL; + } + uint16_t port = 11211; + if( getenv("YRMCDS_PORT") ) { + port = (uint16_t)atoi(getenv("YRMCDS_PORT")); + } + + check_error( yrmcds_connect(c, host, port) ); + return c; +} + +static void test_main(yrmcds* c); + +#define TEST_MAIN() static void test_main(yrmcds* c) + +int main(int argc, char** argv) { + yrmcds c_; + yrmcds* c = get_yrmcds(&c_); + if( c == NULL ) { + fprintf(stderr, "No YRMCDS_HOST. Skipped.\n"); + return 0; + } + + test_main(c); + yrmcds_close(c); + + if( n_failures > 0 ) { + fprintf(stderr, "%d tests failed.\n", n_failures); + return 1; + } + fprintf(stderr, "Passed.\n"); + return 0; +} + +#define DEF_TEST(name) void test_##name(yrmcds* c) + +#define CALL_TEST(name) \ + fprintf(stderr, "[%s]\n", #name); \ + test_##name(c); \ + uint32_t serial_##name; \ + check_error( yrmcds_flush(c, 0, 0, &serial_##name) ); \ + while( 1 ) { \ + yrmcds_response r; \ + check_error( yrmcds_recv(c, &r) ); \ + if( r.serial == serial_##name ) break; \ + } \ + sleep(1) + +#define ASSERT(expr, to_return) \ + if( ! (expr) ) { \ + fprintf(stderr, "assertion failure at line %d: %s\n", \ + __LINE__, #expr); \ + n_failures++; \ + if( to_return ) return; \ + } diff --git a/web/server/h2o/libh2o/deps/libyrmcds/t/text.c b/web/server/h2o/libh2o/deps/libyrmcds/t/text.c new file mode 100644 index 000000000..9419cb410 --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/t/text.c @@ -0,0 +1,294 @@ +#include "t.h" +#include <string.h> +#include <yrmcds.h> + +DEF_TEST(set) { + // quiet cannot be used + ASSERT( yrmcds_set(c, "a", 1, "a", 1, 0, 0, 0, 1, NULL) == YRMCDS_BAD_ARGUMENT, 0 ); + + uint32_t serial; + check_error( yrmcds_set(c, "hoge", 4, "hoge", 4, 1, 0, 0, 0, &serial) ); + + yrmcds_response r; + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + + check_error( yrmcds_get(c, "hoge", 4, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + ASSERT( r.data_len == 4, 0 ); + ASSERT( memcmp(r.data, "hoge", 4) == 0, 0 ); + ASSERT( r.flags == 1, 0 ); + + // CAS failure + check_error( yrmcds_set(c, "hoge", 4, "fu", 2, 2, 0, r.cas_unique+1, + 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_EXISTS, 1 ); + + check_error( yrmcds_getk(c, "hoge", 4, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + ASSERT( r.key_len == 4, 0 ); + ASSERT( memcmp(r.key, "hoge", 4) == 0, 0 ); + ASSERT( r.data_len == 4, 0 ); + ASSERT( memcmp(r.data, "hoge", 4) == 0, 0 ); + ASSERT( r.flags == 1, 0 ); + + // CAS success + check_error( yrmcds_set(c, "hoge", 4, "fu", 2, 4097, 0, r.cas_unique, + 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + + check_error( yrmcds_getk(c, "hoge", 4, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + ASSERT( r.key_len == 4, 0 ); + ASSERT( memcmp(r.key, "hoge", 4) == 0, 0 ); + ASSERT( r.data_len == 2, 0 ); + ASSERT( memcmp(r.data, "fu", 2) == 0, 0 ); + ASSERT( r.flags == 4097, 0 ); + + // set exptime + check_error( yrmcds_set(c, "hoge", 4, "999", 3, 0, 1, 0, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + + sleep(2); + + check_error( yrmcds_getk(c, "hoge", 4, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_NOTFOUND, 1 ); +} + +DEF_TEST(add) { + // quiet cannot be used + ASSERT( yrmcds_add(c, "a", 1, "a", 1, 0, 0, 0, 1, NULL) == YRMCDS_BAD_ARGUMENT, 0 ); + + uint32_t serial; + check_error( yrmcds_add(c, "hoge", 4, "hoge", 4, 1, 0, 0, 0, &serial) ); + + yrmcds_response r; + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + + check_error( yrmcds_add(c, "hoge", 4, "fu", 2, 16, 0, 0, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_NOTSTORED, 1 ); +} + +DEF_TEST(replace) { + // quiet cannot be used + ASSERT( yrmcds_replace(c, "a", 1, "a", 1, 0, 0, 0, 1, NULL) == YRMCDS_BAD_ARGUMENT, 0 ); + + uint32_t serial; + check_error( yrmcds_replace(c, "abc", 3, "hoge", 4, 1, 0, 0, 0, &serial) ); + + yrmcds_response r; + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_NOTSTORED, 1 ); + + check_error( yrmcds_set(c, "abc", 3, "def", 3, 16, 0, 0, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + + check_error( yrmcds_replace(c, "abc", 3, "hoge", 4, 1, 0, 0, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); +} + +DEF_TEST(append) { + uint32_t serial; + yrmcds_response r; + + // quiet cannot be used + ASSERT( yrmcds_append(c, "a", 1, "a", 1, 1, NULL) == YRMCDS_BAD_ARGUMENT, 0 ); + + check_error( yrmcds_append(c, "012", 3, "345", 3, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_NOTSTORED, 1 ); + + check_error( yrmcds_set(c, "012", 3, "345", 3, 1, 0, 0, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + + check_error( yrmcds_append(c, "012", 3, "6789", 4, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + + check_error( yrmcds_getk(c, "012", 3, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + ASSERT( r.data_len == 7, 1 ); + ASSERT( memcmp(r.data, "3456789", 7) == 0, 0 ); +} + +DEF_TEST(prepend) { + uint32_t serial; + yrmcds_response r; + + // quiet cannot be used + ASSERT( yrmcds_prepend(c, "a", 1, "a", 1, 1, NULL) == YRMCDS_BAD_ARGUMENT, 0 ); + + check_error( yrmcds_prepend(c, "012", 3, "345", 3, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_NOTSTORED, 1 ); + + check_error( yrmcds_set(c, "012", 3, "345", 3, 1, 0, 0, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + + check_error( yrmcds_prepend(c, "012", 3, "6789", 4, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + + check_error( yrmcds_getk(c, "012", 3, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + ASSERT( r.data_len == 7, 1 ); + ASSERT( memcmp(r.data, "6789345", 7) == 0, 0 ); +} + +DEF_TEST(touch) { + uint32_t serial; + yrmcds_response r; + + // quiet cannot be used + ASSERT( yrmcds_touch(c, "a", 1, 0, 1, NULL) == YRMCDS_BAD_ARGUMENT, 0 ); + + check_error( yrmcds_set(c, "a", 1, "345", 3, 0, 1, 0, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + + check_error( yrmcds_touch(c, "a", 1, 0, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + + sleep(2); + + check_error( yrmcds_getk(c, "a", 1, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); +} + +DEF_TEST(incdec) { + uint32_t serial; + yrmcds_response r; + + // quiet cannot be used + ASSERT( yrmcds_incr(c, "a", 1, 100, 1, NULL) == YRMCDS_BAD_ARGUMENT, 0 ); + ASSERT( yrmcds_decr(c, "a", 1, 100, 1, NULL) == YRMCDS_BAD_ARGUMENT, 0 ); + + check_error( yrmcds_incr(c, "a", 1, 100, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_NOTFOUND, 0 ); + + check_error( yrmcds_decr(c, "a", 1, 100, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_NOTFOUND, 0 ); + + check_error( yrmcds_set(c, "a", 1, "345", 3, 0, 1, 0, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + + check_error( yrmcds_incr(c, "a", 1, 1000, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + ASSERT( r.value == 1345, 1 ); + + check_error( yrmcds_decr(c, "a", 1, 1, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + ASSERT( r.value == 1344, 1 ); + + check_error( yrmcds_getk(c, "a", 1, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + ASSERT( r.data_len == 4, 1 ); + ASSERT( memcmp(r.data, "1344", 4) == 0, 0 ); +} + +DEF_TEST(remove) { + uint32_t serial; + yrmcds_response r; + + // quiet cannot be used + ASSERT( yrmcds_remove(c, "a", 1, 1, NULL) == YRMCDS_BAD_ARGUMENT, 0 ); + + check_error( yrmcds_remove(c, "uuu", 3, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_NOTFOUND, 0 ); + + check_error( yrmcds_set(c, "uuu", 3, "345", 3, 111, 0, 0, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + + check_error( yrmcds_remove(c, "uuu", 3, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + + check_error( yrmcds_getk(c, "uuu", 3, 0, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_NOTFOUND, 1 ); +} + +DEF_TEST(version) { + uint32_t serial; + yrmcds_response r; + + check_error( yrmcds_version(c, &serial) ); + check_error( yrmcds_recv(c, &r) ); + ASSERT( r.serial == serial, 0 ); + ASSERT( r.status == YRMCDS_STATUS_OK, 1 ); + ASSERT( r.data_len > 0, 1 ); + fprintf(stderr, "version=%.*s\n", (int)r.data_len, r.data); +} + +TEST_MAIN() { + check_error( yrmcds_text_mode(c) ); + + CALL_TEST(set); + CALL_TEST(add); + CALL_TEST(replace); + CALL_TEST(append); + CALL_TEST(prepend); + CALL_TEST(touch); + CALL_TEST(incdec); + CALL_TEST(remove); + CALL_TEST(version); +} diff --git a/web/server/h2o/libh2o/deps/libyrmcds/text_mode.c b/web/server/h2o/libh2o/deps/libyrmcds/text_mode.c new file mode 100644 index 000000000..77f950863 --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/text_mode.c @@ -0,0 +1,32 @@ +// (C) 2016 Cybozu. + +#include "yrmcds.h" + +#include <errno.h> + +yrmcds_error yrmcds_text_mode(yrmcds* c) { + if( c == NULL ) + return YRMCDS_BAD_ARGUMENT; + +#ifndef LIBYRMCDS_NO_INTERNAL_LOCK + int e = pthread_mutex_lock(&c->lock); + if( e != 0 ) { + errno = e; + return YRMCDS_SYSTEM_ERROR; + } +#endif // ! LIBYRMCDS_NO_INTERNAL_LOCK + + yrmcds_error ret = YRMCDS_OK; + if( c->serial != 0 ) { + ret = YRMCDS_IN_BINARY; + goto OUT; + } + + c->text_mode = 1; + + OUT: +#ifndef LIBYRMCDS_NO_INTERNAL_LOCK + pthread_mutex_unlock(&c->lock); +#endif + return ret; +} diff --git a/web/server/h2o/libh2o/deps/libyrmcds/yc-cnt.c b/web/server/h2o/libh2o/deps/libyrmcds/yc-cnt.c new file mode 100644 index 000000000..f25178c2b --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/yc-cnt.c @@ -0,0 +1,306 @@ +#include "yrmcds.h" +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static const char USAGE[] = + "Usage: yc-cnt [-s SERVER] [-p PORT] [-h] SUBCOMMAND\n" + "\n" + "Subcommands:\n" + " noop\n" + " get NAME\n" + " acquire RESOURCES MAXIMUM\n" + " release RESOURCES\n" + " stats\n" + " dump\n"; + +static const char DEFAULT_SERVER[] = "localhost"; +static const uint16_t DEFAULT_PORT = 11215; + +static void recv_or_die(yrmcds_cnt* c, yrmcds_cnt_response* r, uint32_t serial) { + yrmcds_error e; + while( 1 ) { + e = yrmcds_cnt_recv(c, r); + if( e != YRMCDS_OK ) { + fprintf(stderr, "yc-cnt: failed to recv: %s\n", + yrmcds_strerror(e)); + yrmcds_cnt_close(c); + exit(1); + } + if( r->serial == serial ) + break; + } +} + +static void cmd_noop(yrmcds_cnt* c) { + yrmcds_error e; + uint32_t serial; + + e = yrmcds_cnt_noop(c, &serial); + if( e != YRMCDS_OK ) { + fprintf(stderr, "yc-cnt: failed to send noop: %s\n", + yrmcds_strerror(e)); + yrmcds_cnt_close(c); + exit(1); + } + + yrmcds_cnt_response r; + recv_or_die(c, &r, serial); + + if( r.status == YRMCDS_STATUS_OK ) { + puts("OK"); + } else { + printf("ERROR: %02x %.*s\n", r.status, (int)r.body_length, r.body); + yrmcds_cnt_close(c); + exit(2); + } +} + +static void cmd_get(yrmcds_cnt* c, + const char* name, size_t name_len) { + yrmcds_error e; + uint32_t serial; + + e = yrmcds_cnt_get(c, name, name_len, &serial); + if( e != YRMCDS_OK ) { + fprintf(stderr, "yc-cnt: failed to send get: %s\n", + yrmcds_strerror(e)); + yrmcds_cnt_close(c); + exit(1); + } + + yrmcds_cnt_response r; + recv_or_die(c, &r, serial); + + if( r.status == YRMCDS_STATUS_OK ) { + printf("%" PRIu32 "\n", r.current_consumption); + } else { + printf("ERROR: %02x %.*s\n", r.status, (int)r.body_length, r.body); + yrmcds_cnt_close(c); + exit(2); + } +} + +static void cmd_acquire(yrmcds_cnt* c, + const char* name, size_t name_len, + uint32_t resouces, uint32_t initial) { + yrmcds_error e; + uint32_t serial; + + e = yrmcds_cnt_acquire(c, name, name_len, resouces, initial, &serial); + if( e != YRMCDS_OK ) { + fprintf(stderr, "yc-cnt: failed to send acquire: %s\n", + yrmcds_strerror(e)); + yrmcds_cnt_close(c); + exit(1); + } + + yrmcds_cnt_response r; + recv_or_die(c, &r, serial); + + if( r.status == YRMCDS_STATUS_OK ) { + printf("%" PRIu32 "\n", r.resources); + } else { + printf("ERROR: %02x %.*s\n", r.status, (int)r.body_length, r.body); + yrmcds_cnt_close(c); + exit(2); + } +} + +static void cmd_release(yrmcds_cnt* c, + const char* name, size_t name_len, + uint32_t resouces) { + yrmcds_error e; + uint32_t serial; + + e = yrmcds_cnt_release(c, name, name_len, resouces, &serial); + if( e != YRMCDS_OK ) { + fprintf(stderr, "yc-cnt: failed to send release: %s\n", + yrmcds_strerror(e)); + yrmcds_cnt_close(c); + exit(1); + } + + yrmcds_cnt_response r; + recv_or_die(c, &r, serial); + + if( r.status == YRMCDS_STATUS_OK ) { + puts("OK"); + } else { + printf("ERROR: %02x %.*s\n", r.status, (int)r.body_length, r.body); + yrmcds_cnt_close(c); + exit(2); + } +} + +static void cmd_stats(yrmcds_cnt* c) { + yrmcds_error e; + uint32_t serial; + + e = yrmcds_cnt_stats(c, &serial); + if( e != YRMCDS_OK ) { + fprintf(stderr, "yc-cnt: failed to send stats: %s\n", + yrmcds_strerror(e)); + yrmcds_cnt_close(c); + exit(1); + } + + yrmcds_cnt_response r; + recv_or_die(c, &r, serial); + + if( r.status == YRMCDS_STATUS_OK ) { + size_t i; + for( i = 0; i < r.stats->count; ++i ) { + yrmcds_cnt_stat* stat = &r.stats->records[i]; + printf("%.*s %.*s\n", (int)stat->name_length, stat->name, + (int)stat->value_length, stat->value); + } + } else { + printf("ERROR: %02x %.*s\n", r.status, (int)r.body_length, r.body); + yrmcds_cnt_close(c); + exit(2); + } +} + +static void cmd_dump(yrmcds_cnt* c) { + yrmcds_error e; + uint32_t serial; + + e = yrmcds_cnt_dump(c, &serial); + if( e != YRMCDS_OK ) { + fprintf(stderr, "yc-cnt: failed to send stats: %s\n", + yrmcds_strerror(e)); + yrmcds_cnt_close(c); + exit(1); + } + + for(;;) { + yrmcds_cnt_response r; + recv_or_die(c, &r, serial); + + if( r.status != YRMCDS_STATUS_OK ) { + printf("ERROR: %02x %.*s\n", r.status, (int)r.body_length, r.body); + yrmcds_cnt_close(c); + exit(2); + } + if( r.body_length == 0 ) + break; + printf("%" PRIu32 " %" PRIu32 " %.*s\n", + r.current_consumption, r.max_consumption, + (int)r.name_length, r.name); + } +} + +void usage() { + fprintf(stderr, "%s", USAGE); + exit(1); +} + +int main(int argc, char** argv) { + const char* server = DEFAULT_SERVER; + uint16_t port = DEFAULT_PORT; + + while( 1 ) { + int n; + int c = getopt(argc, argv, "s:p:h"); + if( c == -1 ) break; + switch( c ) { + case 's': + server = optarg; + break; + case 'p': + n = atoi(optarg); + if( n <= 0 || n > 65535 ) { + fprintf(stderr, "yc-cnt: invalid TCP port.\n"); + return 1; + } + port = (uint16_t)n; + break; + case 'h': + usage(); + default: + return 1; + } + } + + if( optind == argc ) + usage(); + + argc -= optind; + argv += optind; + + yrmcds_cnt c; + yrmcds_error e = yrmcds_cnt_connect(&c, server, port); + if( e != YRMCDS_OK ) { + fprintf(stderr, "yc-cnt: failed to connect to '%s:%d': %s\n", + server, port, yrmcds_strerror(e)); + exit(1); + } + yrmcds_cnt_set_timeout(&c, 1); + + if( strcmp(argv[0], "noop") == 0 ) { + if( argc != 1 ) { + fprintf(stderr, "yc-cnt: invalid number of arguments.\n"); + goto EXIT; + } + cmd_noop(&c); + } else if( strcmp(argv[0], "get") == 0 ) { + if( argc != 2 ) { + fprintf(stderr, "yc-cnt: invalid number of arguments.\n"); + goto EXIT; + } + cmd_get(&c, argv[1], strlen(argv[1])); + } else if( strcmp(argv[0], "acquire") == 0 ){ + if( argc != 4 ) { + fprintf(stderr, "yc-cnt: invalid number of arguments.\n"); + goto EXIT; + } + uint32_t resources, initial; + if( sscanf(argv[2], "%" PRIu32, &resources) != 1 ) { + fprintf(stderr, "yc-cnt: RESOURCES must be a unsigned 4-byte integer.\n"); + goto EXIT; + } + if( sscanf(argv[3], "%" PRIu32, &initial) != 1 ) { + fprintf(stderr, "yc-cnt: MAXIMUM must be a unsigned 4-byte integer.\n"); + goto EXIT; + } + cmd_acquire(&c, argv[1], strlen(argv[1]), resources, initial); + } else if( strcmp(argv[0], "release") == 0 ){ + if( argc != 3 ) { + fprintf(stderr, "yc-cnt: invalid number of arguments.\n"); + goto EXIT; + } + uint32_t resources; + if( sscanf(argv[2], "%" PRIu32, &resources) != 1 ) { + fprintf(stderr, "yc-cnt: RESOURCES must be a unsigned 4-byte integer.\n"); + goto EXIT; + } + cmd_release(&c, argv[1], strlen(argv[1]), resources); + } else if( strcmp(argv[0], "stats") == 0 ) { + if( argc != 1 ) { + fprintf(stderr, "yc-cnt: invalid number of arguments.\n"); + goto EXIT; + } + cmd_stats(&c); + } else if( strcmp(argv[0], "dump") == 0 ) { + if( argc != 1 ) { + fprintf(stderr, "yc-cnt: invalid number of arguments.\n"); + goto EXIT; + } + cmd_dump(&c); + } else { + fprintf(stderr, "yc-cnt: unknown command: %s\n", argv[0]); + goto EXIT; + } + + yrmcds_cnt_close(&c); + return 0; + +EXIT: + yrmcds_cnt_close(&c); + exit(1); +} diff --git a/web/server/h2o/libh2o/deps/libyrmcds/yc.c b/web/server/h2o/libh2o/deps/libyrmcds/yc.c new file mode 100644 index 000000000..78d3df45d --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/yc.c @@ -0,0 +1,1099 @@ +// (C) 2013-2015 Cybozu. + +#include "yrmcds.h" + +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static const uint16_t DEFAULT_PORT = 11211; +static const char DEFAULT_SERVER[] = "localhost"; +static const size_t DEFAULT_COMPRESS = 16384; +static int debug = 0; +static int quiet = 0; + +static void version() { + printf("yc with libyrmcds " LIBYRMCDS_VERSION "\n"); +} + +static void usage() { + printf("Usage: yc " + "[-h] [-v] [-d] [-t] [-s SERVER] [-p PORT] [-c COMPRESS] COMMAND ...\n\n" + "Options:\n" + " -h print help and exit.\n" + " -v print version information.\n" + " -d turn on debug messages.\n" + " -t turn on text protocol mode.\n" + " -q Use quiet commands, if possible.\n" + " -s connect to SERVER. Default: localhost\n" + " -p TCP port number. Default: 11211\n" + " -c compression threshold. Default: 16384\n\n" + "Commands:\n" + " noop\n" + " ping the server.\n" + " get KEY\n" + " get an object.\n" + " getk KEY\n" + " get an object with key.\n" + " gat KEY EXPIRE\n" + " get and touch an object.\n" + " gatk KEY EXPIRE\n" + " get and touch an object with key.\n" + " lag KEY\n" + " lock and get an object.\n" + " lagk KEY\n" + " lock and get an object with key.\n" + " touch KEY EXPIRE\n" + " touch an object.\n" + " set KEY FILE [EXPIRE [FLAGS [CAS]]]\n" + " store FILE data. If FILE is \"-\", stdin is used.\n" + " replace KEY FILE [EXPIRE [FLAGS [CAS]]]\n" + " update an existing object. FILE is the same as set.\n" + " add KEY FILE [EXPIRE [FLAGS [CAS]]]\n" + " create a new object. FILE is the same as set.\n" + " rau KEY FILE [EXPIRE [FLAGS]]\n" + " replace a locked object then unlock it.\n" + " Since this command always fails, do not use this.\n" + " incr KEY VALUE [INITIAL [EXPIRE]]\n" + " increments an exiting object's value by VALUE.\n" + " If INITIAL is given, new object is created when KEY\n" + " is not found. EXPIRE is used only when an object is\n" + " created.\n" + " decr KEY VALUE [INITIAL [EXPIRE]]\n" + " decrements an exiting object's value by VALUE.\n" + " If INITIAL is given, new object is created when KEY\n" + " is not found. EXPIRE is used only when an object is\n" + " created.\n" + " append KEY FILE\n" + " append FILE data FILE is the same as set.\n" + " prepend KEY FILE\n" + " prepend FILE data FILE is the same as set.\n" + " delete KEY\n" + " delete an object.\n" + " lock KEY\n" + " locks an object.\n" + " unlock KEY\n" + " this command always fails. Do not use this.\n" + " unlockall\n" + " this command has no effect.\n" + " flush [DELAY]\n" + " flush all unlocked items immediately or after DELAY seconds.\n" + " stat [settings|items|sizes]\n" + " obtain general or specified statistics.\n" + " keys [PREFIX]\n" + " dump keys matching PREFIX.\n" + " version\n" + " shows the server version.\n" + " quit\n" + " just quits. Not much interesting.\n" + ); +} + +static void print_response(const yrmcds_response* r) { + fprintf(stderr, "dump response:\n" + " serial: %u\n" + " length: %lu\n" + " status: 0x%04x\n" + " command: 0x%02x\n" + " cas: %" PRIu64 "\n" + " flags: 0x%08x\n" + " value: %" PRIu64 "\n", + r->serial, (unsigned long)r->length, r->status, r->command, + r->cas_unique, r->flags, r->value); + if( r->key_len ) + fprintf(stderr, " key: %.*s (%lu bytes)\n", + (int)r->key_len, r->key, (unsigned long)r->key_len); + if( r->data_len ) + fprintf(stderr, " data: %.*s (%lu bytes)\n", + (int)r->data_len, r->data, (unsigned long)r->data_len); +} + +static void write_data(const yrmcds_response* r) { + const char* p = r->data; + size_t to_write = r->data_len; + while( to_write > 0 ) { + ssize_t n = write(STDOUT_FILENO, p, to_write); + if( n == -1 ) return; + p += n; + to_write -= (size_t)n; + } + // writing a newline breaks data equality... + //char nl = '\n'; + //write(STDOUT_FILENO, &nl, 1); +} + +static size_t read_data(const char* filename, char** pdata) { + int fd; + if( strcmp(filename, "-") == 0 ) { + fd = STDIN_FILENO; + } else { + fd = open(filename, O_RDONLY); + if( fd == -1 ) return 0; + } + + size_t data_len = 0; + size_t capacity = 1 << 20; + *pdata = (char*)malloc(capacity); + if( *pdata == NULL ) return 0; + while( 1 ) { + if( (capacity - data_len) < (1 << 20) ) { + char* new_data = (char*)realloc(*pdata, capacity * 2); + if( new_data == NULL ) { + free(*pdata); + *pdata = NULL; + return 0; + } + *pdata = new_data; + capacity *= 2; + } + ssize_t n = read(fd, *pdata + data_len, 1 << 20); + if( n == -1 ) { + free(*pdata); + *pdata = NULL; + return 0; + } + if( n == 0 ) break; + data_len += (size_t)n; + } + + if( fd != STDIN_FILENO ) + close(fd); + return data_len; +} + + +#define CHECK_ERROR(e) \ + if( e != 0 ) { \ + if( e == YRMCDS_SYSTEM_ERROR ) { \ + fprintf(stderr, "system error: %s\n", strerror(errno)); \ + } else { \ + fprintf(stderr, "yrmcds error: %s\n", yrmcds_strerror(e)); \ + } \ + return 2; \ + } + +#define CHECK_RESPONSE(r) \ + if( r->status != YRMCDS_STATUS_OK ) { \ + fprintf(stderr, "Command failed: 0x%04x %.*s\n", \ + r->status, (int)r->data_len, r->data); \ + return 3; \ + } + +int cmd_noop(int argc, char** argv, yrmcds* s) { + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e = yrmcds_noop(s, &serial); + CHECK_ERROR(e); + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + printf("OK\n"); + return 0; +} + +int cmd_get(int argc, char** argv, yrmcds* s) { + if( argc != 1 ) { + fprintf(stderr, "Wrong number of arguments.\n"); + return 1; + } + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e = yrmcds_get(s, argv[0], strlen(argv[0]), quiet, &serial); + CHECK_ERROR(e); + if( quiet ) { + e = yrmcds_noop(s, &serial); + CHECK_ERROR(e); + } + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + while( 1 ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + if( r->serial == serial ) + break; + } + write_data(r); + return 0; +} + +int cmd_getk(int argc, char** argv, yrmcds* s) { + if( argc != 1 ) { + fprintf(stderr, "Wrong number of arguments.\n"); + return 1; + } + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e = yrmcds_getk(s, argv[0], strlen(argv[0]), quiet, &serial); + CHECK_ERROR(e); + if( quiet ) { + e = yrmcds_noop(s, &serial); + CHECK_ERROR(e); + } + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + while( 1 ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + if( r->serial == serial ) + break; + } + write_data(r); + return 0; +} + +int cmd_gat(int argc, char** argv, yrmcds* s) { + if( argc != 2 ) { + fprintf(stderr, "Wrong number of arguments.\n"); + return 1; + } + const char* key = argv[0]; + uint32_t expire = (uint32_t)strtoull(argv[1], NULL, 0); + + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e = yrmcds_get_touch(s, key, strlen(key), expire, quiet, &serial); + CHECK_ERROR(e); + if( quiet ) { + e = yrmcds_noop(s, &serial); + CHECK_ERROR(e); + } + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + while( 1 ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + if( r->serial == serial ) + break; + } + write_data(r); + return 0; +} + +int cmd_gatk(int argc, char** argv, yrmcds* s) { + if( argc != 2 ) { + fprintf(stderr, "Wrong number of arguments.\n"); + return 1; + } + const char* key = argv[0]; + uint32_t expire = (uint32_t)strtoull(argv[1], NULL, 0); + + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e = yrmcds_getk_touch(s, key, strlen(key), expire, quiet, &serial); + CHECK_ERROR(e); + if( quiet ) { + e = yrmcds_noop(s, &serial); + CHECK_ERROR(e); + } + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + while( 1 ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + if( r->serial == serial ) + break; + } + write_data(r); + return 0; +} + +int cmd_lag(int argc, char** argv, yrmcds* s) { + if( argc != 1 ) { + fprintf(stderr, "Wrong number of arguments.\n"); + return 1; + } + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e = yrmcds_lock_get(s, argv[0], strlen(argv[0]), quiet, &serial); + CHECK_ERROR(e); + if( quiet ) { + e = yrmcds_noop(s, &serial); + CHECK_ERROR(e); + } + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + while( 1 ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + if( r->serial == serial ) + break; + } + write_data(r); + fprintf(stderr, "Press enter to unlock.\n"); + getchar(); + return 0; +} + +int cmd_lagk(int argc, char** argv, yrmcds* s) { + if( argc != 1 ) { + fprintf(stderr, "Wrong number of arguments.\n"); + return 1; + } + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e = yrmcds_lock_getk(s, argv[0], strlen(argv[0]), quiet, &serial); + CHECK_ERROR(e); + if( quiet ) { + e = yrmcds_noop(s, &serial); + CHECK_ERROR(e); + } + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + while( 1 ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + if( r->serial == serial ) + break; + } + write_data(r); + fprintf(stderr, "Press enter to unlock.\n"); + getchar(); + return 0; +} + +int cmd_touch(int argc, char** argv, yrmcds* s) { + if( argc != 2 ) { + fprintf(stderr, "Wrong number of arguments.\n"); + return 1; + } + const char* key = argv[0]; + uint32_t expire = (uint32_t)strtoull(argv[1], NULL, 0); + + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e = yrmcds_touch(s, key, strlen(key), expire, quiet, &serial); + CHECK_ERROR(e); + if( quiet ) { + e = yrmcds_noop(s, &serial); + CHECK_ERROR(e); + } + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + while( 1 ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + if( r->serial == serial ) + break; + } + return 0; +} + +int cmd_set(int argc, char** argv, yrmcds* s) { + if( argc < 2 || 5 < argc ) { + fprintf(stderr, "Wrong number of arguments.\n"); + return 1; + } + const char* key = argv[0]; + char* data = NULL; + size_t data_len = read_data(argv[1], &data); + if( data == NULL ) { + fprintf(stderr, "Failed to read data.\n"); + return 2; + } + uint32_t expire = 0; + uint32_t flags = 0; + uint64_t cas = 0; + + if( argc > 2 ) + expire = (uint32_t)strtoull(argv[2], NULL, 0); + if( argc > 3 ) + flags = (uint32_t)strtoull(argv[3], NULL, 0); + if( argc > 4 ) + cas = (uint64_t)strtoull(argv[4], NULL, 0); + + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e = yrmcds_set(s, key, strlen(key), data, data_len, + flags, expire, cas, quiet, &serial); + free(data); + CHECK_ERROR(e); + if( quiet ) { + e = yrmcds_noop(s, &serial); + CHECK_ERROR(e); + } + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + while( 1 ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + if( r->serial == serial ) + break; + } + return 0; +} + +int cmd_replace(int argc, char** argv, yrmcds* s) { + if( argc < 2 || 5 < argc ) { + fprintf(stderr, "Wrong number of arguments.\n"); + return 1; + } + const char* key = argv[0]; + char* data = NULL; + size_t data_len = read_data(argv[1], &data); + if( data == NULL ) { + fprintf(stderr, "Failed to read data.\n"); + return 2; + } + uint32_t expire = 0; + uint32_t flags = 0; + uint64_t cas = 0; + + if( argc > 2 ) + expire = (uint32_t)strtoull(argv[2], NULL, 0); + if( argc > 3 ) + flags = (uint32_t)strtoull(argv[3], NULL, 0); + if( argc > 4 ) + cas = (uint64_t)strtoull(argv[4], NULL, 0); + + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e = yrmcds_replace(s, key, strlen(key), data, data_len, + flags, expire, cas, quiet, &serial); + free(data); + CHECK_ERROR(e); + if( quiet ) { + e = yrmcds_noop(s, &serial); + CHECK_ERROR(e); + } + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + while( 1 ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + if( r->serial == serial ) + break; + } + return 0; +} + +int cmd_add(int argc, char** argv, yrmcds* s) { + if( argc < 2 || 5 < argc ) { + fprintf(stderr, "Wrong number of arguments.\n"); + return 1; + } + const char* key = argv[0]; + char* data = NULL; + size_t data_len = read_data(argv[1], &data); + if( data == NULL ) { + fprintf(stderr, "Failed to read data.\n"); + return 2; + } + uint32_t expire = 0; + uint32_t flags = 0; + uint64_t cas = 0; + + if( argc > 2 ) + expire = (uint32_t)strtoull(argv[2], NULL, 0); + if( argc > 3 ) + flags = (uint32_t)strtoull(argv[3], NULL, 0); + if( argc > 4 ) + cas = (uint64_t)strtoull(argv[4], NULL, 0); + + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e = yrmcds_add(s, key, strlen(key), data, data_len, + flags, expire, cas, quiet, &serial); + free(data); + CHECK_ERROR(e); + if( quiet ) { + e = yrmcds_noop(s, &serial); + CHECK_ERROR(e); + } + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + while( 1 ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + if( r->serial == serial ) + break; + } + return 0; +} + +int cmd_rau(int argc, char** argv, yrmcds* s) { + if( argc < 2 || 4 < argc ) { + fprintf(stderr, "Wrong number of arguments.\n"); + return 1; + } + const char* key = argv[0]; + char* data = NULL; + size_t data_len = read_data(argv[1], &data); + if( data == NULL ) { + fprintf(stderr, "Failed to read data.\n"); + return 2; + } + uint32_t expire = 0; + uint32_t flags = 0; + + if( argc > 2 ) + expire = (uint32_t)strtoull(argv[2], NULL, 0); + if( argc > 3 ) + flags = (uint32_t)strtoull(argv[3], NULL, 0); + + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e = yrmcds_replace_unlock(s, key, strlen(key), data, data_len, + flags, expire, quiet, &serial); + free(data); + CHECK_ERROR(e); + if( quiet ) { + e = yrmcds_noop(s, &serial); + CHECK_ERROR(e); + } + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + while( 1 ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + if( r->serial == serial ) + break; + } + return 0; +} + +int cmd_incr(int argc, char** argv, yrmcds* s) { + if( argc < 2 || 4 < argc ) { + fprintf(stderr, "Wrong number of arguments.\n"); + return 1; + } + const char* key = argv[0]; + uint64_t value = (uint64_t)strtoull(argv[1], NULL, 0); + uint64_t initial = 0; + uint32_t expire = ~(uint32_t)0; + + if( argc > 2 ) { + initial = (uint64_t)strtoull(argv[2], NULL, 0); + expire = 0; + } + if( argc > 3 ) + expire = (uint32_t)strtoull(argv[3], NULL, 0); + + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e; + if( argc == 2 ) { + e = yrmcds_incr(s, key, strlen(key), value, quiet, &serial); + } else { + e = yrmcds_incr2(s, key, strlen(key), value, initial, expire, + quiet, &serial); + } + CHECK_ERROR(e); + if( quiet ) { + e = yrmcds_noop(s, &serial); + CHECK_ERROR(e); + } + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + while( 1 ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + if( r->serial == serial ) + break; + } + printf("%" PRIu64 "\n", r->value); + return 0; +} + +int cmd_decr(int argc, char** argv, yrmcds* s) { + if( argc < 2 || 4 < argc ) { + fprintf(stderr, "Wrong number of arguments.\n"); + return 1; + } + const char* key = argv[0]; + uint64_t value = (uint64_t)strtoull(argv[1], NULL, 0); + uint64_t initial = 0; + uint32_t expire = ~(uint32_t)0; + + if( argc > 2 ) { + initial = (uint64_t)strtoull(argv[2], NULL, 0); + expire = 0; + } + if( argc > 3 ) + expire = (uint32_t)strtoull(argv[3], NULL, 0); + + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e; + if( argc == 2 ) { + e = yrmcds_decr(s, key, strlen(key), value, quiet, &serial); + } else { + e = yrmcds_decr2(s, key, strlen(key), value, initial, expire, + quiet, &serial); + } + CHECK_ERROR(e); + if( quiet ) { + e = yrmcds_noop(s, &serial); + CHECK_ERROR(e); + } + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + while( 1 ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + if( r->serial == serial ) + break; + } + printf("%" PRIu64 "\n", r->value); + return 0; +} + +int cmd_append(int argc, char** argv, yrmcds* s) { + if( argc != 2 ) { + fprintf(stderr, "Wrong number of arguments.\n"); + return 1; + } + const char* key = argv[0]; + char* data = NULL; + size_t data_len = read_data(argv[1], &data); + if( data == NULL ) { + fprintf(stderr, "Failed to read data.\n"); + return 2; + } + + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e = yrmcds_append(s, key, strlen(key), + data, data_len, quiet, &serial); + free(data); + CHECK_ERROR(e); + if( quiet ) { + e = yrmcds_noop(s, &serial); + CHECK_ERROR(e); + } + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + while( 1 ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + if( r->serial == serial ) + break; + } + return 0; +} + +int cmd_prepend(int argc, char** argv, yrmcds* s) { + if( argc != 2 ) { + fprintf(stderr, "Wrong number of arguments.\n"); + return 1; + } + const char* key = argv[0]; + char* data = NULL; + size_t data_len = read_data(argv[1], &data); + if( data == NULL ) { + fprintf(stderr, "Failed to read data.\n"); + return 2; + } + + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e = yrmcds_prepend(s, key, strlen(key), + data, data_len, quiet, &serial); + free(data); + CHECK_ERROR(e); + if( quiet ) { + e = yrmcds_noop(s, &serial); + CHECK_ERROR(e); + } + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + while( 1 ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + if( r->serial == serial ) + break; + } + return 0; +} + +int cmd_delete(int argc, char** argv, yrmcds* s) { + if( argc != 1 ) { + fprintf(stderr, "Wrong number of arguments.\n"); + return 1; + } + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e = yrmcds_remove(s, argv[0], strlen(argv[0]), quiet, &serial); + CHECK_ERROR(e); + if( quiet ) { + e = yrmcds_noop(s, &serial); + CHECK_ERROR(e); + } + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + while( 1 ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + if( r->serial == serial ) + break; + } + write_data(r); + return 0; +} + +int cmd_lock(int argc, char** argv, yrmcds* s) { + if( argc != 1 ) { + fprintf(stderr, "Wrong number of arguments.\n"); + return 1; + } + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e = yrmcds_lock(s, argv[0], strlen(argv[0]), quiet, &serial); + CHECK_ERROR(e); + if( quiet ) { + e = yrmcds_noop(s, &serial); + CHECK_ERROR(e); + } + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + while( 1 ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + if( r->serial == serial ) + break; + } + fprintf(stderr, "Press enter to unlock.\n"); + getchar(); + return 0; +} + +int cmd_unlock(int argc, char** argv, yrmcds* s) { + if( argc != 1 ) { + fprintf(stderr, "Wrong number of arguments.\n"); + return 1; + } + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e = yrmcds_unlock(s, argv[0], strlen(argv[0]), quiet, &serial); + CHECK_ERROR(e); + if( quiet ) { + e = yrmcds_noop(s, &serial); + CHECK_ERROR(e); + } + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + while( 1 ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + if( r->serial == serial ) + break; + } + return 0; +} + +int cmd_unlockall(int argc, char** argv, yrmcds* s) { + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e = yrmcds_unlockall(s, quiet, &serial); + CHECK_ERROR(e); + if( quiet ) { + e = yrmcds_noop(s, &serial); + CHECK_ERROR(e); + } + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + while( 1 ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + if( r->serial == serial ) + break; + } + return 0; +} + +int cmd_flush(int argc, char** argv, yrmcds* s) { + uint32_t delay = 0; + if( argc == 1 ) + delay = (uint32_t)strtoull(argv[0], NULL, 0); + + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e = yrmcds_flush(s, delay, quiet, &serial); + CHECK_ERROR(e); + if( quiet ) { + e = yrmcds_noop(s, &serial); + CHECK_ERROR(e); + } + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + while( 1 ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + if( r->serial == serial ) + break; + } + return 0; +} + +int cmd_stat(int argc, char** argv, yrmcds* s) { + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e; + if( argc > 0 ) { + if( strcmp(argv[0], "settings") == 0 ) { + e = yrmcds_stat_settings(s, &serial); + } else if( strcmp(argv[0], "items") == 0 ) { + e = yrmcds_stat_items(s, &serial); + } else if( strcmp(argv[0], "sizes") == 0 ) { + e = yrmcds_stat_sizes(s, &serial); + } else { + fprintf(stderr, "No such statistics.\n"); + return 1; + } + } else { + e = yrmcds_stat_general(s, &serial); + } + CHECK_ERROR(e); + while( 1 ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + if( r->key_len == 0 ) + break; + if( r->data_len == 0 ) + continue; + printf("%.*s: %.*s\n", (int)r->key_len, r->key, + (int)r->data_len, r->data); + } + return 0; +} + +int cmd_keys(int argc, char** argv, yrmcds* s) { + const char* prefix = NULL; + size_t prefix_len = 0; + if( argc == 1 ) { + prefix = argv[0]; + prefix_len = strlen(prefix); + } + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e = yrmcds_keys(s, prefix, prefix_len, &serial); + CHECK_ERROR(e); + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + while( 1 ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + if( r->serial != serial ) + continue; + if( r->key_len == 0 ) + break; + printf("%.*s\n", (int)r->key_len, r->key); + } + return 0; +} + +int cmd_version(int argc, char** argv, yrmcds* s) { + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e = yrmcds_version(s, &serial); + CHECK_ERROR(e); + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + printf("%.*s\n", (int)r->data_len, r->data); + return 0; +} + +int cmd_quit(int argc, char** argv, yrmcds* s) { + yrmcds_response r[1]; + uint32_t serial; + yrmcds_error e = yrmcds_quit(s, quiet, &serial); + CHECK_ERROR(e); + if( debug ) + fprintf(stderr, "request serial = %u\n", serial); + if( ! quiet ) { + e = yrmcds_recv(s, r); + CHECK_ERROR(e); + if( debug ) + print_response(r); + CHECK_RESPONSE(r); + } + return 0; +} + +int main(int argc, char** argv) { + const char* server = DEFAULT_SERVER; + uint16_t port = DEFAULT_PORT; + size_t compression = DEFAULT_COMPRESS; + int text_mode = 0; + + while( 1 ) { + int n; + int c = getopt(argc, argv, "s:p:c:dtqvh"); + if( c == -1 ) break; + switch( c ) { + case 's': + server = optarg; + break; + case 'p': + n = atoi(optarg); + if( n <= 0 || n > 65535 ) { + fprintf(stderr, "Invalid TCP port.\n"); + return 1; + } + port = (uint16_t)n; + break; + case 'c': + n = atoi(optarg); + if( n <= 0 ) { + fprintf(stderr, "Invalid compression thoreshold.\n"); + return 1; + } + compression = (size_t)n; + break; + case 'd': + debug = 1; + break; + case 't': + text_mode = 1; + break; + case 'q': + quiet = 1; + break; + case 'v': + version(); + return 0; + case 'h': + usage(); + return 0; + default: + return 1; + } + } + + if( optind == argc ) { + usage(); + return 0; + } + + const char* cmd = argv[optind]; + argc -= optind + 1; + argv += optind + 1; + + yrmcds s[1]; + yrmcds_error e = yrmcds_connect(s, server, port); + CHECK_ERROR(e); + if( text_mode ) { + e = yrmcds_text_mode(s); + CHECK_ERROR(e); + } + e = yrmcds_set_compression(s, compression); + if( e != 0 && e != YRMCDS_NOT_IMPLEMENTED ) { + yrmcds_close(s); + CHECK_ERROR(e); + } + + int ret = 1; +#define do_cmd(name) \ + if( strcmp(cmd, #name) == 0 ) { \ + ret = cmd_##name(argc, argv, s); \ + goto OUT; \ + } + + do_cmd(noop); + do_cmd(get); + do_cmd(getk); + do_cmd(gat); + do_cmd(gatk); + do_cmd(lag); + do_cmd(lagk); + do_cmd(touch); + do_cmd(set); + do_cmd(replace); + do_cmd(add); + do_cmd(rau); + do_cmd(incr); + do_cmd(decr); + do_cmd(append); + do_cmd(prepend); + do_cmd(delete); + do_cmd(lock); + do_cmd(unlock); + do_cmd(unlockall); + do_cmd(flush); + do_cmd(stat); + do_cmd(keys); + do_cmd(version); + do_cmd(quit); + + fprintf(stderr, "No such command: %s\n", cmd); + + OUT: + yrmcds_close(s); + return ret; +} diff --git a/web/server/h2o/libh2o/deps/libyrmcds/yrmcds.h b/web/server/h2o/libh2o/deps/libyrmcds/yrmcds.h new file mode 100644 index 000000000..758f1bdc9 --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/yrmcds.h @@ -0,0 +1,1071 @@ +/** @file yrmcds.h + * libyrmcds public API. + * (C) 2013-2016 Cybozu. + */ + +#pragma once + +#ifndef YRMCDS_H_INCLUDED +#define YRMCDS_H_INCLUDED + +/// Library version string such as "1.3.0". +#define LIBYRMCDS_VERSION "1.3.0" + +/// Library version number such as 10201. +#define LIBYRMCDS_VERSION_NUMBER 10300 + +#include <pthread.h> +#include <stddef.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Data structure of a connection to memcached/yrmcds server. + */ +typedef struct { + int sock; ///< the socket file descriptor. + + /* for sending */ + pthread_mutex_t lock; ///< guard lock to serialize sends. + uint32_t serial; ///< last issued serial number. + size_t compress_size; ///< threshold data size for LZ4 compression. + + /* for receiving */ + char* recvbuf; ///< received data buffer. + size_t capacity; ///< buffer capacity + size_t used; ///< used bytes. + size_t last_size; ///< size of the last response. + char* decompressed; ///< decompressed data. + int invalid; ///< invalid flag. + + /* for text mode */ + int text_mode; ///< text mode flag. + uint32_t rserial; ///< serial emulation. +} yrmcds; + + +/** + * Server status codes. + */ +typedef enum { + YRMCDS_STATUS_OK = 0, + YRMCDS_STATUS_NOTFOUND = 0x0001, + YRMCDS_STATUS_EXISTS = 0x0002, + YRMCDS_STATUS_TOOLARGEVALUE = 0x0003, + YRMCDS_STATUS_INVALID = 0x0004, + YRMCDS_STATUS_NOTSTORED = 0x0005, + YRMCDS_STATUS_NONNUMERIC = 0x0006, + YRMCDS_STATUS_LOCKED = 0x0010, + YRMCDS_STATUS_NOTLOCKED = 0x0011, + YRMCDS_STATUS_UNKNOWNCOMMAND = 0x0081, + YRMCDS_STATUS_OUTOFMEMORY = 0x0082, + + YRMCDS_STATUS_OTHER = 0xffff, ///< unknown error in text protocol. +} yrmcds_status; + + +/** + * Binary commands. + */ +typedef enum { + YRMCDS_CMD_GET = '\x00', + YRMCDS_CMD_SET = '\x01', + YRMCDS_CMD_ADD = '\x02', + YRMCDS_CMD_REPLACE = '\x03', + YRMCDS_CMD_DELETE = '\x04', + YRMCDS_CMD_INCREMENT = '\x05', + YRMCDS_CMD_DECREMENT = '\x06', + YRMCDS_CMD_QUIT = '\x07', + YRMCDS_CMD_FLUSH = '\x08', + YRMCDS_CMD_GETQ = '\x09', + YRMCDS_CMD_NOOP = '\x0a', + YRMCDS_CMD_VERSION = '\x0b', + YRMCDS_CMD_GETK = '\x0c', + YRMCDS_CMD_GETKQ = '\x0d', + YRMCDS_CMD_APPEND = '\x0e', + YRMCDS_CMD_PREPEND = '\x0f', + YRMCDS_CMD_STAT = '\x10', + YRMCDS_CMD_SETQ = '\x11', + YRMCDS_CMD_ADDQ = '\x12', + YRMCDS_CMD_REPLACEQ = '\x13', + YRMCDS_CMD_DELETEQ = '\x14', + YRMCDS_CMD_INCREMENTQ = '\x15', + YRMCDS_CMD_DECREMENTQ = '\x16', + YRMCDS_CMD_QUITQ = '\x17', + YRMCDS_CMD_FLUSHQ = '\x18', + YRMCDS_CMD_APPENDQ = '\x19', + YRMCDS_CMD_PREPENDQ = '\x1a', + YRMCDS_CMD_TOUCH = '\x1c', + YRMCDS_CMD_GAT = '\x1d', + YRMCDS_CMD_GATQ = '\x1e', + YRMCDS_CMD_GATK = '\x23', + YRMCDS_CMD_GATKQ = '\x24', + + YRMCDS_CMD_LOCK = '\x40', + YRMCDS_CMD_LOCKQ = '\x41', + YRMCDS_CMD_UNLOCK = '\x42', + YRMCDS_CMD_UNLOCKQ = '\x43', + YRMCDS_CMD_UNLOCKALL = '\x44', + YRMCDS_CMD_UNLOCKALLQ = '\x45', + YRMCDS_CMD_LAG = '\x46', + YRMCDS_CMD_LAGQ = '\x47', + YRMCDS_CMD_LAGK = '\x48', + YRMCDS_CMD_LAGKQ = '\x49', + YRMCDS_CMD_RAU = '\x4a', + YRMCDS_CMD_RAUQ = '\x4b', + + YRMCDS_CMD_KEYS = '\x50', + + YRMCDS_CMD_BOTTOM // place this at the bottom. +} yrmcds_command; + + +/** + * Data structure to store a response packet. + */ +typedef struct { + uint32_t serial; ///< serial number of the corresponding request. + size_t length; ///< the length of the response packet. + yrmcds_status status; ///< the response status. + yrmcds_command command; ///< the request command. + uint64_t cas_unique; ///< CAS unique value. + uint32_t flags; ///< The object's flags. + const char* key; ///< Returned key for GetK/GaTK/LaGK. + size_t key_len; ///< The length of \p key. + const char* data; ///< Returned data for Get commands. + size_t data_len; ///< The length of \p data. + uint64_t value; ///< The new value after Increment or Decrement. +} yrmcds_response; + + +/** + * Library error numbers. + */ +typedef enum { + YRMCDS_OK = 0, + YRMCDS_SYSTEM_ERROR, ///< check \p errno for details. + YRMCDS_BAD_ARGUMENT, ///< bad arguments. + YRMCDS_NOT_RESOLVED, ///< host name cannot be resolved. + YRMCDS_TIMEOUT, ///< time out for some network operations. + YRMCDS_DISCONNECTED, ///< connection was reset unexpectedly. + YRMCDS_OUT_OF_MEMORY, ///< malloc/realloc failed. + YRMCDS_COMPRESS_FAILED, ///< LZ4 compression failed. + YRMCDS_PROTOCOL_ERROR, ///< received malformed packet. + YRMCDS_NOT_IMPLEMENTED, ///< the function is not available. + YRMCDS_IN_BINARY, ///< connection is fixed for binary protocol. + YRMCDS_BAD_KEY, ///< bad key. +} yrmcds_error; + + +/** + * Reserved flag bits. + */ +typedef enum { + YRMCDS_FLAG_COMPRESS = 1 << 30, ///< transparent LZ4 compression. +} yrmcds_flags; + + +/** + * @defgroup yrmcds_functions Public functions + * @{ + */ + +/** + * Return a string to describe a library error. + * @param e An error number returned from a library function. + * @return A pointer to a constant string. + */ +const char* yrmcds_strerror(yrmcds_error e); + + +/** + * Connecct to a memcached/yrmcds server. + * @param c A pointer to ::yrmcds. + * @param node The server name. + * @param port TCP port number of the server (normally 11211). + * @return 0 if connected successfully. Other values indicate an error. + * + * This function connects to a memcached/yrmcds server and initializes + * \p c. TCP_NODELAY flag will be set for the returned socket. + */ +yrmcds_error yrmcds_connect(yrmcds* c, const char* node, uint16_t port); + + +/** + * Close the connection. + * @param c A pointer to ::yrmcds. + * @return 0 if \p c is valid. Other values indicate an error. + * + * This function closes the connection and frees buffers in \p c. + */ +yrmcds_error yrmcds_close(yrmcds* c); + + +/** + * Shutdown the receiving end of the socket. + * @param c A pointer to ::yrmcds. + * @return 0 if \p c is valid. Other values indicate an error. + * + * This function simply calls \p shutdown system call with \p SHUT_RD . + * This can be used to interrupt a thread waiting in ::yrmcds_recv. + * + * Note that interrupted ::yrmcds_recv will return ::YRMCDS_DISCONNECTED. + * + * \see https://github.com/cybozu/libyrmcds/issues/8 + */ +yrmcds_error yrmcds_shutdown(yrmcds* c); + + +/** + * Turn on text protocol mode. + * @param c A pointer to ::yrmcds. + * @return 0 if succeeded. Other values indicate an error. + * + * This function puts the connection into text protocol mode. + * \p c should be a newly connected object; if any (binary) request + * has been sent, this function will return an error. + * + * Text protocol mode has overheads and limitations; most notably, + * \p quiet option for command sending functions cannot be enabled. + */ +yrmcds_error yrmcds_text_mode(yrmcds* c); + + +/** + * Enable/disable (de)compression for large objects. + * @param c A pointer to ::yrmcds. + * @param threshold The threshold for compression. + * @return 0 if arguments are valid. Other values indicate an error. + * + * This function enables transparent compression using LZ4 if \p threshold + * is greater than 0. If \p threshold is 0, then compression is disabled. + * + * The compression is disabled by default. + * + * If the library is built without LZ4, this function always return + * ::YRMCDS_NOT_IMPLEMENTED. + * + * Note that ::YRMCDS_FLAG_COMPRESS bit in the flags of compressed objects + * will be used by the library iff the compression is enabled. + */ +yrmcds_error yrmcds_set_compression(yrmcds* c, size_t threshold); + + +/** + * Return the underlying socket in ::yrmcds. + * @param c A pointer to ::yrmcds. + * @return A UNIX file descriptor of a socket. + */ +int yrmcds_fileno(yrmcds* c); + + +/** + * Set timeout seconds for send/recv operations. + * @param c A pointer to ::yrmcds. + * @param timeout Seconds before network operations time out. + * @return 0 if arguments are valid. Other values indicate an error. + */ +yrmcds_error yrmcds_set_timeout(yrmcds* c, int timeout); + + +/** + * Receives a response packet. + * @param c A pointer to ::yrmcds. + * @param r A pointer to ::yrmcds_response. + * @return 0 if succeeded. Other values indicate an error. + * + * This function receives a response packet. If no response is available, + * the function will be blocked. For each \p c, only one thread can use + * this function, though command sending functions can be used in parallel. + * + * The response data stored in \p r keep valid until the next call of this + * function or until ::yrmcds_close is called. \p r can be reused for the + * next call of ::yrmcds_recv. + * + * This will return ::YRMCDS_DISCONNECTED when the socket is closed or + * ::yrmcds_shutdown is called from another thread. + */ +yrmcds_error yrmcds_recv(yrmcds* c, yrmcds_response* r); + + +/** + * Send Noop command. + * @param c A pointer to ::yrmcds. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends Noop command to the server. + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error yrmcds_noop(yrmcds* c, uint32_t* serial); + + +/** + * Send Get/GetQ command. + * @param c A pointer to ::yrmcds. + * @param key Key data. + * @param key_len Length of \p key. + * @param quiet 0 to send Get, other values to send GetQ. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends Get/GetQ command to the server. + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error yrmcds_get(yrmcds* c, const char* key, size_t key_len, + int quiet, uint32_t* serial); + + +/** + * Send GetK/GetKQ command. + * @param c A pointer to ::yrmcds. + * @param key Key data. + * @param key_len Length of \p key. + * @param quiet 0 to send GetK, other values to send GetKQ. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends GetK/GetKQ command to the server. + * Unlike yrmcds_get(), the response to this request brings the key. + * + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error yrmcds_getk(yrmcds* c, const char* key, size_t key_len, + int quiet, uint32_t* serial); + + +/** + * Send GaT/GaTQ command. + * @param c A pointer to ::yrmcds. + * @param key Key data. + * @param key_len Length of \p key. + * @param expire Expiration time. 0 disables expiration. + * @param quiet 0 to send GaT, other values to send GaTQ. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends GaT/GaTQ (get and touch) command to the server. + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error yrmcds_get_touch(yrmcds* c, const char* key, size_t key_len, + uint32_t expire, int quiet, uint32_t* serial); + + +/** + * Send GaTK/GaTKQ command. + * @param c A pointer to ::yrmcds. + * @param key Key data. + * @param key_len Length of \p key. + * @param expire Expiration time. 0 disables expiration. + * @param quiet 0 to send GaTK, other values to send GaTKQ. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends GaTK/GaTKQ (get and touch) command to the server. + * Unlike yrmcds_get_touch(), the response to this request brings the key. + * + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error yrmcds_getk_touch(yrmcds* c, const char* key, size_t key_len, + uint32_t expire, int quiet, uint32_t* serial); + + +/** + * Send LaG/LaGQ command. + * @param c A pointer to ::yrmcds. + * @param key Key data. + * @param key_len Length of \p key. + * @param quiet 0 to send LaG, other values to send LaGQ. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends LaG/LaGQ command to the server. + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error yrmcds_lock_get(yrmcds* c, const char* key, size_t key_len, + int quiet, uint32_t* serial); + + +/** + * Send LaGK/LaGKQ command. + * @param c A pointer to ::yrmcds. + * @param key Key data. + * @param key_len Length of \p key. + * @param quiet 0 to send LaGK, other values to send LaGKQ. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends LaGK/LaGKQ command to the server. + * Unlike yrmcds_lock_get(), the response to this request brings the key. + * + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error yrmcds_lock_getk(yrmcds* c, const char* key, size_t key_len, + int quiet, uint32_t* serial); + + +/** + * Send Touch command. + * @param c A pointer to ::yrmcds. + * @param key Key data. + * @param key_len Length of \p key. + * @param expire Expiration time. 0 disables expiration. + * @param quiet Reserved for future enhancement. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends Touch command to the server. + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error yrmcds_touch(yrmcds* c, const char* key, size_t key_len, + uint32_t expire, int quiet, uint32_t* serial); + + +/** + * Send Set/SetQ command. + * @param c A pointer to ::yrmcds. + * @param key Key data. + * @param key_len Length of \p key. + * @param data Data to be stored. + * @param data_len Length of \p data. + * @param flags Flags stored along with the data. + * @param expire Expiration time. 0 disables expiration. + * @param cas Try compare-and-swap. 0 disables CAS. + * @param quiet 0 to send Set, other values to send SetQ. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends Set/SetQ command to the server. + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error yrmcds_set(yrmcds* c, const char* key, size_t key_len, + const char* data, size_t data_len, + uint32_t flags, uint32_t expire, uint64_t cas, + int quiet, uint32_t* serial); + + +/** + * Send Replace/ReplaceQ command. + * @param c A pointer to ::yrmcds. + * @param key Key data. + * @param key_len Length of \p key. + * @param data Data to be stored. + * @param data_len Length of \p data. + * @param flags Flags stored along with the data. + * @param expire Expiration time. 0 disables expiration. + * @param cas Try compare-and-swap. 0 disables CAS. + * @param quiet 0 to send Replace, other values to send ReplaceQ. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends Replace/ReplaceQ command to the server. + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error yrmcds_replace(yrmcds* c, const char* key, size_t key_len, + const char* data, size_t data_len, + uint32_t flags, uint32_t expire, uint64_t cas, + int quiet, uint32_t* serial); + + +/** + * Send Add/AddQ command. + * @param c A pointer to ::yrmcds. + * @param key Key data. + * @param key_len Length of \p key. + * @param data Data to be stored. + * @param data_len Length of \p data. + * @param flags Flags stored along with the data. + * @param expire Expiration time. 0 disables expiration. + * @param cas Try compare-and-swap. 0 disables CAS. + * @param quiet 0 to send Add, other values to send AddQ. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends Add/AddQ command to the server. + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error yrmcds_add(yrmcds* c, const char* key, size_t key_len, + const char* data, size_t data_len, + uint32_t flags, uint32_t expire, uint64_t cas, + int quiet, uint32_t* serial); + + +/** + * Send RaU/RaUQ command. + * @param c A pointer to ::yrmcds. + * @param key Key data. + * @param key_len Length of \p key. + * @param data Data to be stored. + * @param data_len Length of \p data. + * @param flags Flags stored along with the data. + * @param expire Expiration time. 0 disables expiration. + * @param quiet 0 to send RaU, other values to send RaUQ. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends RaU/RaUQ (replace and unlock) command to the server. + * The command will fail unless the object is locked by the same session. + * + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error yrmcds_replace_unlock(yrmcds* c, const char* key, size_t key_len, + const char* data, size_t data_len, + uint32_t flags, uint32_t expire, + int quiet, uint32_t* serial); + + +/** + * Send Increment/IncrementQ command. + * @param c A pointer to ::yrmcds. + * @param key Key data. + * @param key_len Length of \p key. + * @param value Amount to add. + * @param quiet 0 to send Increment, other values to send IncrementQ. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends Increment/IncrementQ command to the server. + * If \p key is not found, the command will fail. + * + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error yrmcds_incr(yrmcds* c, const char* key, size_t key_len, + uint64_t value, int quiet, uint32_t* serial); + + +/** + * Send Increment/IncrementQ command. + * @param c A pointer to ::yrmcds. + * @param key Key data. + * @param key_len Length of \p key. + * @param value Amount to add. + * @param initial Initial value used when \p key does not exist. + * @param expire Expiration time. 0 disables expiration. + * @param quiet 0 to send Increment, other values to send IncrementQ. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends Increment/IncrementQ command to the server. + * Unlike yrmcds_incr(), this function creates a new object with + * \p initial and \p expire when \p key is not found. + * + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error yrmcds_incr2(yrmcds* c, const char* key, size_t key_len, + uint64_t value, uint64_t initial, uint32_t expire, + int quiet, uint32_t* serial); + + +/** + * Send Decrement/DecrementQ command. + * @param c A pointer to ::yrmcds. + * @param key Key data. + * @param key_len Length of \p key. + * @param value Amount to add. + * @param quiet 0 to send Decrement, other values to send DecrementQ. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends Decrement/DecrementQ command to the server. + * If \p key is not found, the command will fail. + * + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error yrmcds_decr(yrmcds* c, const char* key, size_t key_len, + uint64_t value, int quiet, uint32_t* serial); + + +/** + * Send Decrement/DecrementQ command. + * @param c A pointer to ::yrmcds. + * @param key Key data. + * @param key_len Length of \p key. + * @param value Amount to add. + * @param initial Initial value used when \p key does not exist. + * @param expire Expiration time. 0 disables expiration. + * @param quiet 0 to send Decrement, other values to send DecrementQ. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends Decrement/DecrementQ command to the server. + * Unlike yrmcds_decr(), this function creates a new object with + * \p initial and \p expire when \p key is not found. + * + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error yrmcds_decr2(yrmcds* c, const char* key, size_t key_len, + uint64_t value, uint64_t initial, uint32_t expire, + int quiet, uint32_t* serial); + + +/** + * Send Append/AppendQ command. + * @param c A pointer to ::yrmcds. + * @param key Key data. + * @param key_len Length of \p key. + * @param data Data. + * @param data_len Length of \p data. + * @param quiet 0 to send Append, other values to send AppendQ. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends Append/AppendQ command to the server. + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + * + * \b WARNING: if compression is enabled, this may collapse the data! + */ +yrmcds_error yrmcds_append(yrmcds* c, const char* key, size_t key_len, + const char* data, size_t data_len, + int quiet, uint32_t* serial); + + +/** + * Send Prepend/PrependQ command. + * @param c A pointer to ::yrmcds. + * @param key Key data. + * @param key_len Length of \p key. + * @param data Data. + * @param data_len Length of \p data. + * @param quiet 0 to send Prepend, other values to send PrependQ. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends Prepend/PrependQ command to the server. + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + * + * \b WARNING: if compression is enabled, this may collapse the data! + */ +yrmcds_error yrmcds_prepend(yrmcds* c, const char* key, size_t key_len, + const char* data, size_t data_len, + int quiet, uint32_t* serial); + + +/** + * Send Delete/DeleteQ command. + * @param c A pointer to ::yrmcds. + * @param key Key data. + * @param key_len Length of \p key. + * @param quiet 0 to send Delete, other values to send DeleteQ. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends Delete/DeleteQ command to the server. + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error yrmcds_remove(yrmcds* c, const char* key, size_t key_len, + int quiet, uint32_t* serial); + + +/** + * Send Lock/LockQ command. + * @param c A pointer to ::yrmcds. + * @param key Key data. + * @param key_len Length of \p key. + * @param quiet 0 to send Lock, other values to send LockQ. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends Lock/LockQ command to the server. + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error yrmcds_lock(yrmcds* c, const char* key, size_t key_len, + int quiet, uint32_t* serial); + + +/** + * Send Unlock/UnlockQ command. + * @param c A pointer to ::yrmcds. + * @param key Key data. + * @param key_len Length of \p key. + * @param quiet 0 to send Unlock, other values to send UnlockQ. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends Unlock/UnlockQ command to the server. + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error yrmcds_unlock(yrmcds* c, const char* key, size_t key_len, + int quiet, uint32_t* serial); + + +/** + * Send UnlockAll/UnlockAllQ command. + * @param c A pointer to ::yrmcds. + * @param quiet 0 to send UnlockAll, other values to send UnlockAllQ. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends UnlockAll/UnlockAllQ command to the server. + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error yrmcds_unlockall(yrmcds* c, int quiet, uint32_t* serial); + + +/** + * Send Flush/FlushQ command. + * @param c A pointer to ::yrmcds. + * @param delay delay seconds before flush. + * @param quiet 0 to send Flush, other values to send FlushQ. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends Flush/FlushQ command to the server. + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error yrmcds_flush(yrmcds* c, uint32_t delay, + int quiet, uint32_t* serial); + +/** + * Send Stat command to obtain general statistics. + * @param c A pointer to ::yrmcds. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + */ +yrmcds_error yrmcds_stat_general(yrmcds* c, uint32_t* serial); + + +/** + * Send Stat command to obtain setting statistics. + * @param c A pointer to ::yrmcds. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + */ +yrmcds_error yrmcds_stat_settings(yrmcds* c, uint32_t* serial); + + +/** + * Send Stat command to obtain item statistics. + * @param c A pointer to ::yrmcds. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + */ +yrmcds_error yrmcds_stat_items(yrmcds* c, uint32_t* serial); + + +/** + * Send Stat command to obtain size statistics. + * @param c A pointer to ::yrmcds. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + */ +yrmcds_error yrmcds_stat_sizes(yrmcds* c, uint32_t* serial); + + +/** + * Send Keys command to list all keys matching the given prefix. + * To retrieve all keys, pass \p NULL and 0 as \p prefix and \p prefix_len. + * @param c A pointer to ::yrmcds. + * @param prefix Prefix data. + * @param prefix_len Length of \p prefix. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + */ +yrmcds_error yrmcds_keys(yrmcds* c, const char* prefix, size_t prefix_len, + uint32_t* serial); + + +/** + * Send Version command. + * @param c A pointer to ::yrmcds. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + */ +yrmcds_error yrmcds_version(yrmcds* c, uint32_t* serial); + + +/** + * Send Quit/QuitQ command. + * @param c A pointer to ::yrmcds. + * @param quiet 0 to send Quit, other values to send QuitQ. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + */ +yrmcds_error yrmcds_quit(yrmcds* c, int quiet, uint32_t* serial); + + +/** + * @} + * @mainpage A memcached/yrmcds client library for C. + * + * - \ref yrmcds_functions + * - \ref yrmcds_cnt_functions + */ + +/** + * Data structure to store the reference to a statistic data for the counter extension. + */ +typedef struct { + const char* name; ///< the name of a statistic item + size_t name_length; ///< the length of \p name + const char* value; ///< the ASCII text information + size_t value_length; ///< the length of \p value +} yrmcds_cnt_stat; + +/** + * Data structure to store statistics for the counter extension. + */ +typedef struct { + yrmcds_cnt_stat* records; ///< the array of the statistic information + size_t count; ///< the number of statistics + size_t capacity; ///< the maximum number of records that \p records can hold. +} yrmcds_cnt_statistics; + +/** + * Data structure of a connection to yrmcds counter server. + */ +typedef struct { + pthread_mutex_t lock; ///< guard lock to serialize sends. + yrmcds_cnt_statistics stats; ///< the result of `stats` command. + char* recvbuf; ///< received data buffer. + size_t capacity; ///< buffer capacity. + size_t used; ///< used bytes. + size_t last_size; ///< size of the last response. + int sock; ///< the socket file descriptor. + int invalid; ///< invalid flag. + uint32_t serial; ///< last issued serial number. +} yrmcds_cnt; + +/** + * Server status codes for the counter extension. + */ +typedef enum { + YRMCDS_CNT_STATUS_OK = 0x00, + YRMCDS_CNT_STATUS_NOT_FOUND = 0x01, + YRMCDS_CNT_STATUS_INVALID = 0x04, + YRMCDS_CNT_STATUS_RESOURCE_NOT_AVAILABLE = 0x21, + YRMCDS_CNT_STATUS_NOT_ACQUIRED = 0x22, + YRMCDS_CNT_STATUS_UNKNOWN_COMMAND = 0x81, + YRMCDS_CNT_STATUS_OUT_OF_MEMORY = 0x82, +} yrmcds_cnt_status; + +/** + * Binary commands for the counter extension. + */ +typedef enum { + YRMCDS_CNT_CMD_NOOP = 0x00, + YRMCDS_CNT_CMD_GET = 0x01, + YRMCDS_CNT_CMD_ACQUIRE = 0x02, + YRMCDS_CNT_CMD_RELEASE = 0x03, + YRMCDS_CNT_CMD_STATS = 0x10, + YRMCDS_CNT_CMD_DUMP = 0x11, +} yrmcds_cnt_command; + +/** + * Data structure to store a response packet for the counter extension. + */ +typedef struct { + yrmcds_cnt_statistics* stats; ///< the result of `stats` command. + const char* body; ///< the pointer to the response body. + size_t body_length; ///< the body length of the response packet. + const char* name; ///< the name of the semaphore (only for Dump command). + size_t name_length; ///< the length of \p name (only for Dump command). + uint32_t serial; ///< serial number of the corresponding request. + uint32_t resources; ///< the number of acquired resources. + uint32_t current_consumption; ///< the current consumption of resources. + uint32_t max_consumption; ///< maximum consumption (only for Dump command). + uint8_t status; ///< the response status. + uint8_t command; ///< the request command. +} yrmcds_cnt_response; + +/** + * @defgroup yrmcds_cnt_functions Public functions for the counter extension + * @{ + */ + +/** + * Connecct to a yrmcds server. + * @param c A pointer to ::yrmcds_cnt. + * @param node The server name. + * @param port TCP port number of the server (normally 11215). + * @return 0 if connected successfully. Other values indicate an error. + * + * This function connects to a yrmcds server and initializes + * \p c. TCP_NODELAY flag will be set for the returned socket. + */ +yrmcds_error +yrmcds_cnt_connect(yrmcds_cnt* c, const char* node, uint16_t port); + +/** + * Close the connection. + * @param c A pointer to ::yrmcds_cnt. + * @return 0 if \p c is valid. Other values indicate an error. + * + * This function closes the connection and frees buffers in \p c. + */ +yrmcds_error +yrmcds_cnt_close(yrmcds_cnt* c); + +/** + * Shutdown the receiving end of the socket. + * @param c A pointer to ::yrmcds_cnt. + * @return 0 if \p c is valid. Other values indicate an error. + * + * This function simply calls \p shutdown system call with \p SHUT_RD . + * This can be used to interrupt a thread waiting in ::yrmcds_cnt_recv. + * + * Note that interrupted ::yrmcds_cnt_recv will return ::YRMCDS_DISCONNECTED. + * + * \see https://github.com/cybozu/libyrmcds/issues/8 + */ +yrmcds_error +yrmcds_cnt_shutdown(yrmcds_cnt* c); + +/** + * Return the underlying socket in ::yrmcds_cnt. + * @param c A pointer to ::yrmcds_cnt. + * @return A UNIX file descriptor of a socket. + */ +int +yrmcds_cnt_fileno(yrmcds_cnt* c); + +/** + * Set timeout seconds for send/recv operations. + * @param c A pointer to ::yrmcds_cnt. + * @param timeout Seconds before network operations time out. + * @return 0 if arguments are valid. Other values indicate an error. + */ +yrmcds_error +yrmcds_cnt_set_timeout(yrmcds_cnt* c, int timeout); + +/** + * Receives a response packet. + * @param c A pointer to ::yrmcds_cnt. + * @param r A pointer to ::yrmcds_cnt_response. + * @return 0 if succeeded. Other values indicate an error. + * + * This function receives a response packet. If no response is available, + * the function will be blocked. For each \p c, only one thread can use + * this function, though command sending functions can be used in parallel. + * + * The response data stored in \p r keep valid until the next call of this + * function or until yrmcds_cnt_close() is called. \p r can be reused for the + * next call of yrmcds_cnt_recv(). + */ +yrmcds_error +yrmcds_cnt_recv(yrmcds_cnt* c, yrmcds_cnt_response* r); + +/** + * Send Noop command. + * @param c A pointer to ::yrmcds_cnt. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends Noop command to the server. + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error +yrmcds_cnt_noop(yrmcds_cnt* c, uint32_t* serial); + +/** + * Send Get command. + * @param c A pointer to ::yrmcds_cnt. + * @param name Name. + * @param name_len Length of \p name. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends Get command to the server. + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error +yrmcds_cnt_get(yrmcds_cnt* c, + const char* name, size_t name_len, uint32_t* serial); + +/** + * Send Acquire command. + * @param c A pointer to ::yrmcds_cnt. + * @param name Name. + * @param name_len Length of \p name. + * @param resources The number of resources to acquire. + * @param maximum The maximum number of resources. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends Acquire command to the server. + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error +yrmcds_cnt_acquire(yrmcds_cnt* c, const char* name, size_t name_len, + uint32_t resources, uint32_t maximum, uint32_t* serial); + +/** + * Send Release command. + * @param c A pointer to ::yrmcds_cnt. + * @param name Name. + * @param name_len Length of \p name. + * @param resources The number of resources to release. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends Release command to the server. + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error +yrmcds_cnt_release(yrmcds_cnt* c, const char* name, size_t name_len, + uint32_t resources, uint32_t* serial); + +/** + * Send Stats command. + * @param c A pointer to ::yrmcds_cnt. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends Stats command to the server. + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error +yrmcds_cnt_stats(yrmcds_cnt* c, uint32_t* serial); + +/** + * Send Dump command. + * @param c A pointer to ::yrmcds_cnt. + * @param serial A pointer to \p uint32_t, or \p NULL. + * @return 0 if succeeded. Other values indicate an error. + * + * This function sends Dump command to the server. + * If \p serial is not \p NULL, the serial number of the request will be + * stored if the command was sent successfully. + */ +yrmcds_error +yrmcds_cnt_dump(yrmcds_cnt* c, uint32_t* serial); + +/** + * @} + */ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // YRMCDS_H_INCLUDED diff --git a/web/server/h2o/libh2o/deps/libyrmcds/yrmcds_portability.h b/web/server/h2o/libh2o/deps/libyrmcds/yrmcds_portability.h new file mode 100644 index 000000000..c9fd4d91a --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/yrmcds_portability.h @@ -0,0 +1,50 @@ +/** @file yrmcds_portability.h + * + * Private header file for OS portability. + * + * Some code are copied from a public domain source at: + * https://gist.github.com/yinyin/2027912 + * + * This is public domain, too. + */ + +#pragma once + +#ifndef YRMCDS_PORTABILITY_H_INCLUDED +#define YRMCDS_PORTABILITY_H_INCLUDED + +#if defined(__APPLE__) +# include <libkern/OSByteOrder.h> +# define htobe16(x) OSSwapHostToBigInt16(x) +# define htole16(x) OSSwapHostToLittleInt16(x) +# define be16toh(x) OSSwapBigToHostInt16(x) +# define le16toh(x) OSSwapLittleToHostInt16(x) +# define htobe32(x) OSSwapHostToBigInt32(x) +# define htole32(x) OSSwapHostToLittleInt32(x) +# define be32toh(x) OSSwapBigToHostInt32(x) +# define le32toh(x) OSSwapLittleToHostInt32(x) +# define htobe64(x) OSSwapHostToBigInt64(x) +# define htole64(x) OSSwapHostToLittleInt64(x) +# define be64toh(x) OSSwapBigToHostInt64(x) +# define le64toh(x) OSSwapLittleToHostInt64(x) +#elif defined(__linux__) +# include <endian.h> +#elif defined(sun) // Solaris +# include <sys/byteorder.h> +# define htobe16(x) BE_16(x) +# define htole16(x) LE_16(x) +# define be16toh(x) BE_IN16(x) +# define le16toh(x) LE_IN16(x) +# define htobe32(x) BE_32(x) +# define htole32(x) LE_32(x) +# define be32toh(x) BE_IN32(x) +# define le32toh(x) LE_IN32(x) +# define htobe64(x) BE_64(x) +# define htole64(x) LE_64(x) +# define be64toh(x) BE_IN64(x) +# define le64toh(x) LE_IN64(x) +#else // *BSD +# include <sys/endian.h> +#endif + +#endif // YRMCDS_PORTABILITY_H_INCLUDED diff --git a/web/server/h2o/libh2o/deps/libyrmcds/yrmcds_text.h b/web/server/h2o/libh2o/deps/libyrmcds/yrmcds_text.h new file mode 100644 index 000000000..d1cb69aa2 --- /dev/null +++ b/web/server/h2o/libh2o/deps/libyrmcds/yrmcds_text.h @@ -0,0 +1,47 @@ +/** @file yrmcds_text.h + * + * Private header file for text protocol. + */ + +#pragma once + +#ifndef YRMCDS_TEXT_H_INCLUDED +#define YRMCDS_TEXT_H_INCLUDED + +#include "yrmcds.h" + +yrmcds_error yrmcds_text_get(yrmcds* c, const char* key, size_t key_len, + int quiet, uint32_t* serial); +yrmcds_error yrmcds_text_touch(yrmcds* c, const char* key, size_t key_len, + uint32_t expire, int quiet, uint32_t* serial); +yrmcds_error yrmcds_text_set(yrmcds* c, const char* key, size_t key_len, + const char* data, size_t data_len, + uint32_t flags, uint32_t expire, uint64_t cas, + int quiet, uint32_t* serial); +yrmcds_error yrmcds_text_replace(yrmcds* c, const char* key, size_t key_len, + const char* data, size_t data_len, + uint32_t flags, uint32_t expire, uint64_t cas, + int quiet, uint32_t* serial); +yrmcds_error yrmcds_text_add(yrmcds* c, const char* key, size_t key_len, + const char* data, size_t data_len, + uint32_t flags, uint32_t expire, uint64_t cas, + int quiet, uint32_t* serial); +yrmcds_error yrmcds_text_append(yrmcds* c, const char* key, size_t key_len, + const char* data, size_t data_len, + int quiet, uint32_t* serial); +yrmcds_error yrmcds_text_prepend(yrmcds* c, const char* key, size_t key_len, + const char* data, size_t data_len, + int quiet, uint32_t* serial); +yrmcds_error yrmcds_text_incr(yrmcds* c, const char* key, size_t key_len, + uint64_t value, int quiet, uint32_t* serial); +yrmcds_error yrmcds_text_decr(yrmcds* c, const char* key, size_t key_len, + uint64_t value, int quiet, uint32_t* serial); +yrmcds_error yrmcds_text_remove(yrmcds* c, const char* key, size_t key_len, + int quiet, uint32_t* serial); +yrmcds_error yrmcds_text_flush(yrmcds* c, uint32_t delay, + int quiet, uint32_t* serial); +yrmcds_error yrmcds_text_version(yrmcds* c, uint32_t* serial); +yrmcds_error yrmcds_text_quit(yrmcds* c, uint32_t* serial); + + +#endif // YRMCDS_TEXT_H_INCLUDED |