diff --git a/ftk/AUTHORS b/ftk/AUTHORS new file mode 100644 index 0000000..8c6c0a4 --- /dev/null +++ b/ftk/AUTHORS @@ -0,0 +1,4 @@ +Daniel Sanders (dsanders@novell.com) +Andrew Hodgkinson (ahodgkinson@novell.com) + +... and many others ... diff --git a/ftk/COPYING b/ftk/COPYING new file mode 100644 index 0000000..3912109 --- /dev/null +++ b/ftk/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ftk/ChangeLog b/ftk/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/ftk/Doxyfile b/ftk/Doxyfile new file mode 100644 index 0000000..8d36b42 --- /dev/null +++ b/ftk/Doxyfile @@ -0,0 +1,281 @@ +#------------------------------------------------------------------------- +# Desc: Doxyfile 1.4.6 +# Tabs: 3 +# +# Copyright (c) 2006 Novell, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of version 2 of the GNU General Public +# License as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, contact Novell, Inc. +# +# To contact Novell about this file by physical or electronic mail, +# you may find current contact information at www.novell.com +# +# $Id$ +#------------------------------------------------------------------------- + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = "XFLAIM" +PROJECT_NUMBER = +OUTPUT_DIRECTORY = build/docs +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +USE_WINDOWS_ENCODING = YES +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = NO +STRIP_FROM_PATH = "" +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = YES +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 3 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +BUILTIN_STL_SUPPORT = NO +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +HIDE_UNDOC_MEMBERS = YES +HIDE_UNDOC_CLASSES = YES +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = NO +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = NO +SHOW_DIRECTORIES = NO +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = src/xflaim.h +FILE_PATTERNS = *.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 +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +USE_HTAGS = NO +VERBATIM_HEADERS = NO +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = YES +TREEVIEW_WIDTH = 250 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = NO +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +MAX_DOT_GRAPH_DEPTH = 1000 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/ftk/Makefile b/ftk/Makefile new file mode 100644 index 0000000..36d51f2 --- /dev/null +++ b/ftk/Makefile @@ -0,0 +1,1579 @@ +#------------------------------------------------------------------------- +# Desc: GNU makefile for FTK library +# Tabs: 3 +# +# Copyright (c) 2000-2006 Novell, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of version 2 of the GNU General Public +# License as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, contact Novell, Inc. +# +# To contact Novell about this file by physical or electronic mail, +# you may find current contact information at www.novell.com +# +# $Id: $ +#------------------------------------------------------------------------- + +############################################################################# +# +# Sample Usage: +# +# make clean debug all +# +############################################################################# + +# -- Project -- + +project_name = ftk +project_desc = FLAIM cross-platform toolkit library + +# -- Versions -- + +major_version = 1 +minor_version = 0 + +calc_svn_revision = + +ifneq (,$(findstring ignore-local-mods,$(MAKECMDGOALS))) + submake_targets += ignore-local-mods + ignore_local_mods = 1 +endif + +ifneq (,$(findstring ilm,$(MAKECMDGOALS))) + submake_targets += ilm + ignore_local_mods = 1 +endif + +ifdef ignore_local_mods + local_mods_ok = 1 +else + local_mods_ok = +endif + +ifneq (,$(findstring dist,$(MAKECMDGOALS))) + calc_svn_revision = 1 + ifndef ignore_local_mods + local_mods_ok = + endif +endif + +ifneq (,$(findstring rpm,$(MAKECMDGOALS))) + calc_svn_revision = 1 + ifndef ignore_local_mods + local_mods_ok = + endif +endif + +ifneq (,$(findstring pathinfo,$(MAKECMDGOALS))) + calc_svn_revision = 1 + ifndef ignore_local_mods + local_mods_ok = + endif +endif + +ifneq (,$(findstring changelog,$(MAKECMDGOALS))) + + calc_svn_revision = 1 + + # Get the info for this directory + + ifndef svn_user + $(error Must define svn_user= in environment or as a parameter) + endif + + ifndef svn_rev + $(error Must define svn_rev= in environment or as a parameter) + endif + + svnrevs = $(subst :, ,$(svn_rev)) + svn_low_rev = $(word 1,$(svnrevs)) + svn_high_rev = $(word 2,$(svnrevs)) + + svnurl0 := $(shell svn info) + svnurl1 = $(subst URL: ,URL:,$(svnurl0)) + svnurl2 = $(filter URL:%,$(svnurl1)) + svnurl3 = $(subst URL:,,$(svnurl2)) + svnurl = $(subst ://,://$(svn_user)@,$(svnurl3)) +endif + +ifdef calc_svn_revision + + # Get the info for all files. + + ifndef local_mods_ok + srevision := $(shell svnversion . -n) + + ifneq (,$(findstring M,$(srevision))) + $(error Local modifications found - please check in before making distro) + endif + + ifneq (,$(findstring :,$(srevision))) + $(error Mixed revisions in repository - please update before making distro) + endif + endif + + numdigits = $(words $(subst 9,9 ,$(subst 8,8 ,$(subst 7,7 ,\ + $(subst 6,6 ,$(subst 5,5 ,$(subst 4,4 ,$(subst 3,3 ,\ + $(subst 2,2 ,$(subst 1,1 ,$(subst 0,0 ,$(1)))))))))))) + revision0 := $(shell svn info -R) + revision1 = $(subst Last Changed Rev: ,LastChangedRev:,$(revision0)) + revision2 = $(filter LastChangedRev:%,$(revision1)) + revision3 = $(subst LastChangedRev:,,$(revision2)) + revision4 = $(sort $(revision3)) + revision5 = $(foreach num,$(revision4),$(call numdigits,$(num)):$(num)) + revision6 = $(sort $(revision5)) + revision7 = $(word $(words $(revision6)),$(revision6)) + svn_revision = $(word 2,$(subst :, ,$(revision7))) + +else + ifeq "$(wildcard SVNRevision.*)" "" + svn_revision = 0 + else + svn_revision = $(word 2,$(subst ., ,$(wildcard SVNRevision.*))) + endif +endif + +ifeq "$(svn_high_rev)" "" + svn_high_rev = $(svn_revision) +endif + +version = $(major_version).$(minor_version).$(svn_revision) + +so_current = 1 +so_revision = 0 +so_age = 0 +shared_lib_version = + +package_release_num = 1 + +# -- Paths initializations -- + +ifndef rpm_build_root + ifneq (,$(DESTDIR)) + rpm_build_root = $(DESTDIR)/ + else + rpm_build_root = + endif +endif + +install_prefix = /usr + +# -- RPM, SPEC file names + +package_proj_name = lib$(project_name) +package_proj_name_and_ver = $(package_proj_name)-$(version) + +# -- Target variables -- + +target_build_type = +usenativecc = yes +target_os_family = +target_processor_family = +target_word_size = +requested_word_size = +win_target = +unix_target = +netware_target = +submake_targets = + +# -- Enable command echoing -- + +ifneq (,$(findstring verbose,$(MAKECMDGOALS))) + submake_targets += verbose + ec = +else + ec = @ +endif + +# -- Determine if we are only cleaning -- + +util_targets = +test_targets = basictest +all_targets = rpms install libs all allutils test $(util_targets) $(test_targets) +found_targets = $(foreach target,$(MAKECMDGOALS),$(if $(findstring $(target),$(all_targets)),$(target),)) + +ifneq (,$(findstring clean,$(MAKECMDGOALS))) + do_clean = 1 + ifeq ($(if $(findstring 0,$(words $(found_targets))),1,0),0) + $(error Cannot specify other targets with clean target) + endif +else + do_clean = 0 +endif + +# -- Determine the host operating system -- + +ifndef host_os_family + ifneq (,$(findstring WIN,$(OS))) + host_os_family = win + endif +endif + +ifndef host_os_family + ifneq (,$(findstring Win,$(OS))) + host_os_family = win + endif +endif + +ifeq (,$(OSTYPE)) + ifneq (,$(RPM_OS)) + HOSTTYPE = $(RPM_OS) + endif +endif + +ifndef host_os_family + ifneq (,$(findstring Linux,$(OSTYPE))) + host_os_family = linux + endif +endif + +ifndef host_os_family + ifneq (,$(findstring linux,$(OSTYPE))) + host_os_family = linux + endif +endif + +ifndef host_os_family + ifneq (,$(findstring solaris,$(OSTYPE))) + host_os_family = solaris + endif +endif + +ifndef host_os_family + ifneq (,$(findstring darwin,$(OSTYPE))) + host_os_family = osx + endif +endif + +ifndef host_os_family + ifneq (,$(findstring aix,$(OSTYPE))) + host_os_family = aix + endif +endif + +ifndef host_os_family + ifneq (,$(findstring hpux,$(OSTYPE))) + host_os_family = hpux + endif +endif + +ifndef host_os_family + $(error Host operating system could not be determined. You may need to export HOSTTYPE from the environment.) +endif + +# -- Target build type -- + +ifndef target_build_type + ifneq (,$(findstring debug,$(MAKECMDGOALS))) + submake_targets += debug + target_build_type = debug + endif +endif + +ifndef target_build_type + ifneq (,$(findstring release,$(MAKECMDGOALS))) + submake_targets += release + target_build_type = release + endif +endif + +ifndef target_build_type + target_build_type = release +endif + +# -- Use non-native (i.e., gcc) compiler on Solaris, etc. + +ifneq (,$(findstring usegcc,$(MAKECMDGOALS))) + submake_targets += usegcc + usenativecc = no +endif + +# -- Override platform default word size? -- + +ifneq (,$(findstring 64bit,$(MAKECMDGOALS))) + submake_targets += 64bit + requested_word_size = 64 +endif + +ifneq (,$(findstring 32bit,$(MAKECMDGOALS))) + submake_targets += 32bit + requested_word_size = 32 +endif + +# -- Target operating system -- + +ifndef target_os_family + ifeq ($(host_os_family),linux) + unix_target = yes + target_os_family = linux + endif +endif + +ifndef target_os_family + ifeq ($(host_os_family),solaris) + unix_target = yes + target_os_family = solaris + endif +endif + +ifndef target_os_family + ifeq ($(host_os_family),osx) + unix_target = yes + target_os_family = osx + endif +endif + +ifndef target_os_family + ifeq ($(host_os_family),aix) + unix_target = yes + target_os_family = aix + endif +endif + +ifndef target_os_family + ifeq ($(host_os_family),hpux) + unix_target = yes + target_os_family = hpux + endif +endif + +ifneq (,$(findstring nlm,$(MAKECMDGOALS))) + submake_targets += nlm + netware_target = yes + target_os_family = netware + host_os_family = win +endif + +ifndef target_os_family + ifeq ($(host_os_family),win) + win_target = yes + target_os_family = win + endif +endif + +ifndef target_os_family + $(error Target operating system could not be determined) +endif + +# -- Host word size and processor -- + +host_native_word_size = +host_processor_family = +host_supported_word_sizes = + +ifneq (,$(PROCESSOR_ARCHITECTURE)) + HOSTTYPE = $(PROCESSOR_ARCHITECTURE) +endif + +ifeq (,$(HOSTTYPE)) + ifneq (,$(RPM_ARCH)) + HOSTTYPE = $(RPM_ARCH) + endif + $(error HOSTTYPE environment variable has not been set) +endif + +ifeq (,$(HOSTTYPE)) + $(error HOSTTYPE environment variable has not been set) +endif + +ifndef host_native_word_size + ifneq (,$(findstring x86_64,$(HOSTTYPE))) + host_processor_family = x86 + host_native_word_size = 64 + host_supported_word_sizes = 32 64 + endif +endif + +ifndef host_native_word_size + ifneq (,$(findstring x86,$(HOSTTYPE))) + host_processor_family = x86 + host_native_word_size = 32 + host_supported_word_sizes = 32 + endif +endif + +ifndef host_native_word_size + ifneq (,$(findstring 86,$(HOSTTYPE))) + host_processor_family = x86 + host_native_word_size = 32 + host_supported_word_sizes = 32 + endif +endif + +ifndef host_native_word_size + ifneq (,$(findstring ia64,$(HOSTTYPE))) + host_processor_family = ia64 + host_native_word_size = 64 + host_supported_word_sizes = 64 + endif +endif + +ifndef host_native_word_size + ifneq (,$(findstring s390x,$(HOSTTYPE))) + host_processor_family = s390 + host_native_word_size = 64 + host_supported_word_sizes = 31 64 + endif +endif + +ifndef host_native_word_size + ifneq (,$(findstring s390,$(HOSTTYPE))) + host_processor_family = s390 + host_native_word_size = 31 + host_supported_word_sizes = 31 + endif +endif + +ifndef host_native_word_size + ifneq (,$(findstring ppc64,$(HOSTTYPE))) + host_processor_family = powerpc + host_native_word_size = 64 + host_supported_word_sizes = 32 64 + endif +endif + +ifndef host_native_word_size + ifneq (,$(findstring ppc,$(HOSTTYPE))) + host_processor_family = powerpc + host_native_word_size = 32 + host_supported_word_sizes = 32 + endif +endif + +ifndef host_native_word_size + ifneq (,$(findstring sparc,$(HOSTTYPE))) + host_processor_family = sparc + host_native_word_size = 64 + host_supported_word_sizes = 32 64 + endif +endif + +ifndef host_native_word_size + ifneq (,$(findstring powerpc,$(HOSTTYPE))) + host_processor_family = powerpc + host_native_word_size = 32 + host_supported_word_sizes = 32 64 + endif +endif + +ifndef host_native_word_size + ifneq (,$(findstring rs6000,$(HOSTTYPE))) + host_processor_family = powerpc + host_native_word_size = 64 + host_supported_word_sizes = 32 64 + endif +endif + +ifndef host_native_word_size + ifneq (,$(findstring hppa,$(HOSTTYPE))) + host_processor_family = hppa + host_native_word_size = 64 + host_supported_word_sizes = 32 64 + endif +endif + +ifndef host_native_word_size + $(error Unable to determine host word size) +endif + +# -- Target word size and processor -- + +ifneq (,$(findstring nlm,$(MAKECMDGOALS))) + target_processor_family = x86 + target_word_size = 32 + target_supported_word_sizes = 32 +else + target_processor_family = $(host_processor_family) + target_word_size = $(host_native_word_size) + target_supported_word_sizes = $(host_supported_word_sizes) +endif + +ifdef requested_word_size + ifneq (,$(findstring $(requested_word_size),$(target_supported_word_sizes))) + target_word_size = $(requested_word_size) + else + $(error Unsupported target word size) + endif +endif + +# -- Helper functions -- + +define normpath +$(strip $(subst \,/,$(1))) +endef + +ifeq (win,$(host_os_family)) +define hostpath +$(strip $(subst /,\,$(1))) +endef +else +define hostpath +$(strip $(1)) +endef +endif + +ifeq (win,$(host_os_family)) +define ppath +$(strip $(subst \,\\,$(subst /,\,$(1)))) +endef +else +define ppath +$(strip $(1)) +endef +endif + +ifeq (win,$(host_os_family)) + define create_archive + -$(ec)$(call rmcmd,$(2)) + $(ec)cmd /C "cd $(call hostpath,$(1)) && $(call hostpath,$(tooldir)/7z) a -ttar -r $(call hostpath,$(2)).tar $(call hostpath,$(3))" + $(ec)cmd /C "cd $(call hostpath,$(1)) && $(call hostpath,$(tooldir)/7z) a -tgzip -r $(call hostpath,$(2)).tar.gz $(call hostpath,$(2)).tar" + $(ec)cmd /C "cd $(call hostpath,$(1)) && del $(call hostpath,$(2)).tar" + endef + + define extract_archive + $(ec)cmd /C "cd $(call hostpath,$(1)) && $(call hostpath,$(tooldir)/7z) x -y $(call hostpath,$(2)).tar.gz + $(ec)cmd /C "cd $(call hostpath,$(1)) && $(call hostpath,$(tooldir)/7z) x -y $(call hostpath,$(2)).tar + endef +else + define create_archive + -$(ec)$(call rmcmd,$(2)) + $(ec)tar cf $(2).tar -C $(1) $(3) + $(ec)gzip -f $(2).tar + $(ec)chmod 775 $(2).tar.gz + endef + + define extract_archive + $(ec)gunzip $(strip $(1))/$(2).tar.gz + $(ec)tar xvf $(strip $(1))/$(2).tar -C $(1) + endef +endif + +# Platform-specific commands, directories, etc. + +ifeq ($(host_os_family),win) + allprereqs = $(call hostpath,$+) + copycmd = copy /Y $(call hostpath,$(1)) $(call hostpath,$(2)) 1>NUL + dircopycmd = xcopy /Y /E /V /I $(call hostpath,$(1)) $(call hostpath,$(2)) + rmcmd = if exist $(call hostpath,$(1)) del /Q $(call hostpath,$(1)) 1>NUL + rmdircmd = if exist $(call hostpath,$(1)) rmdir /q /s $(call hostpath,$(1)) 1>NUL + mkdircmd = -if not exist $(call hostpath,$(1)) mkdir $(call hostpath,$(1)) + runtest = cmd /C "cd $(call hostpath,$(test_dir)) && $(1) -d" + topdir := $(call normpath,$(shell chdir)) +else + allprereqs = $+ + copycmd = cp -f $(1) $(2) + dircopycmd = cp -rf $(1) $(2) + rmcmd = rm -f $(1) + rmdircmd = rm -rf $(1) + mkdircmd = mkdir -p $(1) + runtest = sh -c "cd $(test_dir); ./$(1) -d; exit" + topdir := $(shell pwd) +endif + +# If this is an un-tar'd or un-zipped source package, the tools directory +# will be subordinate to the top directory. Otherwise, it will be +# a sibling to the top directory - which is how it is set up in the +# subversion repository. + +ifeq "$(wildcard $(topdir)/tools*)" "" + tooldir := $(dir $(topdir))tools/$(host_os_family) +else + tooldir := $(topdir)/tools/$(host_os_family) +endif + +# Files and Directories + +ifeq ($(target_word_size),64) + ifeq ($(target_os_family),linux) + lib_dir_name = lib64 + endif +endif + +ifndef lib_dir_name + lib_dir_name = lib +endif + +lib_install_dir = $(rpm_build_root)$(install_prefix)/$(lib_dir_name) +include_install_dir = $(rpm_build_root)$(install_prefix)/include +pkgconfig_install_dir = $(lib_install_dir)/pkgconfig +build_output_dir = $(topdir)/build +docs_output_dir = $(build_output_dir)/docs + +target_path = $(build_output_dir)/$(target_os_family)-$(target_processor_family)-$(target_word_size)/$(target_build_type) + +package_dir = $(target_path)/package +spec_dir = $(package_dir)/SPECS +spec_file = $(spec_dir)/$(package_proj_name).spec +package_sources_dir = $(package_dir)/SOURCES +package_bin_dir = $(package_dir)/BIN +package_build_dir = $(package_dir)/BUILD +package_rpms_dir = $(package_dir)/RPMS +package_srpms_dir = $(package_dir)/SRPMS +pkgconfig_file_name = $(package_proj_name).pc +pkgconfig_file = $(package_dir)/$(pkgconfig_file_name) + +package_stage_parent_dir = $(package_dir)/stage +package_stage_dir = $(package_stage_parent_dir)/$(package_proj_name_and_ver) +package_bin_stage_dir = $(package_stage_parent_dir)/$(package_proj_name_and_ver)/$(target_os_family)-$(target_processor_family)-$(target_word_size)/$(target_build_type) +package_lib_stage_dir = $(package_bin_stage_dir)/lib +package_shared_lib_stage_dir = $(package_lib_stage_dir)/shared +package_static_lib_stage_dir = $(package_lib_stage_dir)/static +package_util_stage_dir = $(package_bin_stage_dir)/util +package_inc_stage_dir = $(package_stage_parent_dir)/$(package_proj_name_and_ver)/include + +src_package_dir = $(package_sources_dir) +bin_package_dir = $(package_bin_dir) + +src_package_base_name = $(package_proj_name_and_ver) +bin_package_base_name = $(package_proj_name_and_ver)-$(target_os_family)-$(target_processor_family)-$(target_word_size)-bin + +src_package_name=$(src_package_base_name).tar.gz +bin_package_name=$(bin_package_base_name).tar.gz + +rpm_name = $(package_proj_name_and_ver)-$(package_release_num).$(HOSTTYPE).rpm +srpm_name = $(package_proj_name_and_ver)-$(package_release_num).src.rpm +develrpm_name = $(package_proj_name)-devel-$(version)-$(package_release_num).$(HOSTTYPE).rpm + +inc_dirs = src util + +util_dir = $(target_path)/util +test_dir = $(target_path)/test +sample_dir = $(target_path)/sample +lib_dir = $(target_path)/$(lib_dir_name) +shared_lib_dir = $(lib_dir)/shared +static_lib_dir = $(lib_dir)/static + +util_obj_dir = $(util_dir)/obj +test_obj_dir = $(test_dir)/obj +sample_obj_dir = $(sample_dir)/obj + +lib_obj_dir = $(lib_dir)/obj + +# -- Utility variables -- + +em := +sp := $(em) $(em) +percent := \045 +dollar := \044 +question := \077 +asterisk := \052 +dash := \055 + +# -- Tools -- + +libr = +exe_linker = +shared_linker = +compiler = + +ifdef unix_target + gprintf = printf +else + gprintf = $(call hostpath,$(tooldir)/printf.exe) +endif + +# Compiler definitions and flags + +ccflags = +ccdefs = + +ifeq ($(target_word_size),64) + ccdefs += FLM_64BIT +endif + +############################################################################## +# Win settings +############################################################################## +ifdef win_target + exe_suffix = .exe + obj_suffix = .obj + lib_prefix = + static_lib_suffix = .lib + shared_lib_suffix = .dll + libr = lib.exe + exe_linker = link.exe + shared_linker = link.exe + compiler = cl.exe + + # Compiler defines and flags + + ccflags += /nologo /c /GF /GR /J /MD /W4 /WX /Zi /Zp1 + ccdefs += _CRT_SECURE_NO_DEPRECATE + ccdefs += WIN32_LEAN_AND_MEAN + ccdefs += WIN32_EXTRA_LEAN + + ifeq ($(target_build_type),debug) + ccflags += /Ob1 /Od /RTC1 /Wp64 + ccdefs += FLM_DEBUG + else + ccflags += /O2 + endif + + # Linker switches + + shared_link_flags = \ + /DLL \ + /DEBUG /PDB:$(call hostpath,$(@:.dll=.pdb)) \ + /map:$(call hostpath,$(@:.dll=.map)) \ + /INCREMENTAL:NO \ + /NOLOGO \ + /OUT:$(call hostpath,$@) + + exe_link_flags = \ + /DEBUG /PDB:$(call hostpath,$(@:.exe=.pdb)) \ + /map:$(call hostpath,$(@:.exe=.map)) \ + /INCREMENTAL:NO \ + /FIXED:NO \ + /NOLOGO \ + /OUT:$(call hostpath,$@) + + # Libraries that our various components need to link against + + lib_link_libs = imagehlp.lib user32.lib rpcrt4.lib wsock32.lib + exe_link_libs = $(lib_link_libs) + + # Convert the list of defines into a proper set of command-line params + + ifdef ccdefs + ccdefine = $(foreach def,$(strip $(ccdefs)),/D$(def)) + endif + + # Same thing for the include dirs + + ccinclude = $(foreach inc_dir,$(strip $(inc_dirs)),/I$(subst @,$(sp),$(call hostpath,$(inc_dir)))) + + # Concatenate everything into the ccflags variable + + ccflags += $(ccdefine) $(ccinclude) +endif + +############################################################################## +# Linux/Unix settings +############################################################################## +ifdef unix_target + + ifneq ($(so_age),0) + shared_lib_version = $(so_current).$(so_revision).$(so_age) + else + ifneq ($(so_revision),0) + shared_lib_version = $(so_current).$(so_revision) + else + shared_lib_version = $(so_current) + endif + endif + + exe_suffix = + obj_suffix = .o + lib_prefix = lib + static_lib_suffix = .a + shared_lib_suffix = .so.$(shared_lib_version) + + compiler = g++ + exe_linker = g++ + shared_linker = g++ + + ifeq ($(target_os_family),osx) + libr = libtool + else + libr = ar + endif + + ifeq ($(usenativecc),yes) + ifeq ($(target_os_family),solaris) + compiler = CC + exe_linker = CC + shared_linker = ld + endif + endif + + ifeq ($(usenativecc),yes) + ifeq ($(target_os_family),aix) + compiler = xlC_r + exe_linker = xlC_r + shared_linker = xlC_r + endif + endif + + ifeq ($(usenativecc),yes) + ifeq ($(target_os_family),hpux) + compiler = aCC + exe_linker = aCC + shared_linker = aCC + endif + endif + + # Compiler defines and flags + + ifeq ($(compiler),g++) + ccflags += -Wall -Werror -fPIC + ifneq ($(target_processor_family),ia64) + ccflags += -m$(target_word_size) + endif + endif + + ifeq ($(target_os_family),linux) + + # Must support 64 bit file sizes - even for 32 bit builds. + + ccdefs += N_PLAT_UNIX _LARGEFILE64_SOURCE _FILE_OFFSET_BITS=64 + + ifdef jhome + inc_dirs += $(subst $(sp),@,"$(jhome)/include/linux") + endif + + ifeq ($(target_build_type),release) + ccflags += \ + -O \ + -foptimize-sibling-calls \ + -fstrength-reduce -fcse-follow-jumps \ + -fcse-skip-blocks \ + -frerun-cse-after-loop \ + -frerun-loop-opt \ + -fgcse \ + -fgcse-lm \ + -fgcse-sm \ + -fdelete-null-pointer-checks \ + -fexpensive-optimizations \ + -fregmove \ + -fsched-interblock \ + -fsched-spec \ + -fcaller-saves \ + -fpeephole2 \ + -freorder-blocks \ + -freorder-functions \ + -falign-functions \ + -falign-jumps \ + -falign-loops \ + -falign-labels \ + -fcrossjumping + endif + endif + + ifeq ($(target_os_family),solaris) + ifeq ($(usenativecc),yes) + ccflags += -KPIC -errwarn=%all -errtags -erroff=hidef,inllargeuse + ifeq ($(target_word_size),64) + ccflags += -xarch=generic64 + else + # Must support 64 bit file sizes - even for 32 bit builds. + + ccdefs += _LARGEFILE_SOURCE _FILE_OFFSET_BITS=64 + ccflags += -xarch=generic + endif + endif + endif + + ifeq ($(target_os_family),aix) + ifeq ($(usenativecc),yes) + ccflags += -qthreaded + ifeq ($(target_word_size),64) + ccflags += -q64 + else + # Must support 64 bit file sizes - even for 32 bit builds. + + ccflags += -q32 + ccdefs += _LARGEFILE_SOURCE _FILE_OFFSET_BITS=64 + endif + endif + endif + + ifeq ($(target_os_family),hpux) + ifeq ($(usenativecc),yes) + # Must support 64 bit file sizes - even for 32 bit builds. + + ccdefs += _LARGEFILE_SOURCE _FILE_OFFSET_BITS=64 + endif + endif + + ifeq ($(target_os_family),osx) + ccdefs += OSX + endif + + ccdefs += _REENTRANT + + ifeq ($(target_build_type),debug) + ccdefs += FLM_DEBUG + ccflags += -g + endif + + # Convert the list of defines into a proper set of command-line params + + ifdef ccdefs + ccdefine = $(foreach def,$(strip $(ccdefs)),-D$(def)) + endif + + # Same thing for the include dirs + + ccinclude = $(foreach inc_dir,$(strip $(inc_dirs)),-I$(subst @,$(sp),$(inc_dir))) + + # Concatenate everything into the ccflags variable + + ccflags += $(ccdefine) $(ccinclude) + + # Linker switches + + shared_link_flags = + link_flags = -o $@ + + ifeq ($(compiler),g++) + ifneq ($(target_processor_family),ia64) + shared_link_flags += -m$(target_word_size) + link_flags += -m$(target_word_size) + endif + endif + + lib_link_libs = -lpthread + exe_link_libs = -lpthread + + ifeq ($(target_os_family),linux) + lib_link_libs += -lrt -lstdc++ -ldl + exe_link_libs += -lrt -lstdc++ -ldl -lncurses + shared_link_flags += -shared -Wl,-Bsymbolic -fpic \ + -Wl,-soname,$(@F).1 -o $@ + endif + + ifeq ($(target_os_family),solaris) + link_flags += -R /usr/lib/lwp + shared_link_flags += -G -pic -o $@ + + ifeq ($(usenativecc),yes) + ifeq ($(target_word_size),64) + link_flags += -xarch=generic64 + else + link_flags += -xarch=generic + endif + endif + + lib_link_libs += -lm -lc -ldl -lsocket -lnsl -lrt + exe_link_libs += -lm -lc -ldl -lsocket -lnsl -lrt -lcurses + endif + + ifeq ($(target_os_family),aix) + lib_link_libs += -lm -lc + exe_link_libs += -lm -lc -lcurses + endif + + ifeq ($(target_os_family),hpux) + lib_link_libs += -lm -lc -lrt + exe_link_libs += -lm -lc -lrt -lcurses + endif + + ifeq ($(target_os_family),osx) + shared_lib_suffix = -$(major_version).$(so_current).dylib + lib_link_libs += -lstdc++ -ldl + exe_link_libs += -lstdc++ -ldl -lncurses + shared_link_flags += -dynamiclib + shared_link_flags += -current_version $(major_version).$(so_current).$(so_revision) + shared_link_flags += -compatibility_version $(major_version).$(so_current).0 + shared_link_flags += -o $@ + endif + + exe_link_flags = $(link_flags) +endif + +############################################################################## +# NetWare settings +############################################################################## +ifdef netware_target + exe_suffix = .nlm + obj_suffix = .obj + lib_prefix = + static_lib_suffix = .lib + shared_lib_suffix = .nlm + + ifdef WATCOM + wc_dir = $(WATCOM) + endif + + ifdef watcom + wc_dir = $(watcom) + endif + + ifndef wc_dir + wc_dir = $(WC_DIR) + endif + + ifndef wc_dir + $(error Target operating system could not be determined) + endif + + libr = "$(call normpath,$(strip $(wc_dir)))/binnt/wlib.exe" + exe_linker = "$(call normpath,$(strip $(wc_dir)))/binnt/wlink.exe" + shared_linker = "$(call normpath,$(strip $(wc_dir)))/binnt/wlink.exe" + compiler = "$(call normpath,$(wc_dir))/binnt/wpp386.exe" + + ifneq ($(build),release) + ccdefs += FLM_DEBUG + endif + + ccflags += /ez /6s /w4 /za /zp1 /zq /zm /s /ei /of+ /we /bt=NETWARE + + ifeq ($(build),release) + ccflags += /oair + else + ccflags += /hc + endif + + libflags += /b /q /p=256 + link_flags = /m /l /v /s + + export include = $(foreach inc_dir,$(strip $(inc_dirs)),$(call hostpath,$(inc_dir));) + export INCLUDE = $(include) + export wpp386 = /d$(subst $(sp), /d,$(strip $(ccdefs))) $(ccflags) + export wcc386 = /d$(subst $(sp), /d,$(strip $(ccdefs))) $(ccflags) + + define make_lis_file_cmd + $(ec)$(gprintf) "option verbose\n" > $(4) + $(ec)$(gprintf) "option stack=32k\n" >> $(4) + $(ec)$(gprintf) "option synchronize\n" >> $(4) + $(ec)$(gprintf) "option nod\n" >> $(4) + $(ec)$(gprintf) "option map\n" >> $(4) + $(ec)$(gprintf) "option start=FlmLoad, exit=FlmUnload, caseexact\n" >> $(4) + $(ec)$(gprintf) "option nodefaultlibs\n" >> $(4) + $(ec)$(gprintf) "option screenname 'NONE'\n" >> $(4) + $(ec)$(gprintf) "option threadname '$(2)'\n" >> $(4) + $(ec)$(gprintf) "debug all debug novell\n" >> $(4) + $(ec)$(gprintf) "form novell nlm '$(2)'\n" >> $(4) + $(ec)$(gprintf) "name $(call ppath,$(1)/$(2)$(exe_suffix))\n" >> $(4) + + $(ec)$(gprintf) "file $(subst $(sp),\nfile ,$(call ppath,$(3)))\n" >> $(4) + $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)\n" >> $(4) + $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/netware/clib3s.lib)\n" >> $(4) + $(ec)$(gprintf) "library $(call ppath,$(ftk_static_lib))\n" >> $(4) + endef + + define flm_exe_link_cmd + $(call make_lis_file_cmd,$(1),$(2),$(3),$(call hostpath,$(1)/$(2).lis)) + $(ec)$(call hostpath,$(exe_linker)) @$(call hostpath,$(1)/$(2).lis) + $(ec)$(call rmcmd,$(target_path)/$(1).lis) + endef + +endif + +# -- File lists -- + +ftk_src = \ + $(patsubst src/%.cpp,%.cpp,$(wildcard src/*.cpp)) + +sample_src = \ + sample.cpp \ + $(util_common_src) + +ftk_test_src = \ + ftktest.cpp + +# -- FTK library -- + +ftk_obj = $(patsubst %.cpp,$(lib_obj_dir)/%$(obj_suffix),$(ftk_src)) +ftk_static_lib = $(static_lib_dir)/$(lib_prefix)$(project_name)$(static_lib_suffix) +ifndef netware_target + ftk_shared_lib = $(shared_lib_dir)/$(lib_prefix)$(project_name)$(shared_lib_suffix) + ftk_shared_imp_lib = $(shared_lib_dir)/$(lib_prefix)$(project_name)$(static_lib_suffix) +endif + +# -- Unit tests -- + +ftk_test_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ftk_test_src)) + +# -- Utilities -- + +sample_obj = $(patsubst %.cpp,$(sample_obj_dir)/%$(obj_suffix),$(sample_src)) +sample_exe = $(sample_dir)/sample$(exe_suffix) + +# -- Make system pattern search paths -- + +vpath %.cpp src util sample + +# -- Default target -- + +.PHONY : libs +libs: status clean dircheck $(ftk_static_lib) $(ftk_shared_lib) + +# -- *.cpp -> *$(obj_suffix) -- + +ifdef win_target +define cpp_to_obj_template +$$($(1)_obj_dir)/%$$(obj_suffix) : %.cpp + $$(ec)$$(compiler) $$(ccflags) /Fo$$(call hostpath,$$@) $$(call hostpath,$$<) +endef +endif + +ifdef unix_target +define cpp_to_obj_template +$$($(1)_obj_dir)/%$$(obj_suffix) : %.cpp + $$(ec)$$(gprintf) "$$<\n" + $$(ec)$$(compiler) $$(ccflags) -c $$< -o $$@ +endef +endif + +ifdef netware_target +define cpp_to_obj_template +$$($(1)_obj_dir)/%$(obj_suffix) : %.cpp + $$(ec)$$(gprintf) "$$(notdir $$(strip $$@))\n" + $$(ec)$$(call hostpath,$$(compiler)) $$(call hostpath,$$<) /fo=$$(call hostpath,$$@) +endef +endif + +$(foreach tmpl,lib util sample test,$(eval $(call cpp_to_obj_template,$(tmpl)))) + +# -- ftk.lib and libftk.a -- + +$(ftk_static_lib) : $(ftk_obj) + $(ec)$(gprintf) "Building $@ ...\n" +ifdef win_target + $(ec)$(libr) /NOLOGO $(call hostpath,$+) /OUT:$(call hostpath,$@) +endif +ifdef unix_target + $(ec)rm -f $@ +ifeq ($(target_os_family),osx) + $(ec)$(libr) -static -o $@ $+ +else + $(ec)$(libr) -rcs $@ $+ +endif +endif +ifdef netware_target + $(ec)dir /s/b $(call hostpath,$(lib_obj_dir)/*$(obj_suffix)) > $(call hostpath,$(static_lib_dir)/xflmlib.lis) + $(ec)$(call hostpath,$(libr)) $(libflags) $(call hostpath,$(ftk_static_lib)) @$(call hostpath,$(static_lib_dir)/xflmlib.lis) +endif + +# -- ftk.dll and libftk.so -- + +$(ftk_shared_lib) : $(ftk_obj) $(ftk_jni_obj) + $(ec)$(gprintf) "Building $@ ...\n" +ifdef win_target + $(ec)$(exe_linker) $(call hostpath,$+) $(shared_link_flags) $(lib_link_libs) +endif +ifdef unix_target + $(ec)rm -f $@ + $(ec)$(shared_linker) $+ $(shared_link_flags) $(lib_link_libs) +endif + +# -- ftk.lib import library -- + +$(ftk_shared_imp_lib) : $(ftk_shared_lib) + $(ec)$(gprintf) "Building $@ ...\n" + +# -- Utility link command -- + +ifndef flm_exe_link_cmd + define flm_exe_link_cmd + $(ec)$(exe_linker) $(exe_link_flags) $(allprereqs) $(exe_link_libs) + endef +endif + +# -- sample -- + +.PHONY : sample +sample: status clean dircheck libs $(sample_exe) +$(sample_exe): $(sample_obj) $(ftk_static_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(call flm_exe_link_cmd,$(sample_dir),sample,$(sample_obj)) + $(ec)$(call copycmd,sample/xmlfiles/*.xml,$(sample_dir)) + +# -- basictest -- + +.PHONY : basictest +basictest: status clean dircheck $(test_dir)/basictest$(exe_suffix) +$(test_dir)/ftktest$(exe_suffix): $(ftk_test_obj) $(ftk_static_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(call flm_exe_link_cmd,$(test_dir),basictest,$(ftk_test_obj)) + +# -- version -- + +define make_version_files + $(ec)$(gprintf) "$(version)" > $(1)/VERSION + $(ec)$(gprintf) " " > $(1)/SVNRevision.$(svn_revision) + $(ec)$(gprintf) "Version files created.\n" +endef + +# -- srcdist -- + +.PHONY : srcdist +srcdist: status clean dircheck docs spec + ifeq "$(svn_revision)" "0" + $(error SVN revision cannot be $(svn_revision)) + else + $(ec)$(gprintf) "Creating source package (SVN revision $(svn_revision)) ...\n" + endif + -$(ec)$(call rmdircmd,$(package_stage_dir)) + $(ec)$(call mkdircmd,$(package_stage_dir)) + $(ec)$(call make_version_files,$(package_stage_dir)) + $(ec)$(call copycmd,Makefile,$(package_stage_dir)) + $(ec)$(call copycmd,COPYING,$(package_stage_dir)) + $(ec)$(call copycmd,Doxyfile,$(package_stage_dir)) + $(ec)$(call dircopycmd,src,$(package_stage_dir)/src) + $(ec)$(call dircopycmd,util,$(package_stage_dir)/util) + $(ec)$(call dircopycmd,sample,$(package_stage_dir)/sample) + $(ec)$(call dircopycmd,$(docs_output_dir),$(package_stage_dir)/docs) + $(ec)$(call dircopycmd,$(dir $(topdir))tools,$(package_stage_dir)/tools) +ifneq ($(host_os_family),win) + -$(ec)rm -rf `find $(package_stage_dir) -name .svn` +endif +ifeq ($(host_os_family),win) + $(ec)$(call copycmd,make.exe,$(package_stage_dir)) +endif + $(ec)$(call create_archive,$(package_stage_parent_dir), \ + $(src_package_dir)/$(src_package_base_name), \ + $(package_proj_name_and_ver)) + $(ec)$(call copycmd,$(package_proj_name).changes,$(package_stage_dir)) + $(ec)$(call rmdircmd,$(package_stage_dir)) + $(ec)$(gprintf) "Source package created.\n" + +# -- bindist -- + +.PHONY : bindist +bindist: status clean dircheck all binpackage + $(ec)$(gprintf) "" + +# -- binpackage -- + +.PHONY : binpackage +binpackage: status + ifeq "$(svn_revision)" "0" + $(error SVN revision cannot be $(svn_revision)) + else + $(ec)$(gprintf) "Creating binary package (SVN revision $(svn_revision)) ...\n" + endif + -$(ec)$(call rmdircmd,$(package_stage_dir)) + $(ec)$(call mkdircmd,$(package_stage_dir)) + $(ec)$(call mkdircmd,$(package_inc_stage_dir)) + $(ec)$(call mkdircmd,$(package_shared_lib_stage_dir)) + $(ec)$(call mkdircmd,$(package_static_lib_stage_dir)) + $(ec)$(call mkdircmd,$(package_util_stage_dir)) + $(ec)$(call make_version_files,$(package_stage_dir)) + $(ec)$(call copycmd,COPYING,$(package_stage_dir)) + $(ec)$(call copycmd,src/ftk.h,$(package_inc_stage_dir)) + $(ec)$(call copycmd,$(ftk_static_lib),$(package_static_lib_stage_dir)) +ifdef ftk_shared_lib + $(ec)$(call copycmd,$(ftk_shared_lib),$(package_shared_lib_stage_dir)) +endif +ifdef win_target + $(ec)$(call copycmd,$(ftk_shared_imp_lib),$(package_shared_lib_stage_dir)) +endif + $(ec)$(call copycmd,$(checkdb_exe),$(package_util_stage_dir)) + $(ec)$(call copycmd,$(rebuild_exe),$(package_util_stage_dir)) + $(ec)$(call copycmd,$(view_exe),$(package_util_stage_dir)) + $(ec)$(call copycmd,$(xshell_exe),$(package_util_stage_dir)) + $(ec)$(call create_archive,$(package_stage_parent_dir), \ + $(bin_package_dir)/$(bin_package_base_name), \ + $(package_proj_name_and_ver)) + $(ec)$(call rmdircmd,$(package_stage_parent_dir)) + $(ec)$(gprintf) "Binary package created.\n" + +# -- dist -- + +.PHONY : dist +dist: status clean dircheck srcdist + ifeq "$(svn_revision)" "0" + $(error SVN revision cannot be $(svn_revision)) + else + $(ec)$(gprintf) "Creating distribution (SVN revision $(svn_revision)) ...\n" + endif + $(ec)$(call copycmd,$(src_package_dir)/$(src_package_name),$(package_dir)) + $(ec)$(call extract_archive,$(package_dir),$(src_package_base_name)) + $(ec)$(MAKE) -C $(package_dir)/$(package_proj_name_and_ver) clean + $(ec)$(MAKE) -C $(package_dir)/$(package_proj_name_and_ver) $(submake_targets) all + $(ec)$(MAKE) -C $(package_dir)/$(package_proj_name_and_ver) $(submake_targets) binpackage package_dir="$(package_dir)" + $(ec)$(call rmdircmd,$(package_dir)/$(package_proj_name_and_ver)) + $(ec)$(call rmcmd,$(package_dir)/$(src_package_name)) + $(ec)$(gprintf) "Distribution created.\n" + +# -- pathinfo -- + +.PHONY : pathinfo +pathinfo: + $(ec)$(gprintf) "srcpackage = $(call ppath,$(src_package_dir)/$(src_package_name))\n" + $(ec)$(gprintf) "binpackage = $(call ppath,$(bin_package_dir)/$(bin_package_name))\n" + $(ec)$(gprintf) "rpm = $(call ppath,$(package_rpms_dir)/$(HOSTTYPE)/$(rpm_name))\n" + $(ec)$(gprintf) "srcrpm = $(call ppath,$(package_srpms_dir)/$(srpm_name))\n" + $(ec)$(gprintf) "develrpm = $(call ppath,$(package_rpms_dir)/$(HOSTTYPE)/$(develrpm_name))\n" + +# -- Change log -- + +.PHONY : changelog +changelog: + $(ec)$(gprintf) "Creating change log for SVN revisions $(svn_low_rev)-$(svn_high_rev) ...\n" + $(ec)$(gprintf) "Using SVN user $(svn_user) ...\n" + $(ec)$(gprintf) "Using SVN URL $(svnurl) ...\n" + $(ec)svn log $(svnurl) -v -r $(svn_low_rev):$(svn_high_rev) > $(package_sources_dir)/$(package_proj_name_and_ver).tar.log + $(ec)$(gprintf) "Change log created.\n" + +# -- install -- + +.PHONY : install +install: libs pkgconfig +ifneq ($(host_os_family),win) + $(ec)$(gprintf) "Installing ...\n" + mkdir -p $(lib_install_dir)/pkgconfig + mkdir -p $(include_install_dir) + install --mode=644 $(ftk_shared_lib) $(lib_install_dir) + install --mode=644 $(ftk_static_lib) $(lib_install_dir) + install --mode=644 $(pkgconfig_file) $(pkgconfig_install_dir) + install --mode=644 src/ftk.h $(include_install_dir) + -ldconfig $(lib_install_dir) -l -v $(lib_install_dir)/$(lib_prefix)$(project_name)$(shared_lib_suffix) + $(ec)$(gprintf) "Installation complete.\n" +endif + +# -- uninstall -- + +.PHONY : uninstall +uninstall: +ifneq ($(host_os_family),win) + $(ec)$(gprintf) "Uninstalling ...\n" + -rm -rf $(lib_install_dir)/$(lib_prefix)$(project_name)*$(shared_lib_suffix)* + -rm -rf $(lib_install_dir)/$(lib_prefix)$(project_name)$(static_lib_suffix) + -rm -rf $(pkgconfig_install_dir)/$(pkgconfig_file_name) + -rm -rf $(include_install_dir)/ftk.h + $(ec)$(gprintf) "Uninstalled.\n" +endif + +# -- spec file -- + +.PHONY : spec +spec: dircheck + $(ec)$(gprintf) "Creating spec file ...\n" + $(ec)$(gprintf) "Name: $(package_proj_name)\n" > $(spec_file) + $(ec)$(gprintf) "$(percent)define prefix $(install_prefix)\n" >> $(spec_file) + $(ec)$(gprintf) "BuildRequires: gcc-c++ libstdc++ libstdc++-devel\n" >> $(spec_file) + $(ec)$(gprintf) "Summary: $(project_desc)\n" >> $(spec_file) + $(ec)$(gprintf) "URL: http://forge.novell.com/modules/xfmod/project/$(question)flaim\n" >> $(spec_file) + $(ec)$(gprintf) "Version: $(version)\n" >> $(spec_file) + $(ec)$(gprintf) "Release: $(package_release_num)\n" >> $(spec_file) + $(ec)$(gprintf) "License: GPL\n" >> $(spec_file) + $(ec)$(gprintf) "Vendor: Novell, Inc.\n" >> $(spec_file) + $(ec)$(gprintf) "Group: Development/Libraries/C and C++\n" >> $(spec_file) + $(ec)$(gprintf) "Source: $(package_proj_name_and_ver).tar.gz\n" >> $(spec_file) + $(ec)$(gprintf) "BuildRoot: $(percent){_tmppath}/$(percent){name}-$(percent){version}-build\n" >> $(spec_file) + $(ec)$(gprintf) "\n" >> $(spec_file) + $(ec)$(gprintf) "$(percent)description\n" >> $(spec_file) + $(ec)$(gprintf) "FTK is the FLAIM cross-platform toolkit.\n" >> $(spec_file) + $(ec)$(gprintf) "\n" >> $(spec_file) + $(ec)$(gprintf) "Authors:\n" >> $(spec_file) + $(ec)$(gprintf) "$(dash)$(dash)$(dash)$(dash)$(dash)$(dash)$(dash)$(dash)\n" >> $(spec_file) + $(ec)$(gprintf) " Daniel Sanders\n" >> $(spec_file) + $(ec)$(gprintf) " Andrew Hodgkinson\n" >> $(spec_file) + $(ec)$(gprintf) "\n" >> $(spec_file) + $(ec)$(gprintf) "$(percent)package devel\n" >> $(spec_file) + $(ec)$(gprintf) "Summary: FTK static library and header file\n" >> $(spec_file) + $(ec)$(gprintf) "Group: Development/Libraries/C and C++\n" >> $(spec_file) + $(ec)$(gprintf) "Provides: $(package_proj_name)-devel\n" >> $(spec_file) + $(ec)$(gprintf) "\n" >> $(spec_file) + $(ec)$(gprintf) "$(percent)description devel\n" >> $(spec_file) + $(ec)$(gprintf) "FTK is the FLAIM cross-platform toolkit.\n" >> $(spec_file) + $(ec)$(gprintf) "\n" >> $(spec_file) + $(ec)$(gprintf) "Authors:\n" >> $(spec_file) + $(ec)$(gprintf) "$(dash)$(dash)$(dash)$(dash)$(dash)$(dash)$(dash)$(dash)\n" >> $(spec_file) + $(ec)$(gprintf) " Daniel Sanders\n" >> $(spec_file) + $(ec)$(gprintf) " Andrew Hodgkinson\n" >> $(spec_file) + $(ec)$(gprintf) "\n" >> $(spec_file) + $(ec)$(gprintf) "$(percent)prep\n" >> $(spec_file) + $(ec)$(gprintf) "\n" >> $(spec_file) + $(ec)$(gprintf) "$(percent)setup -q\n" >> $(spec_file) + $(ec)$(gprintf) "\n" >> $(spec_file) + $(ec)$(gprintf) "$(percent)build\n" >> $(spec_file) + $(ec)$(gprintf) "$(MAKE) lib_dir_name=$(percent){_lib} $(submake_targets) libs\n" >> $(spec_file) + $(ec)$(gprintf) "\n" >> $(spec_file) + $(ec)$(gprintf) "$(percent)install\n" >> $(spec_file) + $(ec)$(gprintf) "$(MAKE) rpm_build_root=$(dollar)RPM_BUILD_ROOT install_prefix=$(percent){prefix} lib_dir_name=$(percent){_lib} $(submake_targets) install\n" >> $(spec_file) + $(ec)$(gprintf) "\n" >> $(spec_file) + $(ec)$(gprintf) "$(percent)clean\n" >> $(spec_file) + $(ec)$(gprintf) "rm -rf $(dollar)RPM_BUILD_ROOT\n" >> $(spec_file) + $(ec)$(gprintf) "\n" >> $(spec_file) + $(ec)$(gprintf) "$(percent)files\n" >> $(spec_file) + $(ec)$(gprintf) "$(percent)defattr(-,root,root)\n" >> $(spec_file) + $(ec)$(gprintf) "$(percent)doc COPYING VERSION\n" >> $(spec_file) + $(ec)$(gprintf) "$(percent){prefix}/$(percent){_lib}/$(lib_prefix)$(project_name)$(asterisk)$(shared_lib_suffix)$(asterisk)\n" >> $(spec_file) + $(ec)$(gprintf) "\n" >> $(spec_file) + $(ec)$(gprintf) "$(percent)files devel\n" >> $(spec_file) + $(ec)$(gprintf) "$(percent){prefix}/$(percent){_lib}/$(lib_prefix)$(project_name)$(static_lib_suffix)\n" >> $(spec_file) + $(ec)$(gprintf) "$(percent){prefix}/$(percent){_lib}/pkgconfig/$(pkgconfig_file_name)\n" >> $(spec_file) + $(ec)$(gprintf) "$(percent){prefix}/include/ftk.h\n" >> $(spec_file) + $(ec)$(gprintf) "Created spec file.\n" + +# -- PKG-CONFIG -- + +.PHONY : pkgconfig +pkgconfig: dircheck + $(ec)$(gprintf) "prefix=$(install_prefix)\n" > $(pkgconfig_file) + $(ec)$(gprintf) "exec_prefix=$(dollar){prefix}\n" >> $(pkgconfig_file) + $(ec)$(gprintf) "libdir=$(dollar){exec_prefix}/$(lib_dir_name)\n" >> $(pkgconfig_file) + $(ec)$(gprintf) "includedir=$(dollar){prefix}/include\n\n" >> $(pkgconfig_file) + $(ec)$(gprintf) "Name: $(package_proj_name)\n" >> $(pkgconfig_file) + $(ec)$(gprintf) "Description: $(project_desc)\n" >> $(pkgconfig_file) + $(ec)$(gprintf) "Version: $(version)\n" >> $(pkgconfig_file) + $(ec)$(gprintf) "Libs: $(lib_link_libs) -lftk -L$(dollar){libdir}\n" >> $(pkgconfig_file) + $(ec)$(gprintf) "Cflags: -I$(dollar){includedir}\n" >> $(pkgconfig_file) + +# -- SRCRPM -- + +.PHONY : srcrpm +srcrpm: srcdist spec + $(ec)$(gprintf) "Creating source RPM ...\n" + $(ec)rpmbuild --define="_topdir $(package_dir)" --quiet -bs $(spec_file) + $(ec)$(gprintf) "Source RPM created.\n" + +# -- RPMS -- + +.PHONY : rpms +rpms: dist spec + $(ec)$(gprintf) "Creating source and binary RPMs ...\n" + $(ec)rpmbuild --define="_topdir $(package_dir)" --quiet -ba $(spec_file) + $(ec)find $(package_dir) -name *.rpm | xargs chmod 775 + $(ec)$(gprintf) "Source and binary RPMs created.\n" + +# -- Documentation -- + +.PHONY : docs +docs: status clean dircheck + $(ec)$(gprintf) "Creating documentation ...\n" + $(ec)doxygen Doxyfile + $(ec)$(gprintf) "Documentation created.\n" + +# -- misc. targets -- + +.PHONY : status +status: + $(ec)$(gprintf) "===============================================================================\n" + $(ec)$(gprintf) "SVN Revision.................... $(svn_revision)\n" + $(ec)$(gprintf) "Host Operating System Family.... $(host_os_family)\n" + $(ec)$(gprintf) "Top Directory................... $(call ppath,$(topdir))\n" + $(ec)$(gprintf) "Target Operating System Family.. $(target_os_family)\n" + $(ec)$(gprintf) "Target Processor Family......... $(target_processor_family)\n" + $(ec)$(gprintf) "Target Word Size................ $(target_word_size)\n" + $(ec)$(gprintf) "Target Build Type............... $(target_build_type)\n" + $(ec)$(gprintf) "Target Path..................... $(call ppath,$(target_path))\n" + $(ec)$(gprintf) "Compiler........................ $(call ppath,$(compiler))\n" + $(ec)$(gprintf) "Librarian....................... $(call ppath,$(libr))\n" + $(ec)$(gprintf) "Defines......................... $(strip $(ccdefs))\n" + $(ec)$(gprintf) "===============================================================================\n" + +.PHONY : dircheck +dircheck: + $(ec)$(call mkdircmd,$(util_obj_dir)) + $(ec)$(call mkdircmd,$(test_obj_dir)) + $(ec)$(call mkdircmd,$(sample_obj_dir)) + $(ec)$(call mkdircmd,$(lib_obj_dir)) + $(ec)$(call mkdircmd,$(util_dir)) + $(ec)$(call mkdircmd,$(test_dir)) + $(ec)$(call mkdircmd,$(sample_dir)) + $(ec)$(call mkdircmd,$(static_lib_dir)) + $(ec)$(call mkdircmd,$(shared_lib_dir)) + $(ec)$(call mkdircmd,$(package_dir)) + $(ec)$(call mkdircmd,$(spec_dir)) + $(ec)$(call mkdircmd,$(package_sources_dir)) + $(ec)$(call mkdircmd,$(package_bin_dir)) + $(ec)$(call mkdircmd,$(package_build_dir)) + $(ec)$(call mkdircmd,$(package_rpms_dir)) + $(ec)$(call mkdircmd,$(package_srpms_dir)) + +# -- phony targets -- + +.PHONY : all +all: libs allutils + $(ec)$(gprintf) "" + +.PHONY : allutils +allutils: status dircheck libs $(util_targets) + $(ec)$(gprintf) "" + +.PHONY : test +test: status dircheck $(ftk_static_lib) $(test_targets) + $(ec)$(call runtest,basictest) + +.PHONY : debug +debug: + $(ec)$(gprintf) "" + +.PHONY : release +release: + $(ec)$(gprintf) "" + +.PHONY : usegcc +usegcc: + $(ec)$(gprintf) "" + +.PHONY : 32bit +32bit: + $(ec)$(gprintf) "" + +.PHONY : 64bit +64bit: + $(ec)$(gprintf) "" + +.PHONY : win +win: + $(ec)$(gprintf) "" + +.PHONY : linux +linux: + $(ec)$(gprintf) "" + +.PHONY : solaris +solaris: + $(ec)$(gprintf) "" + +.PHONY : osx +osx: + $(ec)$(gprintf) "" + +.PHONY : nlm +nlm: + $(ec)$(gprintf) "" + +.PHONY : verbose +verbose: + $(ec)$(gprintf) "" + +.PHONY : check +check: + $(ec)$(gprintf) "" + +.PHONY : TAGS +TAGS: + $(ec)$(gprintf) "" + +.PHONY : info +info: + $(ec)$(gprintf) "" + +.PHONY : ignore-local-mods +ignore-local-mods: + $(ec)$(gprintf) "" + +.PHONY : ilm +ilm: + $(ec)$(gprintf) "" + +.PHONY : installcheck +installcheck: + $(ec)$(gprintf) "" + +.PHONY : clean +clean: +ifeq ($(do_clean),1) + $(ec)$(gprintf) "\n" + $(ec)$(gprintf) "Cleaning $(target_path) ...\n" + -$(ec)$(call rmdircmd,$(target_path)) + -$(ec)$(call rmcmd,*.pch) + $(ec)$(gprintf) "\n" +endif + +.PHONY : distclean + -$(ec)$(call rmcmd,*.pch) + +.PHONY : mostlyclean +mostlyclean : clean + $(ec)$(gprintf) "" + +.PHONY : maintainer-clean +maintainer-clean: + -$(ec)$(call rmdircmd,$(build_output_dir)) + -$(ec)$(call rmcmd,*.pch) diff --git a/ftk/NEWS b/ftk/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/ftk/README b/ftk/README new file mode 100644 index 0000000..e69de29 diff --git a/ftk/make.exe b/ftk/make.exe new file mode 100644 index 0000000..54d888d Binary files /dev/null and b/ftk/make.exe differ diff --git a/ftk/src/ftk.h b/ftk/src/ftk.h new file mode 100644 index 0000000..547aa68 --- /dev/null +++ b/ftk/src/ftk.h @@ -0,0 +1,3476 @@ +//------------------------------------------------------------------------------ +// Desc: XFLAIM public definitions and interfaces +// +// Tabs: 3 +// +// Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: xflaim.h 3123 2006-01-24 17:19:50 -0700 (Tue, 24 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------------ + +#ifndef FTK_H +#define FTK_H + + #ifndef FLM_PLATFORM_CONFIGURED + #define FLM_PLATFORM_CONFIGURED + + // Determine the build platform + + #undef FLM_WIN + #undef FLM_NLM + #undef FLM_UNIX + #undef FLM_AIX + #undef FLM_LINUX + #undef FLM_SOLARIS + #undef FLM_SPARC + #undef FLM_HPUX + #undef FLM_OSX + #undef FLM_BIG_ENDIAN + #undef FLM_PPC + #undef FLM_STRICT_ALIGNMENT + #undef FLM_S390 + #undef FLM_IA64 + #undef FLM_GNUC + + #if defined( __GNUC__) + #define FLM_GNUC + #endif + + #if defined( __NETWARE__) || defined( NLM) || defined( N_PLAT_NLM) + #define FLM_NLM + #define FLM_OSTYPE_STR "NetWare" + #if defined( __WATCOMC__) + #define FLM_WATCOM_NLM + #elif defined( __MWERKS__) + #define FLM_MWERKS_NLM + #endif + #elif defined( _WIN64) + #define FLM_WIN + #define FLM_OSTYPE_STR "Windows" + #ifndef FLM_64BIT + #define FLM_64BIT + #endif + #define FLM_STRICT_ALIGNMENT + #elif defined( _WIN32) + #define FLM_WIN + #define FLM_OSTYPE_STR "Windows" + #elif defined( _AIX) + #define FLM_AIX + #define FLM_OSTYPE_STR "AIX" + #define FLM_UNIX + #define FLM_BIG_ENDIAN + #define FLM_STRICT_ALIGNMENT + #elif defined( linux) + #define FLM_LINUX + #define FLM_OSTYPE_STR "Linux" + #define FLM_UNIX + #if defined( __PPC__) || defined( __ppc__) + #define FLM_PPC + #define FLM_BIG_ENDIAN + #define FLM_STRICT_ALIGNMENT + #elif defined( __s390__) + #define FLM_S390 + #define FLM_BIG_ENDIAN + #define FLM_STRICT_ALIGNMENT + #elif defined( __s390x__) + #define FLM_S390 + #ifndef FLM_64BIT + #define FLM_64BIT + #endif + #define FLM_BIG_ENDIAN + #define FLM_STRICT_ALIGNMENT + #elif defined( __ia64__) + #define FLM_IA64 + #ifndef FLM_64BIT + #define FLM_64BIT + #endif + #define FLM_STRICT_ALIGNMENT + #endif + #elif defined( sun) + #define FLM_SOLARIS + #define FLM_OSTYPE_STR "Solaris" + #define FLM_UNIX + #define FLM_STRICT_ALIGNMENT + #if defined( sparc) || defined( __sparc) || defined( __sparc__) + #define FLM_SPARC + #define FLM_BIG_ENDIAN + #endif + #elif defined( __hpux) || defined( hpux) + #define FLM_HPUX + #define FLM_OSTYPE_STR "HPUX" + #define FLM_UNIX + #define FLM_BIG_ENDIAN + #define FLM_STRICT_ALIGNMENT + #elif defined( __APPLE__) + #define FLM_OSX + #define FLM_OSTYPE_STR "OSX" + #define FLM_UNIX + #if (defined( __ppc__) || defined( __ppc64__)) + #define FLM_PPC + #define FLM_BIG_ENDIAN + #define FLM_STRICT_ALIGNMENT + #endif + #else + #error Platform architecture is undefined. + #endif + + #if !defined( FLM_64BIT) && !defined( FLM_32BIT) + #if defined( FLM_UNIX) + #if defined( __x86_64__) || defined( _LP64) || \ + defined( __LP64__) || defined( __sparcv9) + #define FLM_64BIT + #endif + #endif + #endif + + #if !defined( FLM_64BIT) + #define FLM_32BIT + #elif defined( FLM_32BIT) + #error Cannot define both FLM_32BIT and FLM_64BIT + #endif + + // Debug or release build? + + #ifndef FLM_DEBUG + #if defined( DEBUG) || (defined( PRECHECKIN) && PRECHECKIN != 0) + #define FLM_DEBUG + #endif + #endif + + // Alignment + + #if defined( FLM_UNIX) || defined( FLM_64BIT) + #define FLM_ALLOC_ALIGN 0x0007 + #define FLM_ALIGN_SIZE 8 + #elif defined( FLM_WIN) || defined( FLM_NLM) + #define FLM_ALLOC_ALIGN 0x0003 + #define FLM_ALIGN_SIZE 4 + #else + #error Platform not supported + #endif + + // Basic type definitions + + #if defined( FLM_UNIX) + typedef unsigned long FLMUINT; + typedef long FLMINT; + typedef unsigned char FLMBYTE; + typedef unsigned short FLMUNICODE; + + typedef unsigned long long FLMUINT64; + typedef unsigned int FLMUINT32; + typedef unsigned short FLMUINT16; + typedef unsigned char FLMUINT8; + typedef long long FLMINT64; + typedef int FLMINT32; + typedef short FLMINT16; + typedef signed char FLMINT8; + typedef char * f_va_list; + + #if defined( FLM_64BIT) || defined( FLM_OSX) || \ + defined( FLM_S390) || defined( FLM_HPUX) || defined( FLM_AIX) + typedef unsigned long FLMSIZET; + #else + typedef unsigned FLMSIZET; + #endif + #else + + #if defined( FLM_WIN) + + #if defined( FLM_64BIT) + typedef unsigned __int64 FLMUINT; + typedef __int64 FLMINT; + typedef unsigned __int64 FLMSIZET; + typedef unsigned int FLMUINT32; + #elif _MSC_VER >= 1300 + typedef unsigned long __w64 FLMUINT; + typedef long __w64 FLMINT; + typedef unsigned int FLMUINT32; + typedef __w64 unsigned int FLMSIZET; + #else + typedef unsigned long FLMUINT; + typedef long FLMINT; + typedef unsigned int FLMUINT32; + typedef __w64 unsigned int FLMSIZET; + #endif + + typedef char * f_va_list; + + #elif defined( FLM_NLM) + + typedef unsigned long int FLMUINT; + typedef long int FLMINT; + typedef unsigned long int FLMUINT32; + typedef unsigned FLMSIZET; + typedef unsigned long f_va_list; + #else + #error Platform not supported + #endif + + typedef unsigned char FLMBYTE; + typedef unsigned short int FLMUNICODE; + + typedef unsigned short int FLMUINT16; + typedef unsigned char FLMUINT8; + typedef signed int FLMINT32; + typedef signed short int FLMINT16; + typedef signed char FLMINT8; + + #if defined( __MWERKS__) + typedef unsigned long long FLMUINT64; + typedef long long FLMINT64; + #else + typedef unsigned __int64 FLMUINT64; + typedef __int64 FLMINT64; + #endif + + #endif + + #if defined( FLM_WIN) || defined( FLM_NLM) + #define FLMATOMIC volatile long + #else + #define FLMATOMIC volatile int + #endif + + typedef FLMINT RCODE; + typedef FLMINT FLMBOOL; + + #define F_FILENAME_SIZE 256 + #define F_PATH_MAX_SIZE 256 + + #define FLM_MAX_UINT ((FLMUINT)(-1L)) + #define FLM_MAX_INT ((FLMINT)(((FLMUINT)(-1L)) >> 1)) + #define FLM_MIN_INT ((FLMINT)((((FLMUINT)(-1L)) >> 1) + 1)) + #define FLM_MAX_UINT32 ((FLMUINT32)(0xFFFFFFFFL)) + #define FLM_MAX_INT32 ((FLMINT32)(0x7FFFFFFFL)) + #define FLM_MIN_INT32 ((FLMINT32)(0x80000000L)) + #define FLM_MAX_UINT16 ((FLMUINT16)(0xFFFF)) + #define FLM_MAX_INT16 ((FLMINT16)(0x7FFF)) + #define FLM_MIN_INT16 ((FLMINT16)(0x8000)) + #define FLM_MAX_UINT8 ((FLMUINT8)0xFF) + + #if( _MSC_VER >= 1200) && (_MSC_VER < 1300) + #define FLM_MAX_UINT64 ((FLMUINT64)(0xFFFFFFFFFFFFFFFFL)) + #define FLM_MAX_INT64 ((FLMINT64)(0x7FFFFFFFFFFFFFFFL)) + #define FLM_MIN_INT64 ((FLMINT64)(0x8000000000000000L)) + #else + #define FLM_MAX_UINT64 ((FLMUINT64)(0xFFFFFFFFFFFFFFFFLL)) + #define FLM_MAX_INT64 ((FLMINT64)(0x7FFFFFFFFFFFFFFFLL)) + #define FLM_MIN_INT64 ((FLMINT64)(0x8000000000000000LL)) + #endif + + #endif + + // xpcselany keeps MS compilers from complaining about multiple definitions + + #if defined(_MSC_VER) + #define xpcselany __declspec(selectany) + #else + #define xpcselany + #endif + + typedef struct + { + FLMUINT32 l; + FLMUINT16 w1; + FLMUINT16 w2; + FLMUINT8 b[ 8]; + } FLM_GUID; + + #define RFLMIID const FLM_GUID & + #define RFLMCLSID const FLM_GUID & + #define FLMGUID FLM_GUID + #define FLMCLSID FLM_GUID + + // FLM_DEFINE_GUID may be used to define or declare a GUID + // #define FLM_INIT_GUID before including this header file when + // you want to define the guid, all other inclusions will only declare + // the guid, not define it. + + #if !defined( PCOM_INIT_GUID) + #define FLM_DEFINE_GUID( name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const FLMGUID name + #else + #define FLM_DEFINE_GUID( name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const xpcselany FLMGUID name \ + = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + #endif + + #define FLMEXTC extern "C" + + #if defined( FLM_WIN) + #define FLMAPI __stdcall + #define FLMEXP __declspec(dllexport) + #ifdef FLM_DEBUG + #define FINLINE inline + #else + #define FINLINE __forceinline + #endif + #elif defined( FLM_NLM) + #define FLMAPI __stdcall + #define FLMEXP FLMEXTC + #define FINLINE inline + #elif defined( FLM_UNIX) + #define FLMAPI + #define FLMEXP FLMEXTC + #define FINLINE inline + #else + #error Platform not supported + #endif + + // flmnovtbl keeps MS compilers from generating vtables for interfaces + + #ifdef _MSC_VER + #define flmnovtbl __declspec( novtable) + #else + #define flmnovtbl + #endif + + #define flminterface struct flmnovtbl + + FLM_DEFINE_GUID( Internal_IID_FLMIUnknown, 0x00000000, 0x0000, 0x0000, + 0xC0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46); + + flminterface FLMIUnknown + { + virtual ~FLMIUnknown() + { + } + + virtual RCODE FLMAPI QueryInterface( + RFLMIID riid, + void ** ppv) = 0; + + virtual FLMINT FLMAPI AddRef( void) = 0; + + virtual FLMINT FLMAPI Release( void) = 0; + }; + + // FLMIClassFactory + // uuid: 00000001-0000-0000-C000-000000000046 (same as MSCOM IClassFactory) + + FLM_DEFINE_GUID( Internal_IID_FLMIClassFactory, 0x00000001, 0x0000, 0x0000, + 0xC0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46); + + flminterface FLMIClassFactory : public FLMIUnknown + { + virtual RCODE FLMAPI CreateInstance( + FLMIUnknown * piuouter, + RFLMIID riid, + void ** ppv) = 0; + + virtual RCODE FLMAPI LockServer( + bool lockf) = 0; + }; + + /**************************************************************************** + Forward References + ****************************************************************************/ + flminterface IF_DataVector; + flminterface IF_DirHdl; + flminterface IF_FileHdl; + flminterface IF_FileSystem; + flminterface IF_IStream; + flminterface IF_PosIStream; + flminterface IF_ResultSet; + flminterface IF_ThreadInfo; + flminterface IF_Pool; + flminterface IF_DynaBuf; + flminterface IF_OStream; + flminterface IF_IOStream; + flminterface IF_LogMessageClient; + flminterface IF_Thread; + flminterface IF_IOBuffer; + flminterface IF_DOMNode; + + /**************************************************************************** + CROSS PLATFORM DEFINITIONS + ****************************************************************************/ + + #ifndef NULL + #define NULL 0 + #endif + + #ifndef TRUE + #define TRUE 1 + #endif + + #ifndef FALSE + #define FALSE 0 + #endif + + // Language definitions - to get rid of testing "US" or multiple bytes + // will define needed languages as a number with backward conversions. + // Keep these defines synchronized with the table in wps6cmpc.c + + #define FLM_US_LANG 0 // English, United States + #define FLM_AF_LANG 1 // Afrikaans + #define FLM_AR_LANG 2 // Arabic + #define FLM_CA_LANG 3 // Catalan + #define FLM_HR_LANG 4 // Croatian + #define FLM_CZ_LANG 5 // Czech + #define FLM_DK_LANG 6 // Danish + #define FLM_NL_LANG 7 // Dutch + #define FLM_OZ_LANG 8 // English, Australia + #define FLM_CE_LANG 9 // English, Canada + #define FLM_UK_LANG 10 // English, United Kingdom + #define FLM_FA_LANG 11 // Farsi + #define FLM_SU_LANG 12 // Finnish + #define FLM_CF_LANG 13 // French, Canada + #define FLM_FR_LANG 14 // French, France + #define FLM_GA_LANG 15 // Galician + #define FLM_DE_LANG 16 // German, Germany + #define FLM_SD_LANG 17 // German, Switzerland + #define FLM_GR_LANG 18 // Greek + #define FLM_HE_LANG 19 // Hebrew + #define FLM_HU_LANG 20 // Hungarian + #define FLM_IS_LANG 21 // Icelandic + #define FLM_IT_LANG 22 // Italian + #define FLM_NO_LANG 23 // Norwegian + #define FLM_PL_LANG 24 // Polish + #define FLM_BR_LANG 25 // Portuguese, Brazil + #define FLM_PO_LANG 26 // Portuguese, Portugal + #define FLM_RU_LANG 27 // Russian + #define FLM_SL_LANG 28 // Slovak + #define FLM_ES_LANG 29 // Spanish + #define FLM_SV_LANG 30 // Swedish + #define FLM_YK_LANG 31 // Ukrainian + #define FLM_UR_LANG 32 // Urdu + #define FLM_TK_LANG 33 // Turkey + #define FLM_JP_LANG 34 // Japanese + #define FLM_KO_LANG 35 // Korean + #define FLM_CT_LANG 36 // Chinese-Traditional + #define FLM_CS_LANG 37 // Chinese-Simplified + #define FLM_LA_LANG 38 // another Asian language + + /**************************************************************************** + Desc: I/O Flags + ****************************************************************************/ + #define FLM_IO_CURRENT_POS FLM_MAX_UINT64 + + #define FLM_IO_RDONLY 0x0001 + #define FLM_IO_RDWR 0x0002 + #define FLM_IO_EXCL 0x0004 + #define FLM_IO_CREATE_DIR 0x0008 + #define FLM_IO_SH_DENYRW 0x0010 + #define FLM_IO_SH_DENYWR 0x0020 + #define FLM_IO_SH_DENYNONE 0x0040 + #define FLM_IO_DIRECT 0x0080 + + // File Positioning Definitions + + #define FLM_IO_SEEK_SET 0 // Beginning of File + #define FLM_IO_SEEK_CUR 1 // Current File Pointer Position + #define FLM_IO_SEEK_END 2 // End of File + + // Maximum file size + + #define FLM_MAXIMUM_FILE_SIZE 0xFFFC0000 + + // Retrieval flags + + #define FLM_INCL 0x0010 + #define FLM_EXCL 0x0020 + #define FLM_EXACT 0x0040 + #define FLM_KEY_EXACT 0x0080 + #define FLM_FIRST 0x0100 + #define FLM_LAST 0x0200 + + /**************************************************************************** + Desc: Comparison flags for strings + ****************************************************************************/ + #define FLM_COMP_CASE_INSENSITIVE 0x0001 + #define FLM_COMP_COMPRESS_WHITESPACE 0x0002 + #define FLM_COMP_NO_WHITESPACE 0x0004 + #define FLM_COMP_NO_UNDERSCORES 0x0008 + #define FLM_COMP_NO_DASHES 0x0010 + #define FLM_COMP_WHITESPACE_AS_SPACE 0x0020 + #define FLM_COMP_IGNORE_LEADING_SPACE 0x0040 + #define FLM_COMP_IGNORE_TRAILING_SPACE 0x0080 + + /**************************************************************************** + Desc: Colors + ****************************************************************************/ + typedef enum + { + FLM_CURRENT_COLOR, + FLM_BLACK, + FLM_BLUE, + FLM_GREEN, + FLM_CYAN, + FLM_RED, + FLM_PURPLE, + FLM_BROWN, + FLM_LIGHTGRAY, + FLM_DARKGRAY, + FLM_LIGHTBLUE, + FLM_LIGHTGREEN, + FLM_LIGHTCYAN, + FLM_LIGHTRED, + FLM_LIGHTPURPLE, + FLM_YELLOW, + FLM_WHITE, + FLM_NUM_COLORS + } eColorType; + + #define F_BLACK "%0C" + #define F_BLUE "%1C" + #define F_GREEN "%2C" + #define F_CYAN "%3C" + #define F_RED "%4C" + #define F_PURPLE "%5C" + #define F_BROWN "%6C" + #define F_LIGHTGRAY "%7C" + #define F_DARKGRAY "%8C" + #define F_LIGHTBLUE "%9C" + #define F_LIGHTGREEN "%10C" + #define F_LIGHTCYAN "%11C" + #define F_LIGHTRED "%12C" + #define F_LIGHTPURPLE "%13C" + #define F_YELLOW "%14C" + #define F_WHITE "%15C" + + #define F_PUSHFORECOLOR "%+0C" + #define F_PUSHBACKCOLOR "%+1C" + #define F_POPFORECOLOR "%-0C" + #define F_POPBACKCOLOR "%-1C" + + #define F_PUSHCOLOR F_PUSHFORECOLOR F_PUSHBACKCOLOR + #define F_POPCOLOR F_POPFORECOLOR F_POPBACKCOLOR + + #define F_BLUE_ON_WHITE "%1.15C" + + /**************************************************************************** + Desc: Data types + ****************************************************************************/ + typedef enum + { + FLM_NODATA_TYPE = 0, + FLM_TEXT_TYPE, + FLM_NUMBER_TYPE, + FLM_BINARY_TYPE + } eFlmDataType; + + /**************************************************************************** + Desc: Slab stats + ****************************************************************************/ + typedef struct + { + FLMUINT64 ui64Slabs; + FLMUINT64 ui64SlabBytes; + FLMUINT64 ui64AllocatedCells; + FLMUINT64 ui64FreeCells; + } FLM_SLAB_USAGE; + + /**************************************************************************** + Desc: Thread info + ****************************************************************************/ + typedef struct + { + FLMUINT uiThreadId; + FLMUINT uiThreadGroup; + FLMUINT uiAppId; + FLMUINT uiStartTime; + const char * pszThreadName; + const char * pszThreadStatus; + } F_THREAD_INFO; + + typedef enum + { + FLM_THREAD_STATUS_UNKNOWN = 0, + FLM_THREAD_STATUS_INITIALIZING, + FLM_THREAD_STATUS_RUNNING, + FLM_THREAD_STATUS_SLEEPING, + FLM_THREAD_STATUS_TERMINATING, + FLM_THREAD_STATUS_STARTING_TRANS, + FLM_THREAD_STATUS_COMMITTING_TRANS, + FLM_THREAD_STATUS_ABORTING_TRANS + } eThreadStatus; + + #define F_THREAD_MIN_STACK_SIZE (16 * 1024) + #define F_THREAD_DEFAULT_STACK_SIZE (16 * 1024) + + typedef RCODE (* F_THREAD_FUNC)(IF_Thread *); + + /**************************************************************************** + Desc: DOM + ****************************************************************************/ + typedef enum + { + INVALID_NODE = 0x00, + DOCUMENT_NODE = 0x01, + ELEMENT_NODE = 0x02, + DATA_NODE = 0x03, + COMMENT_NODE = 0x04, + CDATA_SECTION_NODE = 0x05, + ANNOTATION_NODE = 0x06, + PROCESSING_INSTRUCTION_NODE = 0x07, + ATTRIBUTE_NODE = 0x08, + ANY_NODE_TYPE = 0xFFFF + } eDomNodeType; + + typedef enum + { + FLM_ROOT = 0, + FLM_FIRST_CHILD, + FLM_LAST_CHILD, + FLM_PREV_SIB, + FLM_NEXT_SIB, + FLM_ATTRIBUTE + } eNodeInsertLoc; + + /**************************************************************************** + Desc: XML + ****************************************************************************/ + typedef enum + { + FLM_XML_UTF8_ENCODING, + FLM_XML_USASCII_ENCODING + } XMLEncoding; + + typedef enum + { + XML_NO_ERROR = 0, + XML_ERR_BAD_ELEMENT_NAME, // 1 Invalid element name - does not start with a valid character for element names + XML_ERR_XMLNS_IN_ELEMENT_NAME, // 2 Element names cannot be "xmlns" or have "xmlns:" as a prefix + XML_ERR_ELEMENT_NAME_MISMATCH, // 3 The element name inside the "' + XML_ERR_EXPECTING_ELEMENT_LT, // 6 Expecting a '<' to begin an element name + XML_ERR_EXPECTING_EQ, // 7 Expecting a '=' after the attribute name + XML_ERR_MULTIPLE_XMLNS_DECLS, // 8 Multiple "xmlns" default namespace declarations in an element + XML_ERR_MULTIPLE_PREFIX_DECLS, // 9 Multiple definitions for the same prefix ("xmlns:prefix=...") in an element + XML_ERR_EXPECTING_QUEST_GT, // 10 Expecting "?>" to terminate "= 'a' && (native) <= 'z') \ + ? (native) - 'a' + 'A' \ + : (native)) + + #define f_tolower( native) \ + (((native) >= 'A' && (native) <= 'Z') \ + ? (native) - 'A' + 'a' \ + : (native)) + + #define f_islower( native) \ + ((native) >= 'a' && (native) <= 'z') + + #ifndef FLM_ASCII_PLATFORM + #define FLM_ASCII_PLATFORM + #endif + + /**************************************************************************** + Desc: Unicode character constants and macros + ****************************************************************************/ + + #define FLM_UNICODE_LINEFEED ((FLMUNICODE)10) + #define FLM_UNICODE_SPACE ((FLMUNICODE)32) + #define FLM_UNICODE_BANG ((FLMUNICODE)33) + #define FLM_UNICODE_QUOTE ((FLMUNICODE)34) + #define FLM_UNICODE_POUND ((FLMUNICODE)35) + #define FLM_UNICODE_DOLLAR ((FLMUNICODE)36) + #define FLM_UNICODE_PERCENT ((FLMUNICODE)37) + #define FLM_UNICODE_AMP ((FLMUNICODE)38) + #define FLM_UNICODE_APOS ((FLMUNICODE)39) + #define FLM_UNICODE_LPAREN ((FLMUNICODE)40) + #define FLM_UNICODE_RPAREN ((FLMUNICODE)41) + #define FLM_UNICODE_ASTERISK ((FLMUNICODE)42) + #define FLM_UNICODE_PLUS ((FLMUNICODE)43) + #define FLM_UNICODE_COMMA ((FLMUNICODE)44) + #define FLM_UNICODE_HYPHEN ((FLMUNICODE)45) + #define FLM_UNICODE_PERIOD ((FLMUNICODE)46) + #define FLM_UNICODE_FSLASH ((FLMUNICODE)47) + + #define FLM_UNICODE_0 ((FLMUNICODE)48) + #define FLM_UNICODE_1 ((FLMUNICODE)49) + #define FLM_UNICODE_2 ((FLMUNICODE)50) + #define FLM_UNICODE_3 ((FLMUNICODE)51) + #define FLM_UNICODE_4 ((FLMUNICODE)52) + #define FLM_UNICODE_5 ((FLMUNICODE)53) + #define FLM_UNICODE_6 ((FLMUNICODE)54) + #define FLM_UNICODE_7 ((FLMUNICODE)55) + #define FLM_UNICODE_8 ((FLMUNICODE)56) + #define FLM_UNICODE_9 ((FLMUNICODE)57) + + #define FLM_UNICODE_COLON ((FLMUNICODE)58) + #define FLM_UNICODE_SEMI ((FLMUNICODE)59) + #define FLM_UNICODE_LT ((FLMUNICODE)60) + #define FLM_UNICODE_EQ ((FLMUNICODE)61) + #define FLM_UNICODE_GT ((FLMUNICODE)62) + #define FLM_UNICODE_QUEST ((FLMUNICODE)63) + #define FLM_UNICODE_ATSIGN ((FLMUNICODE)64) + + #define FLM_UNICODE_A ((FLMUNICODE)65) + #define FLM_UNICODE_B ((FLMUNICODE)66) + #define FLM_UNICODE_C ((FLMUNICODE)67) + #define FLM_UNICODE_D ((FLMUNICODE)68) + #define FLM_UNICODE_E ((FLMUNICODE)69) + #define FLM_UNICODE_F ((FLMUNICODE)70) + #define FLM_UNICODE_G ((FLMUNICODE)71) + #define FLM_UNICODE_H ((FLMUNICODE)72) + #define FLM_UNICODE_I ((FLMUNICODE)73) + #define FLM_UNICODE_J ((FLMUNICODE)74) + #define FLM_UNICODE_K ((FLMUNICODE)75) + #define FLM_UNICODE_L ((FLMUNICODE)76) + #define FLM_UNICODE_M ((FLMUNICODE)77) + #define FLM_UNICODE_N ((FLMUNICODE)78) + #define FLM_UNICODE_O ((FLMUNICODE)79) + #define FLM_UNICODE_P ((FLMUNICODE)80) + #define FLM_UNICODE_Q ((FLMUNICODE)81) + #define FLM_UNICODE_R ((FLMUNICODE)82) + #define FLM_UNICODE_S ((FLMUNICODE)83) + #define FLM_UNICODE_T ((FLMUNICODE)84) + #define FLM_UNICODE_U ((FLMUNICODE)85) + #define FLM_UNICODE_V ((FLMUNICODE)86) + #define FLM_UNICODE_W ((FLMUNICODE)87) + #define FLM_UNICODE_X ((FLMUNICODE)88) + #define FLM_UNICODE_Y ((FLMUNICODE)89) + #define FLM_UNICODE_Z ((FLMUNICODE)90) + + #define FLM_UNICODE_LBRACKET ((FLMUNICODE)91) + #define FLM_UNICODE_BACKSLASH ((FLMUNICODE)92) + #define FLM_UNICODE_RBRACKET ((FLMUNICODE)93) + #define FLM_UNICODE_UNDERSCORE ((FLMUNICODE)95) + + #define FLM_UNICODE_a ((FLMUNICODE)97) + #define FLM_UNICODE_b ((FLMUNICODE)98) + #define FLM_UNICODE_c ((FLMUNICODE)99) + #define FLM_UNICODE_d ((FLMUNICODE)100) + #define FLM_UNICODE_e ((FLMUNICODE)101) + #define FLM_UNICODE_f ((FLMUNICODE)102) + #define FLM_UNICODE_g ((FLMUNICODE)103) + #define FLM_UNICODE_h ((FLMUNICODE)104) + #define FLM_UNICODE_i ((FLMUNICODE)105) + #define FLM_UNICODE_j ((FLMUNICODE)106) + #define FLM_UNICODE_k ((FLMUNICODE)107) + #define FLM_UNICODE_l ((FLMUNICODE)108) + #define FLM_UNICODE_m ((FLMUNICODE)109) + #define FLM_UNICODE_n ((FLMUNICODE)110) + #define FLM_UNICODE_o ((FLMUNICODE)111) + #define FLM_UNICODE_p ((FLMUNICODE)112) + #define FLM_UNICODE_q ((FLMUNICODE)113) + #define FLM_UNICODE_r ((FLMUNICODE)114) + #define FLM_UNICODE_s ((FLMUNICODE)115) + #define FLM_UNICODE_t ((FLMUNICODE)116) + #define FLM_UNICODE_u ((FLMUNICODE)117) + #define FLM_UNICODE_v ((FLMUNICODE)118) + #define FLM_UNICODE_w ((FLMUNICODE)119) + #define FLM_UNICODE_x ((FLMUNICODE)120) + #define FLM_UNICODE_y ((FLMUNICODE)121) + #define FLM_UNICODE_z ((FLMUNICODE)122) + + #define FLM_UNICODE_LBRACE ((FLMUNICODE)123) + #define FLM_UNICODE_PIPE ((FLMUNICODE)124) + #define FLM_UNICODE_RBRACE ((FLMUNICODE)125) + #define FLM_UNICODE_TILDE ((FLMUNICODE)126) + #define FLM_UNICODE_C_CEDILLA ((FLMUNICODE)199) + #define FLM_UNICODE_N_TILDE ((FLMUNICODE)209) + #define FLM_UNICODE_c_CEDILLA ((FLMUNICODE)231) + #define FLM_UNICODE_n_TILDE ((FLMUNICODE)241) + + FINLINE FLMBOOL f_isvowel( + FLMUNICODE uChar) + { + uChar = f_unitolower( uChar); + + if( uChar == FLM_UNICODE_a || + uChar == FLM_UNICODE_e || + uChar == FLM_UNICODE_i || + uChar == FLM_UNICODE_o || + uChar == FLM_UNICODE_u || + uChar == FLM_UNICODE_y) + { + return( TRUE); + } + + return( FALSE); + } + + /**************************************************************************** + Desc: Endian macros + ****************************************************************************/ + + FINLINE FLMUINT32 f_byteToLong( + const FLMBYTE * pucBuf) + { + FLMUINT32 ui32Val = 0; + + ui32Val |= ((FLMUINT32)pucBuf[ 0]) << 24; + ui32Val |= ((FLMUINT32)pucBuf[ 1]) << 16; + ui32Val |= ((FLMUINT32)pucBuf[ 2]) << 8; + ui32Val |= ((FLMUINT32)pucBuf[ 3]); + + return( ui32Val); + } + + FINLINE FLMUINT64 f_byteToLong64( + const FLMBYTE * pucBuf) + { + FLMUINT64 ui64Val = 0; + + ui64Val |= ((FLMUINT64)pucBuf[ 0]) << 56; + ui64Val |= ((FLMUINT64)pucBuf[ 1]) << 48; + ui64Val |= ((FLMUINT64)pucBuf[ 2]) << 40; + ui64Val |= ((FLMUINT64)pucBuf[ 3]) << 32; + ui64Val |= ((FLMUINT64)pucBuf[ 4]) << 24; + ui64Val |= ((FLMUINT64)pucBuf[ 5]) << 16; + ui64Val |= ((FLMUINT64)pucBuf[ 6]) << 8; + ui64Val |= ((FLMUINT64)pucBuf[ 7]); + + return( ui64Val); + } + + FLMUINT32 f_byteToInt( const FLMBYTE * ptr); + #define f_byteToInt(p) ( \ + (FLMUINT16) ( ((((FLMBYTE *)(p))[ 0]) << 8) | (((FLMBYTE *)(p))[ 1]) ) ) + + void f_longToByte( FLMINT32 uiNum, FLMBYTE * ptr); + #define f_longToByte( n, p) { \ + FLMUINT32 ui32Temp = (FLMUINT32) (n); FLMBYTE * pTemp = (FLMBYTE *)(p); \ + pTemp[0] = (FLMBYTE) (ui32Temp >> 24); \ + pTemp[1] = (FLMBYTE) (ui32Temp >> 16); \ + pTemp[2] = (FLMBYTE) (ui32Temp >> 8); \ + pTemp[3] = (FLMBYTE) (ui32Temp ); \ + } + + void f_long64ToByte( FLMINT64 uiNum, FLMBYTE * ptr); + #define f_long64ToByte( n, p) { \ + FLMUINT64 ui64Temp = (FLMUINT64) (n); FLMBYTE * pTemp = (FLMBYTE *)(p); \ + pTemp[0] = (FLMBYTE) (ui64Temp >> 56); \ + pTemp[1] = (FLMBYTE) (ui64Temp >> 48); \ + pTemp[2] = (FLMBYTE) (ui64Temp >> 40); \ + pTemp[3] = (FLMBYTE) (ui64Temp >> 32); \ + pTemp[4] = (FLMBYTE) (ui64Temp >> 24); \ + pTemp[5] = (FLMBYTE) (ui64Temp >> 16); \ + pTemp[6] = (FLMBYTE) (ui64Temp >> 8); \ + pTemp[7] = (FLMBYTE) (ui64Temp ); \ + } + + void f_intToByte( FLMINT16 uiNum, FLMBYTE * ptr); + #define f_intToByte( n, p) { \ + FLMUINT16 ui16Temp = (FLMUINT16) (n); FLMBYTE * pTemp = (FLMBYTE *) (p); \ + pTemp[0] = (FLMBYTE) (ui16Temp >> 8); \ + pTemp[1] = (FLMBYTE) (ui16Temp ); \ + } + + #ifndef FLM_BIG_ENDIAN + + #if defined( FLM_SPARC) + #error Wrong endian order selected + #endif + + #define LO(wrd) (*(FLMUINT8 *)&wrd) + #define HI(wrd) (*((FLMUINT8 *)&wrd + 1)) + + #if defined( FLM_STRICT_ALIGNMENT) + + #define FB2UW( bp) \ + ((FLMUINT16)((((FLMUINT16)(((FLMUINT8 *)(bp))[1])) << 8) | \ + (((FLMUINT16)(((FLMUINT8 *)(bp))[0]))))) + + #define FB2UD( bp) \ + ((FLMUINT32)((((FLMUINT32)(((FLMUINT8 *)(bp))[3]))<< 24) | \ + (((FLMUINT32)(((FLMUINT8 *)(bp))[2]))<< 16) | \ + (((FLMUINT32)(((FLMUINT8 *)(bp))[1]))<< 8) | \ + (((FLMUINT32)(((FLMUINT8 *)(bp))[0]))))) + + #define FB2U64( bp) \ + ((FLMUINT64)((((FLMUINT64)(((FLMUINT8 *)(bp))[7])) << 56) | \ + (((FLMUINT64)(((FLMUINT8 *)(bp))[6])) << 48) | \ + (((FLMUINT64)(((FLMUINT8 *)(bp))[5])) << 40) | \ + (((FLMUINT64)(((FLMUINT8 *)(bp))[4])) << 32) | \ + (((FLMUINT64)(((FLMUINT8 *)(bp))[3])) << 24) | \ + (((FLMUINT64)(((FLMUINT8 *)(bp))[2])) << 16) | \ + (((FLMUINT64)(((FLMUINT8 *)(bp))[1]))<< 8) | \ + (((FLMUINT64)(((FLMUINT8 *)(bp))[0]))))) + + #define UW2FBA( uw, bp) \ + (((FLMUINT8 *)(bp))[0] = ((FLMUINT8)(uw)), \ + ((FLMUINT8 *)(bp))[1] = ((FLMUINT8)((((uw) & 0xff00) >> 8)))) + + #define UD2FBA( udw, bp) \ + (((FLMUINT8 *)(bp))[0] = ((FLMUINT8)((udw) & 0xff)), \ + ((FLMUINT8 *)(bp))[1] = ((FLMUINT8)(((udw) & 0xff00) >> 8)), \ + ((FLMUINT8 *)(bp))[2] = ((FLMUINT8)(((udw) & 0xff0000) >> 16)), \ + ((FLMUINT8 *)(bp))[3] = ((FLMUINT8)(((udw) & 0xff000000) >> 24))) + + #define U642FBA( u64, bp) \ + (((FLMUINT8 *)(bp))[0] = ((FLMUINT8)((u64) & 0xff)), \ + ((FLMUINT8 *)(bp))[1] = ((FLMUINT8)(((u64) & 0xff00) >> 8)), \ + ((FLMUINT8 *)(bp))[2] = ((FLMUINT8)(((u64) & 0xff0000) >> 16)), \ + ((FLMUINT8 *)(bp))[3] = ((FLMUINT8)(((u64) & 0xff000000) >> 24)), \ + ((FLMUINT8 *)(bp))[4] = ((FLMUINT8)(((u64) & 0xff00000000) >> 32)), \ + ((FLMUINT8 *)(bp))[5] = ((FLMUINT8)(((u64) & 0xff0000000000) >> 40)), \ + ((FLMUINT8 *)(bp))[6] = ((FLMUINT8)(((u64) & 0xff000000000000) >> 48)), \ + ((FLMUINT8 *)(bp))[7] = ((FLMUINT8)(((u64) & 0xff00000000000000) >> 56))) + #else + #define FB2UW( fbp) \ + (*((FLMUINT16 *)(fbp))) + + #define FB2UD( fbp) \ + (*((FLMUINT32 *)(fbp))) + + #define FB2U64( fbp) \ + (*((FLMUINT64 *)(fbp))) + + #define UW2FBA( uw, fbp) \ + (*((FLMUINT16 *)(fbp)) = ((FLMUINT16) (uw))) + + #define UD2FBA( uw, fbp) \ + (*((FLMUINT32 *)(fbp)) = ((FLMUINT32) (uw))) + + #define U642FBA( uw, fbp) \ + (*((FLMUINT64 *)(fbp)) = ((FLMUINT64) (uw))) + #endif + + #else + + #if defined( __i386__) + #error Wrong endian order selected + #endif + + #define LO( wrd) \ + (*((FLMUINT8 *)&wrd + 1)) + + #define HI( wrd) \ + (*(FLMUINT8 *)&wrd) + + #define FB2UW( bp) \ + ((FLMUINT16)((((FLMUINT16)(((FLMUINT8 *)(bp))[1])) << 8) | \ + (((FLMUINT16)(((FLMUINT8 *)(bp))[0]))))) + + #define FB2UD( bp) \ + ((FLMUINT32)((((FLMUINT32)(((FLMUINT8 *)(bp))[3])) << 24) | \ + (((FLMUINT32)(((FLMUINT8 *)(bp))[2])) << 16) | \ + (((FLMUINT32)(((FLMUINT8 *)(bp))[1])) << 8) | \ + (((FLMUINT32)(((FLMUINT8 *)(bp))[0]))))) + + #define FB2U64( bp) \ + ((FLMUINT64)((((FLMUINT64)(((FLMUINT8 *)(bp))[7])) << 56) | \ + (((FLMUINT64)(((FLMUINT8 *)(bp))[6])) << 48) | \ + (((FLMUINT64)(((FLMUINT8 *)(bp))[5])) << 40) | \ + (((FLMUINT64)(((FLMUINT8 *)(bp))[4])) << 32) | \ + (((FLMUINT64)(((FLMUINT8 *)(bp))[3])) << 24) | \ + (((FLMUINT64)(((FLMUINT8 *)(bp))[2])) << 16) | \ + (((FLMUINT64)(((FLMUINT8 *)(bp))[1])) << 8) | \ + (((FLMUINT64)(((FLMUINT8 *)(bp))[0]))))) + + #define UW2FBA( uw, bp) \ + (((FLMUINT8 *)(bp))[0] = ((FLMUINT8)(uw)), \ + ((FLMUINT8 *)(bp))[1] = ((FLMUINT8)((((uw) & 0xff00) >> 8)))) + + #define UD2FBA( udw, bp) \ + (((FLMUINT8 *)(bp))[0] = ((FLMUINT8)((udw) & 0xff)), \ + ((FLMUINT8 *)(bp))[1] = ((FLMUINT8)(((udw) & 0xff00) >> 8)), \ + ((FLMUINT8 *)(bp))[2] = ((FLMUINT8)(((udw) & 0xff0000) >> 16)), \ + ((FLMUINT8 *)(bp))[3] = ((FLMUINT8)(((udw) & 0xff000000) >> 24))) + + #ifdef FLM_UNIX + #define U642FBA( u64, bp) \ + (((FLMUINT8 *)(bp))[0] = ((FLMUINT8)((u64) & 0xffULL)), \ + ((FLMUINT8 *)(bp))[1] = ((FLMUINT8)(((u64) & 0xff00ULL) >> 8)), \ + ((FLMUINT8 *)(bp))[2] = ((FLMUINT8)(((u64) & 0xff0000ULL) >> 16)), \ + ((FLMUINT8 *)(bp))[3] = ((FLMUINT8)(((u64) & 0xff000000ULL) >> 24)), \ + ((FLMUINT8 *)(bp))[4] = ((FLMUINT8)(((u64) & 0xff00000000ULL) >> 32)), \ + ((FLMUINT8 *)(bp))[5] = ((FLMUINT8)(((u64) & 0xff0000000000ULL) >> 40)), \ + ((FLMUINT8 *)(bp))[6] = ((FLMUINT8)(((u64) & 0xff000000000000ULL) >> 48)), \ + ((FLMUINT8 *)(bp))[7] = ((FLMUINT8)(((u64) & 0xff00000000000000ULL) >> 56))) + #else + #define U642FBA( u64, bp) \ + (((FLMUINT8 *)(bp))[0] = ((FLMUINT8)((u64) & 0xff)), \ + ((FLMUINT8 *)(bp))[1] = ((FLMUINT8)(((u64) & 0xff00) >> 8)), \ + ((FLMUINT8 *)(bp))[2] = ((FLMUINT8)(((u64) & 0xff0000) >> 16)), \ + ((FLMUINT8 *)(bp))[3] = ((FLMUINT8)(((u64) & 0xff000000) >> 24)), \ + ((FLMUINT8 *)(bp))[4] = ((FLMUINT8)(((u64) & 0xff00000000) >> 32)), \ + ((FLMUINT8 *)(bp))[5] = ((FLMUINT8)(((u64) & 0xff0000000000) >> 40)), \ + ((FLMUINT8 *)(bp))[6] = ((FLMUINT8)(((u64) & 0xff000000000000) >> 48)), \ + ((FLMUINT8 *)(bp))[7] = ((FLMUINT8)(((u64) & 0xff00000000000000) >> 56))) + #endif + #endif + + /**************************************************************************** + Desc: File path functions and macros + ****************************************************************************/ + + // This defines the maximum file size we can support for ANY + // platform, ANY file type. It is not 4Gb because of a bug in direct IO + // on Netware. The limitation is that in direct IO mode (on the legacy file + // system) we are not allowed room for the last block. If the block + // size were 64K for example, direct IO only lets us expand to a size of + // 0xFFFF0000. Since we can't anticipate what the block size will be, + // we have to set a maximum that accounts for the maximum block size we + // may ever see. At this point, we are assuming it won't ever be more + // than 256K on legacy file systems. Thus, our limit of 0xFFFC0000. + // (See define of F_MAXIMUM_FILE_SIZE in xflaim.h) + + #if defined( FLM_WIN) || defined( FLM_NLM) + #define FWSLASH '/' + #define SLASH '\\' + #define SSLASH "\\" + #define COLON ':' + #define PERIOD '.' + #define PARENT_DIR ".." + #define CURRENT_DIR "." + #else + #ifndef FWSLASH + #define FWSLASH '/' + #endif + + #ifndef SLASH + #define SLASH '/' + #endif + + #ifndef SSLASH + #define SSLASH "/" + #endif + + #ifndef COLON + #define COLON ':' + #endif + + #ifndef PERIOD + #define PERIOD '.' + #endif + + #ifndef PARENT_DIR + #define PARENT_DIR ".." + #endif + + #ifndef CURRENT_DIR + #define CURRENT_DIR "." + #endif + #endif + + /**************************************************************************** + Desc: CPU release and sleep functions + ****************************************************************************/ + +// #ifdef FLM_NLM +// #define f_yieldCPU() \ +// pthread_yield() +// #else +// #define f_yieldCPU() +// #endif + + void FLMAPI f_yieldCPU( void); + + void FLMAPI f_sleep( + FLMUINT uiMilliseconds); + +// #ifdef FLM_WIN +// #define f_sleep( uiMilliseconds) \ +// Sleep( (DWORD)uiMilliseconds) +// #endif + + /**************************************************************************** + Desc: Time, date, timestamp functions + ****************************************************************************/ + + typedef struct + { + FLMUINT16 year; + FLMBYTE month; + FLMBYTE day; + FLMBYTE hour; + FLMBYTE minute; + FLMBYTE second; + FLMBYTE hundredth; + } F_TMSTAMP; + + #define f_timeIsLeapYear(year) \ + ((((year) & 0x03) == 0) && (((year) % 100) != 0) || (((year) % 400) == 0)) + + void f_timeGetSeconds( + FLMUINT * puiSeconds); + + void f_timeGetTimeStamp( + F_TMSTAMP * pTimeStamp); + + FLMINT f_timeGetLocalOffset( void); + + void f_timeSecondsToDate( + FLMUINT uiSeconds, + F_TMSTAMP * pTimeStamp); + + void f_timeDateToSeconds( + F_TMSTAMP * pTimeStamp, + FLMUINT * puiSeconds); + + FLMINT f_timeCompareTimeStamps( + F_TMSTAMP * pTimeStamp1, + F_TMSTAMP * pTimeStamp2, + FLMUINT flag); + + FINLINE FLMUINT f_localTimeToUTC( + FLMUINT uiSeconds) + { + return( uiSeconds + f_timeGetLocalOffset()); + } + + /**************************************************************************** + Desc: Quick sort + ****************************************************************************/ + + typedef FLMINT (FLMAPI * F_SORT_COMPARE_FUNC)( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2); + + typedef void (FLMAPI * F_SORT_SWAP_FUNC)( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2); + + FLMINT FLMAPI f_qsortUINTCompare( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2); + + void FLMAPI f_qsortUINTSwap( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2); + + void FLMAPI f_qsort( + void * pvBuffer, + FLMUINT uiLowerBounds, + FLMUINT uiUpperBounds, + F_SORT_COMPARE_FUNC fnCompare, + F_SORT_SWAP_FUNC fnSwap); + + /**************************************************************************** + Desc: Environment + ****************************************************************************/ + + void FLMAPI f_getenv( + const char * pszKey, + FLMBYTE * pszBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiValueLen = NULL); + + /**************************************************************************** + Desc: f_sprintf + ****************************************************************************/ + + FLMINT FLMAPI f_vsprintf( + char * pszDestStr, + const char * pszFormat, + f_va_list * args); + + FLMINT FLMAPI f_sprintf( + char * pszDestStr, + const char * pszFormat, + ...); + + /**************************************************************************** + Desc: + ****************************************************************************/ + + void FLMAPI f_memcpy( + void * pvDest, + const void * pvSrc, + FLMUINT uiLength); + + void FLMAPI f_memmove( + void * pvDest, + const void * pvSrc, + FLMUINT uiLength); + + void FLMAPI f_memset( + void * pvDest, + unsigned char ucByte, + FLMUINT uiLength); + + FLMINT FLMAPI f_memcmp( + const void * pvStr1, + const void * pvStr2, + FLMUINT uiLength); + + FLMINT FLMAPI f_strcat( + char * pszDest, + const char * pszSrc); + + FLMINT FLMAPI f_strncat( + char * pszDest, + const char * pszSrc, + FLMUINT uiLength); + + const char * FLMAPI f_strchr( + const char * pszStr, + unsigned char ucByte); + + const char * FLMAPI f_strrchr( + const char * pszStr, + unsigned char ucByte); + + const char * FLMAPI f_strstr( + const char * pszStr, + const char * pszSearch); + + char * FLMAPI f_strupr( + char * pszStr); + + FLMINT FLMAPI f_strcmp( + const char * pszStr1, + const char * pszStr2); + + FLMINT FLMAPI f_strncmp( + const char * pszStr1, + const char * pszStr2, + FLMUINT uiLength); + + FLMINT FLMAPI f_stricmp( + const char * pszStr1, + const char * pszStr2); + + FLMINT FLMAPI f_strnicmp( + const char * pszStr1, + const char * pszStr2, + FLMUINT uiLength); + + FLMINT FLMAPI f_strcpy( + char * pszDest, + const char * pszSrc); + + FLMINT FLMAPI f_strncpy( + char * pszDest, + const char * pszSrc, + FLMUINT uiLength); + + FLMINT FLMAPI f_strlen( + const char * pszStr); + + RCODE FLMAPI f_getCharFromUTF8Buf( + const FLMBYTE ** ppucBuf, + const FLMBYTE * pucEnd, + FLMUNICODE * puChar); + + RCODE FLMAPI f_uni2UTF8( + FLMUNICODE uChar, + FLMBYTE * pucBuf, + FLMUINT * puiBufSize); + + RCODE FLMAPI f_getUTF8Length( + const FLMBYTE * pucBuf, + FLMUINT uiBufLen, + FLMUINT * puiBytes, + FLMUINT * puiChars); + + RCODE FLMAPI f_getUTF8CharFromUTF8Buf( + FLMBYTE ** ppucBuf, + FLMBYTE * pucEnd, + FLMBYTE * pucDestBuf, + FLMUINT * puiLen); + + RCODE FLMAPI f_unicode2UTF8( + FLMUNICODE * puzStr, + FLMUINT uiStrLen, + FLMBYTE * pucBuf, + FLMUINT * puiBufLength); + + /**************************************************************************** + Desc: Memory + ****************************************************************************/ + + RCODE FLMAPI f_alloc( + FLMUINT uiSize, + void ** ppvPtr, + const char * pszFile = __FILE__, + int iLine = __LINE__); + + #define f_alloc(s,p) \ + f_alloc((s),(void **)p) + + RCODE FLMAPI f_calloc( + FLMUINT uiSize, + void ** ppvPtr, + const char * pszFile = __FILE__, + int iLine = __LINE__); + + #define f_calloc(s,p) \ + f_calloc((s),(void **)p) + + RCODE FLMAPI f_realloc( + FLMUINT uiSize, + void ** ppvPtr, + const char * pszFile = __FILE__, + int iLine = __LINE__); + + #define f_realloc(s,p) \ + f_realloc((s),(void **)p) + + RCODE FLMAPI f_recalloc( + FLMUINT uiSize, + void ** ppvPtr, + const char * pszFile = __FILE__, + int iLine = __LINE__); + + #define f_recalloc(s,p) \ + f_recalloc((s),(void **)p) + + #define f_new \ + new( __FILE__, __LINE__) + + RCODE FLMAPI f_free( + void ** ppvPtr); + + #define f_free(p) \ + f_free( (void **)p) + + /**************************************************************************** + Desc: Logging + ****************************************************************************/ + + IF_LogMessageClient * FLMAPI f_beginLogMessage( + FLMUINT uiMsgType); + + void FLMAPI f_logPrintf( + IF_LogMessageClient * pLogMessage, + const char * pszFormatStr, ...); + + void FLMAPI f_logVPrintf( + IF_LogMessageClient * pLogMessage, + const char * szFormatStr, + f_va_list * args); + + void FLMAPI f_endLogMessage( + IF_LogMessageClient ** ppLogMessage); + + /**************************************************************************** + Desc: XML + ****************************************************************************/ + flminterface IF_XML : public F_RefCount + { + public: + + virtual RCODE FLMAPI setup( void) = 0; + + virtual FLMBOOL FLMAPI isPubidChar( + FLMUNICODE uChar) = 0; + + virtual FLMBOOL FLMAPI isQuoteChar( + FLMUNICODE uChar) = 0; + + virtual FLMBOOL FLMAPI isWhitespace( + FLMUNICODE uChar) = 0; + + virtual FLMBOOL FLMAPI isExtender( + FLMUNICODE uChar) = 0; + + virtual FLMBOOL FLMAPI isCombiningChar( + FLMUNICODE uChar) = 0; + + virtual FLMBOOL FLMAPI isNameChar( + FLMUNICODE uChar) = 0; + + virtual FLMBOOL FLMAPI isNCNameChar( + FLMUNICODE uChar) = 0; + + virtual FLMBOOL FLMAPI isIdeographic( + FLMUNICODE uChar) = 0; + + virtual FLMBOOL FLMAPI isBaseChar( + FLMUNICODE uChar) = 0; + + virtual FLMBOOL FLMAPI isDigit( + FLMUNICODE uChar) = 0; + + virtual FLMBOOL FLMAPI isLetter( + FLMUNICODE uChar) = 0; + + virtual FLMBOOL FLMAPI isNameValid( + FLMUNICODE * puzName, + FLMBYTE * pszName) = 0; + }; + + /**************************************************************************** + Desc: DOM + ****************************************************************************/ + flminterface IF_DOMNode : public F_RefCount + { + virtual RCODE FLMAPI createNode( + eDomNodeType eNodeType, + FLMUINT uiNameId, + eNodeInsertLoc eLocation, + IF_DOMNode ** ppNewNode) = 0; + + virtual RCODE FLMAPI createChildElement( + FLMUINT uiChildElementNameId, + eNodeInsertLoc eLocation, + IF_DOMNode ** ppNewChildElementNode) = 0; + + virtual RCODE FLMAPI deleteNode( void); + + virtual RCODE FLMAPI deleteChildren( + FLMUINT uiNameId = 0) = 0; + + virtual RCODE FLMAPI createAttribute( + FLMUINT uiAttrNameId, + IF_DOMNode ** ppAttrNode) = 0; + + virtual RCODE FLMAPI getFirstAttribute( + IF_DOMNode ** ppAttrNode) = 0; + + virtual RCODE FLMAPI getLastAttribute( + IF_DOMNode ** ppAttrNode) = 0; + + virtual RCODE FLMAPI getAttribute( + FLMUINT uiAttrNameId, + IF_DOMNode ** ppAttrNode) = 0; + + virtual RCODE FLMAPI deleteAttribute( + FLMUINT uiAttrNameId) = 0; + + virtual RCODE FLMAPI hasAttribute( + FLMUINT uiAttrNameId, + IF_DOMNode ** ppAttrNode = NULL) = 0; + + virtual RCODE FLMAPI hasAttributes( + FLMBOOL * pbHasAttrs) = 0; + + virtual RCODE FLMAPI hasNextSibling( + FLMBOOL * pbHasNextSibling) = 0; + + virtual RCODE FLMAPI hasPreviousSibling( + FLMBOOL * pbHasPreviousSibling) = 0; + + virtual RCODE FLMAPI hasChildren( + FLMBOOL * pbHasChildren) = 0; + + virtual RCODE FLMAPI isNamespaceDecl( + FLMBOOL * pbIsNamespaceDecl) = 0; + + virtual eDomNodeType FLMAPI getNodeType( void) = 0; + + virtual RCODE FLMAPI getNodeId( + FLMUINT64 * pui64NodeId) = 0; + + virtual RCODE FLMAPI getParentId( + FLMUINT64 * pui64ParentId) = 0; + + virtual RCODE FLMAPI getDocumentId( + FLMUINT64 * pui64DocumentId) = 0; + + virtual RCODE FLMAPI getPrevSibId( + FLMUINT64 * pui64PrevSibId) = 0; + + virtual RCODE FLMAPI getNextSibId( + FLMUINT64 * pui64NextSibId) = 0; + + virtual RCODE FLMAPI getFirstChildId( + FLMUINT64 * pui64FirstChildId) = 0; + + virtual RCODE FLMAPI getLastChildId( + FLMUINT64 * pui64LastChildId) = 0; + + virtual RCODE FLMAPI getNameId( + FLMUINT * puiNameId) = 0; + + virtual RCODE FLMAPI getEncDefId( + FLMUINT * puiEncDefId) = 0; + + virtual RCODE FLMAPI getDataType( + eFlmDataType * puiDataType) = 0; + + virtual RCODE FLMAPI getDataLength( + FLMUINT * puiLength) = 0; + + virtual RCODE FLMAPI getUINT32( + FLMUINT32 * pui32Value) = 0; + + virtual RCODE FLMAPI getUINT( + FLMUINT * puiValue) = 0; + + virtual RCODE FLMAPI getUINT64( + FLMUINT64 * pui64Value) = 0; + + virtual RCODE FLMAPI getINT32( + FLMINT32 * pi32Value) = 0; + + virtual RCODE FLMAPI getINT( + FLMINT * piValue) = 0; + + virtual RCODE FLMAPI getINT64( + FLMINT64 * pi64Value) = 0; + + virtual RCODE FLMAPI getMetaValue( + FLMUINT64 * pui64Value) = 0; + + virtual RCODE FLMAPI getUnicodeChars( + FLMUINT * puiNumChars) = 0; + + virtual RCODE FLMAPI getUnicode( + FLMUNICODE * puzValueBuffer, + FLMUINT uiBufferSize, + FLMUINT uiCharOffset, + FLMUINT uiMaxCharsRequested, + FLMUINT * puiCharsReturned = NULL, + FLMUINT * puiBufferBytesUsed = NULL) = 0; + + virtual RCODE FLMAPI getUnicode( + FLMUNICODE ** ppuzUnicodeValue) = 0; + + virtual RCODE FLMAPI getUnicode( + IF_DynaBuf * pDynaBuf) = 0; + + virtual RCODE FLMAPI getUTF8( + FLMBYTE * pucValueBuffer, + FLMUINT uiBufferSize, + FLMUINT uiCharOffset, + FLMUINT uiMaxCharsRequested, + FLMUINT * puiCharsReturned = NULL, + FLMUINT * puiBufferBytesUsed = NULL) = 0; + + virtual RCODE FLMAPI getUTF8( + FLMBYTE ** ppszUTF8Value) = 0; + + virtual RCODE FLMAPI getUTF8( + IF_DynaBuf * pDynaBuf) = 0; + + virtual RCODE FLMAPI getBinary( + void * pvValue, + FLMUINT uiByteOffset, + FLMUINT uiBytesRequested, + FLMUINT * puiBytesReturned) = 0; + + virtual RCODE FLMAPI getBinary( + IF_DynaBuf * pBuffer) = 0; + + virtual RCODE FLMAPI getAttributeValueUINT32( + FLMUINT uiAttrNameId, + FLMUINT32 * pui32Num) = 0; + + virtual RCODE FLMAPI getAttributeValueUINT32( + FLMUINT uiAttrNameId, + FLMUINT32 * pui32Num, + FLMUINT32 ui32NotFoundDefault) = 0; + + virtual RCODE FLMAPI getAttributeValueUINT( + FLMUINT uiAttrNameId, + FLMUINT * puiNum) = 0; + + virtual RCODE FLMAPI getAttributeValueUINT( + FLMUINT uiAttrNameId, + FLMUINT * puiNum, + FLMUINT uiNotFoundDefault) = 0; + + virtual RCODE FLMAPI getAttributeValueUINT64( + FLMUINT uiAttrNameId, + FLMUINT64 * pui64Num) = 0; + + virtual RCODE FLMAPI getAttributeValueUINT64( + FLMUINT uiAttrNameId, + FLMUINT64 * pui64Num, + FLMUINT64 ui64NotFoundDefault) = 0; + + virtual RCODE FLMAPI getAttributeValueINT( + FLMUINT uiAttrNameId, + FLMINT * piNum) = 0; + + virtual RCODE FLMAPI getAttributeValueINT( + FLMUINT uiAttrNameId, + FLMINT * piNum, + FLMINT iNotFoundDefault) = 0; + + virtual RCODE FLMAPI getAttributeValueINT64( + FLMUINT uiAttrNameId, + FLMINT64 * pi64Num) = 0; + + virtual RCODE FLMAPI getAttributeValueINT64( + FLMUINT uiAttrNameId, + FLMINT64 * pi64Num, + FLMINT64 i64NotFoundDefault) = 0; + + virtual RCODE FLMAPI getAttributeValueUnicode( + FLMUINT uiAttrNameId, + FLMUNICODE * puzValueBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned = NULL, + FLMUINT * puiBufferBytesUsed = NULL) = 0; + + virtual RCODE FLMAPI getAttributeValueUnicode( + FLMUINT uiAttrNameId, + FLMUNICODE ** ppuzValueBuffer) = 0; + + virtual RCODE FLMAPI getAttributeValueUnicode( + FLMUINT uiAttrNameId, + IF_DynaBuf * pDynaBuf) = 0; + + virtual RCODE FLMAPI getAttributeValueUTF8( + FLMUINT uiAttrNameId, + FLMBYTE * pucValueBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned = NULL, + FLMUINT * puiBufferBytesUsed = NULL) = 0; + + virtual RCODE FLMAPI getAttributeValueUTF8( + FLMUINT uiAttrNameId, + FLMBYTE ** ppszValueBuffer) = 0; + + virtual RCODE FLMAPI getAttributeValueUTF8( + FLMUINT uiAttrNameId, + IF_DynaBuf * pDynaBuf) = 0; + + virtual RCODE FLMAPI getAttributeValueBinary( + FLMUINT uiAttrNameId, + void * pvValueBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiValueLength) = 0; + + virtual RCODE FLMAPI getAttributeValueBinary( + FLMUINT uiAttrNameId, + IF_DynaBuf * pDynaBuf) = 0; + + virtual RCODE FLMAPI setUINT( + FLMUINT uiValue, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE FLMAPI setUINT64( + FLMUINT64 ui64Value, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE FLMAPI setINT( + FLMINT iValue, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE FLMAPI setINT64( + FLMINT64 i64Value, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE FLMAPI setMetaValue( + FLMUINT64 ui64Value) = 0; + + virtual RCODE FLMAPI setUnicode( + const FLMUNICODE * puzValue, + FLMUINT uiValueLength = 0, + FLMBOOL bLast = TRUE, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE FLMAPI setUTF8( + const FLMBYTE * pszValue, + FLMUINT uiValueLength = 0, + FLMBOOL bLast = TRUE, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE FLMAPI setBinary( + const void * pvValue, + FLMUINT uiValueLength, + FLMBOOL bLast = TRUE, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE FLMAPI setAttributeValueUINT( + FLMUINT uiAttrNameId, + FLMUINT uiValue, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE FLMAPI setAttributeValueUINT64( + FLMUINT uiAttrNameId, + FLMUINT64 ui64Value, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE FLMAPI setAttributeValueINT( + FLMUINT uiAttrNameId, + FLMINT iValue, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE FLMAPI setAttributeValueINT64( + FLMUINT uiAttrNameId, + FLMINT64 i64Value, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE FLMAPI setAttributeValueUnicode( + FLMUINT uiAttrNameId, + const FLMUNICODE * puzValue, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE FLMAPI setAttributeValueUTF8( + FLMUINT uiAttrNameId, + const FLMBYTE * pucValue, + FLMUINT uiLength = 0, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE FLMAPI setAttributeValueBinary( + FLMUINT uiAttrNameId, + const void * pvValue, + FLMUINT uiLength, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE FLMAPI getDocumentNode( + IF_DOMNode ** ppDocument) = 0; + + virtual RCODE FLMAPI getNextDocument( + IF_DOMNode ** ppNextDocument) = 0; + + virtual RCODE FLMAPI getPreviousDocument( + IF_DOMNode ** ppPrevDocument) = 0; + + virtual RCODE FLMAPI getParentNode( + IF_DOMNode ** ppParent) = 0; + + virtual RCODE FLMAPI getFirstChild( + IF_DOMNode ** ppFirstChild) = 0; + + virtual RCODE FLMAPI getLastChild( + IF_DOMNode ** ppLastChild) = 0; + + virtual RCODE FLMAPI getNextSibling( + IF_DOMNode ** ppNextSibling) = 0; + + virtual RCODE FLMAPI getPreviousSibling( + IF_DOMNode ** ppPrevSibling) = 0; + + virtual RCODE FLMAPI getChild( + eDomNodeType eNodeType, + IF_DOMNode ** ppChild) = 0; + + virtual RCODE FLMAPI getChildElement( + FLMUINT uiElementNameId, + IF_DOMNode ** ppChild, + FLMUINT uiFlags = 0) = 0; + + virtual RCODE FLMAPI getSiblingElement( + FLMUINT uiElementNameId, + FLMBOOL bNext, + IF_DOMNode ** ppSibling) = 0; + + virtual RCODE FLMAPI getAncestorElement( + FLMUINT uiElementNameId, + IF_DOMNode ** ppAncestor) = 0; + + virtual RCODE FLMAPI getDescendantElement( + FLMUINT uiElementNameId, + IF_DOMNode ** ppDescendant) = 0; + + virtual RCODE FLMAPI insertBefore( + IF_DOMNode * pNewChild, + IF_DOMNode * pRefChild) = 0; + + virtual RCODE FLMAPI getPrefix( + FLMUNICODE * puzPrefixBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned = NULL) = 0; + + virtual RCODE FLMAPI getPrefix( + char * pszPrefixBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned = NULL) = 0; + + virtual RCODE FLMAPI getPrefixId( + FLMUINT * puiPrefixId) = 0; + + virtual RCODE FLMAPI setPrefix( + const FLMUNICODE * puzPrefix) = 0; + + virtual RCODE FLMAPI setPrefix( + const char * pszPrefix) = 0; + + virtual RCODE FLMAPI setPrefixId( + FLMUINT uiPrefixId) = 0; + + virtual RCODE FLMAPI getNamespaceURI( + FLMUNICODE * puzNamespaceURIBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned = NULL) = 0; + + virtual RCODE FLMAPI getNamespaceURI( + char * pszNamespaceURIBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned = NULL) = 0; + + virtual RCODE FLMAPI getLocalName( + FLMUNICODE * puzLocalNameBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned = NULL) = 0; + + virtual RCODE FLMAPI getLocalName( + char * pszLocalNameBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned = NULL) = 0; + + virtual RCODE FLMAPI getQualifiedName( + FLMUNICODE * puzQualifiedNameBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned = NULL) = 0; + + virtual RCODE FLMAPI getQualifiedName( + char * pszQualifiedNameBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned = NULL) = 0; + + virtual RCODE FLMAPI getCollection( + FLMUINT * puiCollection) = 0; + + virtual RCODE FLMAPI createAnnotation( + IF_DOMNode ** ppAnnotation, + FLMUINT64 * pui64NodeId = NULL) = 0; + + virtual RCODE FLMAPI getAnnotation( + IF_DOMNode ** ppAnnotation) = 0; + + virtual RCODE FLMAPI getAnnotationId( + FLMUINT64 * pui64AnnotationId) = 0; + + virtual RCODE FLMAPI hasAnnotation( + FLMBOOL * pbHasAnnotation) = 0; + + virtual RCODE FLMAPI getIStream( + IF_PosIStream ** ppIStream, + FLMUINT * puiDataType = NULL, + FLMUINT * puiDataLength = NULL) = 0; + + virtual RCODE FLMAPI getTextIStream( + IF_PosIStream ** ppIStream, + FLMUINT * puiNumChars = NULL) = 0; + + virtual FLMUINT FLMAPI compareNode( + IF_DOMNode * pNode, + char * pszErrBuff, + FLMUINT uiErrBuffLen) = 0; + }; + + /**************************************************************************** + Desc: XML parser + ****************************************************************************/ + flminterface IF_XMLParser : public F_RefCount + { + virtual RCODE FLMAPI setup( void) = 0; + + virtual void FLMAPI reset( void) = 0; + + virtual RCODE FLMAPI import( + IF_IStream * pStream, + FLMUINT uiFlags, + IF_DOMNode * pNodeToLinkTo, + eNodeInsertLoc eInsertLoc, + IF_DOMNode ** ppNewNode, + FLM_IMPORT_STATS * pImportStats) = 0; + + virtual void FLMAPI setStatusCallback( + XML_STATUS_HOOK fnStatus, + void * pvUserData) = 0; + }; + + /**************************************************************************** + Desc: Name table + ****************************************************************************/ + flminterface IF_NameTable : public F_RefCount + { + virtual RCODE FLMAPI setupNameTable( void) = 0; + + virtual void FLMAPI clearTable( + FLMUINT uiPoolBlkSize) = 0; + + virtual RCODE FLMAPI getNextTagTypeAndNumOrder( + FLMUINT uiType, + FLMUINT * puiNextPos, + FLMUNICODE * puzTagName = NULL, + char * pszTagName = NULL, + FLMUINT uiNameBufSize = 0, + FLMUINT * puiTagNum = NULL, + FLMUINT * puiDataType = NULL, + FLMUNICODE * puzNamespace = NULL, + FLMUINT uiNamespaceBufSize = 0, + FLMBOOL bTruncatedNamesOk = TRUE) = 0; + + virtual RCODE FLMAPI getNextTagTypeAndNameOrder( + FLMUINT uiType, + FLMUINT * puiNextPos, + FLMUNICODE * puzTagName = NULL, + char * pszTagName = NULL, + FLMUINT uiNameBufSize = 0, + FLMUINT * puiTagNum = NULL, + FLMUINT * puiDataType = NULL, + FLMUNICODE * puzNamespace = NULL, + FLMUINT uiNamespaceBufSize = 0, + FLMBOOL bTruncatedNamesOk = TRUE) = 0; + + virtual RCODE FLMAPI getFromTagTypeAndName( + FLMUINT uiType, + const FLMUNICODE * puzTagName, + const char * pszTagName, + FLMBOOL bMatchNamespace, + const FLMUNICODE * puzNamespace = NULL, + FLMUINT * puiTagNum = NULL, + FLMUINT * puiDataType = NULL) = 0; + + virtual RCODE FLMAPI getFromTagTypeAndNum( + FLMUINT uiType, + FLMUINT uiTagNum, + FLMUNICODE * puzTagName = NULL, + char * pszTagName = NULL, + FLMUINT * puiNameBufSize = NULL, + FLMUINT * puiDataType = NULL, + FLMUNICODE * puzNamespace = NULL, + char * pszNamespace = NULL, + FLMUINT * puiNamespaceBufSize = NULL, + FLMBOOL bTruncatedNamesOk = TRUE) = 0; + + virtual RCODE FLMAPI addTag( + FLMUINT uiType, + FLMUNICODE * puzTagName, + const char * pszTagName, + FLMUINT uiTagNum, + FLMUINT uiDataType = 0, + FLMUNICODE * puzNamespace = NULL, + FLMBOOL bCheckDuplicates = TRUE) = 0; + + virtual void FLMAPI removeTag( + FLMUINT uiType, + FLMUINT uiTagNum) = 0; + + virtual RCODE FLMAPI cloneNameTable( + IF_NameTable * pSrcNameTable) = 0; + + virtual RCODE FLMAPI importFromNameTable( + IF_NameTable * pSrcNameTable) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + flminterface IF_DeleteStatus : public F_RefCount + { + virtual RCODE FLMAPI reportDelete( + FLMUINT uiBlocksDeleted, + FLMUINT uiBlockSize) = 0; + }; + + /**************************************************************************** + Desc: Block + ****************************************************************************/ + flminterface IF_Block : public F_RefCount + { + virtual FLMUINT FLMAPI getBlockAddress( void) = 0; + + virtual FLMBYTE * FLMAPI getBlockPtr( void) = 0; + }; + + /**************************************************************************** + Desc: Block manager + ****************************************************************************/ + flminterface IF_BlockMgr : public F_RefCount + { + virtual FLMUINT FLMAPI getBlockSize( void) = 0; + + virtual RCODE FLMAPI getBlock( + FLMUINT32 ui32BlockId, + IF_Block ** ppBlock) = 0; + + virtual RCODE FLMAPI createBlock( + IF_Block ** ppBlock) = 0; + + virtual RCODE FLMAPI freeBlock( + IF_Block ** ppBlock) = 0; + + virtual RCODE FLMAPI prepareForUpdate( + IF_Block ** ppBlock) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + flminterface IF_Relocator : public F_RefCount + { + virtual void FLMAPI relocate( + void * pvOldAlloc, + void * pvNewAlloc) = 0; + + virtual FLMBOOL FLMAPI canRelocate( + void * pvOldAlloc) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + flminterface IF_SlabManager : public F_RefCount + { + virtual RCODE FLMAPI setup( + FLMUINT uiPreallocSize) = 0; + + virtual RCODE FLMAPI allocSlab( + void ** ppSlab, + FLMBOOL bMutexLocked) = 0; + + virtual void FLMAPI freeSlab( + void ** ppSlab, + FLMBOOL bMutexLocked) = 0; + + virtual RCODE FLMAPI resize( + FLMUINT uiNumBytes, + FLMUINT * puiActualSize = NULL, + FLMBOOL bMutexLocked = FALSE) = 0; + + virtual void FLMAPI incrementTotalBytesAllocated( + FLMUINT uiCount, + FLMBOOL bMutexLocked) = 0; + + virtual void FLMAPI decrementTotalBytesAllocated( + FLMUINT uiCount, + FLMBOOL bMutexLocked) = 0; + + virtual FLMUINT FLMAPI getSlabSize( void) = 0; + + virtual FLMUINT FLMAPI getTotalSlabs( void) = 0; + + virtual void FLMAPI lockMutex( void) = 0; + + virtual void FLMAPI unlockMutex( void) = 0; + + virtual FLMUINT FLMAPI totalBytesAllocated( void) = 0; + + virtual FLMUINT FLMAPI availSlabs( void) = 0; + + virtual void FLMAPI protectSlab( + void * pSlab) = 0; + + virtual void FLMAPI unprotectSlab( + void * pSlab) = 0; + }; + + /**************************************************************************** + Desc: Class to provide an efficient means of providing many allocations + of a fixed size. + ****************************************************************************/ + flminterface IF_FixedAlloc : public F_RefCount + { + virtual RCODE FLMAPI setup( + IF_Relocator * pRelocator, + IF_SlabManager * pSlabManager, + FLMBOOL bMemProtect, + FLMUINT uiCellSize, + FLM_SLAB_USAGE * pUsageStats) = 0; + + virtual void * FLMAPI allocCell( + IF_Relocator * pRelocator, + void * pvInitialData = NULL, + FLMUINT uiDataSize = 0, + FLMBOOL bMutexLocked = FALSE) = 0; + + virtual void FLMAPI freeCell( + void * ptr, + FLMBOOL bMutexLocked) = 0; + + virtual void FLMAPI freeUnused( void) = 0; + + virtual void FLMAPI freeAll( void) = 0; + + virtual FLMUINT FLMAPI getCellSize( void) = 0; + + virtual void FLMAPI defragmentMemory( void) = 0; + + virtual void FLMAPI protectCell( + void * pvCell) = 0; + + virtual void FLMAPI unprotectCell( + void * pvCell) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + flminterface IF_BufferAlloc : public F_RefCount + { + virtual RCODE FLMAPI setup( + IF_SlabManager * pSlabManager, + FLMBOOL bMemProtect, + FLM_SLAB_USAGE * pUsageStats) = 0; + + virtual RCODE FLMAPI allocBuf( + IF_Relocator * pRelocator, + FLMUINT uiSize, + void * pvInitialData, + FLMUINT uiDataSize, + FLMBYTE ** ppucBuffer, + FLMBOOL * pbAllocatedOnHeap = NULL) = 0; + + virtual RCODE FLMAPI reallocBuf( + IF_Relocator * pRelocator, + FLMUINT uiOldSize, + FLMUINT uiNewSize, + void * pvInitialData, + FLMUINT uiDataSize, + FLMBYTE ** ppucBuffer, + FLMBOOL * pbAllocatedOnHeap = NULL) = 0; + + virtual void FLMAPI freeBuf( + FLMUINT uiSize, + FLMBYTE ** ppucBuffer) = 0; + + virtual FLMUINT FLMAPI getTrueSize( + FLMUINT uiSize, + FLMBYTE * pucBuffer) = 0; + + virtual void FLMAPI defragmentMemory( void) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + flminterface IF_MultiAlloc : public F_RefCount + { + virtual RCODE FLMAPI setup( + IF_SlabManager * pSlabManager, + FLMBOOL bMemProtect, + FLMUINT * puiCellSizes, + FLM_SLAB_USAGE * pUsageStats) = 0; + + virtual RCODE FLMAPI allocBuf( + IF_Relocator * pRelocator, + FLMUINT uiSize, + FLMBYTE ** ppucBuffer, + FLMBOOL bMutexLocked = FALSE) = 0; + + virtual RCODE FLMAPI reallocBuf( + IF_Relocator * pRelocator, + FLMUINT uiNewSize, + FLMBYTE ** ppucBuffer, + FLMBOOL bMutexLocked = FALSE) = 0; + + virtual void FLMAPI freeBuf( + FLMBYTE ** ppucBuffer) = 0; + + virtual void FLMAPI defragmentMemory( void) = 0; + + virtual FLMUINT FLMAPI getTrueSize( + FLMBYTE * pucBuffer) = 0; + + virtual void FLMAPI protectBuffer( + void * pvBuffer, + FLMBOOL bMutexLocked = FALSE) = 0; + + virtual void FLMAPI unprotectBuffer( + void * pvBuffer, + FLMBOOL bMutexLocked = FALSE) = 0; + + virtual void FLMAPI lockMutex( void) = 0; + + virtual void FLMAPI unlockMutex( void) = 0; + }; + + /**************************************************************************** + Desc: Misc. + ****************************************************************************/ + #define F_UNREFERENCED_PARM( parm) \ + (void)parm + + #define f_min(a, b) \ + ((a) < (b) ? (a) : (b)) + + #define f_max(a, b) \ + ((a) < (b) ? (b) : (a)) + + #define f_swap( a, b, tmp) \ + ((tmp) = (a), (a) = (b), (b) = (tmp)) + + RCODE FLMAPI f_filecpy( + const char * pszSourceFile, + const char * pszData); + + RCODE FLMAPI f_filecat( + const char * pszSourceFile, + const char * pszData); + + RCODE FLMAPI f_copyPartial( + IF_FileHdl * pSrcFileHdl, + FLMUINT64 ui64SrcOffset, + FLMUINT64 ui64SrcSize, + IF_FileHdl * pDestFileHdl, + FLMUINT64 ui64DestOffset, + FLMUINT64 * pui64BytesCopiedRV); + + /**************************************************************************** + Desc: Status and return codes + ****************************************************************************/ + #ifndef RC_OK + #define RC_OK( rc) ((rc) == 0) + #endif + + #ifndef RC_BAD + #define RC_BAD( rc) ((rc) != 0) + #endif + + #define FLM_ERROR_BASE(e) ((RCODE)((int)(0x81050000+(e)))) + #define FTK_ERROR_BASE(e) ((RCODE)((int)(0x81055000+(e)))) + + const char * FLMAPI f_errorString( + RCODE rc); + + /**************************************************************************** + Desc: General errors + ****************************************************************************/ + #define NE_FLM_OK 0 + + #define NE_FLM_FIRST_GENERAL_ERROR FTK_ERROR_BASE( 0x100) // NOTE: This is not an error code - do not document + #define NE_FLM_NOT_IMPLEMENTED FTK_ERROR_BASE( 0x101) // NE_NOT_IMPLEMENTED - Attempt was made to use a feature that is not implemented. + #define NE_FLM_MEM FTK_ERROR_BASE( 0x102) // NE_INSUFFICIENT_MEMORY - Attempt to allocate memory failed. + #define NE_FLM_INVALID_PARM FTK_ERROR_BASE( 0x103) // NE_INVALID_PARAMETER - Invalid parameter passed into a function. + #define NE_FLM_TIMEOUT FTK_ERROR_BASE( 0x104) // NE_WAIT_TIMEOUT - Database operation timed out (usually a query operation). + #define NE_FLM_NOT_FOUND FTK_ERROR_BASE( 0x105) // NE_OBJECT_NOT_FOUND - An object was not found. + #define NE_FLM_EXISTS FTK_ERROR_BASE( 0x106) // NE_OBJECT_ALREADY_EXISTS - Object already exists. + #define NE_FLM_USER_ABORT FTK_ERROR_BASE( 0x107) // NE_CALLBACK_CANCELLED - User or application aborted (canceled) the operation + #define NE_FLM_FAILURE FTK_ERROR_BASE( 0x108) // NE_RECOVERABLE_FAILURE - Internal failure. + #define NE_FLM_BOF_HIT FTK_ERROR_BASE( 0x109) // Beginning of results encountered. This error is may be returned when reading query results in reverse order (from last to first). + #define NE_FLM_EOF_HIT FTK_ERROR_BASE( 0x10A) // End of results encountered. This error may be returned when reading query results in forward order (first to last). + #define NE_FLM_END FTK_ERROR_BASE( 0x10B) // End of roll-forward log packets encountered. NOTE: This error code should never be returned to an application. + #define NE_FLM_CONV_BAD_DIGIT FTK_ERROR_BASE( 0x10C) // Non-numeric digit found in text to numeric conversion. + #define NE_FLM_CONV_DEST_OVERFLOW FTK_ERROR_BASE( 0x10D) // Destination buffer not large enough to hold data. + #define NE_FLM_CONV_ILLEGAL FTK_ERROR_BASE( 0x10E) // Attempt to convert between data types is an unsupported conversion. + #define NE_FLM_CONV_NULL_SRC FTK_ERROR_BASE( 0x10F) // Data source cannot be NULL when doing data conversion. + #define NE_FLM_CONV_NUM_OVERFLOW FTK_ERROR_BASE( 0x110) // Numeric overflow (> upper bound) converting to numeric type. + #define NE_FLM_CONV_NUM_UNDERFLOW FTK_ERROR_BASE( 0x111) // Numeric underflow (< lower bound) converting to numeric type. + #define NE_FLM_SYNTAX FTK_ERROR_BASE( 0x112) // Syntax error while parsing XML or query. + #define NE_FLM_UNSUPPORTED_FEATURE FTK_ERROR_BASE( 0x113) // Attempting to use a feature for which full support has been disabled. + #define NE_FLM_FILE_EXISTS FTK_ERROR_BASE( 0x114) // Attempt to create a database, but the file already exists. + #define NE_FLM_COULD_NOT_CREATE_SEMAPHORE FTK_ERROR_BASE( 0x115) // Could not create a semaphore. + #define NE_FLM_BAD_UTF8 FTK_ERROR_BASE( 0x116) // An invalid byte sequence was found in a UTF-8 string + #define NE_FLM_ERROR_WAITING_ON_SEMPAHORE FTK_ERROR_BASE( 0x117) // Error occurred while waiting on a sempahore. + #define NE_FLM_BAD_PLATFORM_FORMAT FTK_ERROR_BASE( 0x118) // Cannot support platform format. NOTE: No need to document this one, it is strictly internal. + #define NE_FLM_BAD_SEN FTK_ERROR_BASE( 0x119) // Invalid simple encoded number. + #define NE_FLM_UNSUPPORTED_INTERFACE FTK_ERROR_BASE( 0x11A) // Requested COM interface is not supported. + #define NE_FLM_BAD_RCODE_TABLE FTK_ERROR_BASE( 0x11B) // The error code tables are incorrect. NOTE: This is an internal error that does not need to be documented. + #define NE_FLM_BUFFER_OVERFLOW FTK_ERROR_BASE( 0x11C) // Buffer overflow. + #define NE_FLM_INVALID_XML FTK_ERROR_BASE( 0x11D) // Invalid XML encountered while parsing document. + #define NE_FLM_ILLEGAL_FLAG FTK_ERROR_BASE( 0x11E) // Illegal flag passed to getChildElement method. Must be zero for elements that can have non-unique child elements. + #define NE_FLM_ILLEGAL_OP FTK_ERROR_BASE( 0x11F) // Illegal operation + #define NE_FLM_COULD_NOT_START_THREAD FTK_ERROR_BASE( 0x120) // Problem starting a new thread + #define NE_FLM_BAD_BASE64_ENCODING FTK_ERROR_BASE( 0x121) // Invalid base64 sequence encountered + #define NE_FLM_STREAM_EXISTS FTK_ERROR_BASE( 0x122) // Stream file already exists + #define NE_FLM_MULTIPLE_MATCHES FTK_ERROR_BASE( 0x123) // Multiple items matched but only one match was expected + #define NE_FLM_NOT_UNIQUE FTK_ERROR_BASE( 0x124) // Non-unique key + #define NE_FLM_BTREE_ERROR FTK_ERROR_BASE( 0x125) // Generic b-tree error + #define NE_FLM_BTREE_KEY_SIZE FTK_ERROR_BASE( 0x126) // Invalid b-tree key size + #define NE_FLM_BTREE_FULL FTK_ERROR_BASE( 0x127) // B-tree cannot grow beyond current size + #define NE_FLM_LAST_GENERAL_ERROR FTK_ERROR_BASE( 0x128) // NOTE: This is not an error code - do not document + + /**************************************************************************** + Desc: I/O Errors + ****************************************************************************/ + + #define NE_FLM_FIRST_IO_ERROR FTK_ERROR_BASE( 0x200) // NOTE: This is not an error code - do not document + #define NE_FLM_IO_ACCESS_DENIED FTK_ERROR_BASE( 0x201) // Access to file is denied. Caller is not allowed access to a file. + #define NE_FLM_IO_BAD_FILE_HANDLE FTK_ERROR_BASE( 0x202) // Bad file handle or file descriptor. + #define NE_FLM_IO_COPY_ERR FTK_ERROR_BASE( 0x203) // Error occurred while copying a file. + #define NE_FLM_IO_DISK_FULL FTK_ERROR_BASE( 0x204) // Disk full. + #define NE_FLM_IO_END_OF_FILE FTK_ERROR_BASE( 0x205) // End of file reached while reading from the file. + #define NE_FLM_IO_OPEN_ERR FTK_ERROR_BASE( 0x206) // Error while opening the file. + #define NE_FLM_IO_SEEK_ERR FTK_ERROR_BASE( 0x207) // Error occurred while positioning (seeking) within a file. + #define NE_FLM_IO_DIRECTORY_ERR FTK_ERROR_BASE( 0x208) // Error occurred while accessing or deleting a directory. + #define NE_FLM_IO_PATH_NOT_FOUND FTK_ERROR_BASE( 0x209) // File not found. + #define NE_FLM_IO_TOO_MANY_OPEN_FILES FTK_ERROR_BASE( 0x20A) // Too many files open. + #define NE_FLM_IO_PATH_TOO_LONG FTK_ERROR_BASE( 0x20B) // File name too long. + #define NE_FLM_IO_NO_MORE_FILES FTK_ERROR_BASE( 0x20C) // No more files in directory. + #define NE_FLM_IO_DELETING_FILE FTK_ERROR_BASE( 0x20D) // Error occurred while deleting a file. + #define NE_FLM_IO_FILE_LOCK_ERR FTK_ERROR_BASE( 0x20E) // Error attempting to acquire a byte-range lock on a file. + #define NE_FLM_IO_FILE_UNLOCK_ERR FTK_ERROR_BASE( 0x20F) // Error attempting to release a byte-range lock on a file. + #define NE_FLM_IO_PATH_CREATE_FAILURE FTK_ERROR_BASE( 0x210) // Error occurred while attempting to create a directory or sub-directory. + #define NE_FLM_IO_RENAME_FAILURE FTK_ERROR_BASE( 0x211) // Error occurred while renaming a file. + #define NE_FLM_IO_INVALID_PASSWORD FTK_ERROR_BASE( 0x212) // Invalid file password. + #define NE_FLM_SETTING_UP_FOR_READ FTK_ERROR_BASE( 0x213) // Error occurred while setting up to perform a file read operation. + #define NE_FLM_SETTING_UP_FOR_WRITE FTK_ERROR_BASE( 0x214) // Error occurred while setting up to perform a file write operation. + #define NE_FLM_IO_CANNOT_REDUCE_PATH FTK_ERROR_BASE( 0x215) // Cannot reduce file name into more components. + #define NE_FLM_INITIALIZING_IO_SYSTEM FTK_ERROR_BASE( 0x216) // Error occurred while setting up to access the file system. + #define NE_FLM_FLUSHING_FILE FTK_ERROR_BASE( 0x217) // Error occurred while flushing file data buffers to disk. + #define NE_FLM_IO_INVALID_FILENAME FTK_ERROR_BASE( 0x218) // Invalid file name. + #define NE_FLM_IO_CONNECT_ERROR FTK_ERROR_BASE( 0x219) // Error connecting to a remote network resource. + #define NE_FLM_OPENING_FILE FTK_ERROR_BASE( 0x21A) // Unexpected error occurred while opening a file. + #define NE_FLM_DIRECT_OPENING_FILE FTK_ERROR_BASE( 0x21B) // Unexpected error occurred while opening a file in direct access mode. + #define NE_FLM_CREATING_FILE FTK_ERROR_BASE( 0x21C) // Unexpected error occurred while creating a file. + #define NE_FLM_DIRECT_CREATING_FILE FTK_ERROR_BASE( 0x21D) // Unexpected error occurred while creating a file in direct access mode. + #define NE_FLM_READING_FILE FTK_ERROR_BASE( 0x21E) // Unexpected error occurred while reading a file. + #define NE_FLM_DIRECT_READING_FILE FTK_ERROR_BASE( 0x21F) // Unexpected error occurred while reading a file in direct access mode. + #define NE_FLM_WRITING_FILE FTK_ERROR_BASE( 0x220) // Unexpected error occurred while writing to a file. + #define NE_FLM_DIRECT_WRITING_FILE FTK_ERROR_BASE( 0x221) // Unexpected error occurred while writing a file in direct access mode. + #define NE_FLM_POSITIONING_IN_FILE FTK_ERROR_BASE( 0x222) // Unexpected error occurred while positioning within a file. + #define NE_FLM_GETTING_FILE_SIZE FTK_ERROR_BASE( 0x223) // Unexpected error occurred while getting a file's size. + #define NE_FLM_TRUNCATING_FILE FTK_ERROR_BASE( 0x224) // Unexpected error occurred while truncating a file. + #define NE_FLM_PARSING_FILE_NAME FTK_ERROR_BASE( 0x225) // Unexpected error occurred while parsing a file's name. + #define NE_FLM_CLOSING_FILE FTK_ERROR_BASE( 0x226) // Unexpected error occurred while closing a file. + #define NE_FLM_GETTING_FILE_INFO FTK_ERROR_BASE( 0x227) // Unexpected error occurred while getting information about a file. + #define NE_FLM_EXPANDING_FILE FTK_ERROR_BASE( 0x228) // Unexpected error occurred while expanding a file. + #define NE_FLM_CHECKING_FILE_EXISTENCE FTK_ERROR_BASE( 0x229) // Unexpected error occurred while checking to see if a file exists. + #define NE_FLM_RENAMING_FILE FTK_ERROR_BASE( 0x22A) // Unexpected error occurred while renaming a file. + #define NE_FLM_SETTING_FILE_INFO FTK_ERROR_BASE( 0x22B) // Unexpected error occurred while setting a file's information. + #define NE_FLM_BTREE_BAD_STATE FTK_ERROR_BASE( 0x22C) + #define NE_FLM_LAST_IO_ERROR FTK_ERROR_BASE( 0x22D) // NOTE: This is not an error code - do not document + + /**************************************************************************** + Desc: Network Errors + ****************************************************************************/ + + #define NE_FLM_FIRST_NET_ERROR FTK_ERROR_BASE( 0x300) // NOTE: This is not an error code - do not document + #define NE_FLM_NOIP_ADDR FTK_ERROR_BASE( 0x301) // IP address not found + #define NE_FLM_SOCKET_FAIL FTK_ERROR_BASE( 0x302) // IP socket failure + #define NE_FLM_CONNECT_FAIL FTK_ERROR_BASE( 0x303) // TCP/IP connection failure + #define NE_FLM_BIND_FAIL FTK_ERROR_BASE( 0x304) // The TCP/IP services on your system may not be configured or installed. + #define NE_FLM_LISTEN_FAIL FTK_ERROR_BASE( 0x305) // TCP/IP listen failed + #define NE_FLM_ACCEPT_FAIL FTK_ERROR_BASE( 0x306) // TCP/IP accept failed + #define NE_FLM_SELECT_ERR FTK_ERROR_BASE( 0x307) // TCP/IP select failed + #define NE_FLM_SOCKET_SET_OPT_FAIL FTK_ERROR_BASE( 0x308) // TCP/IP socket operation failed + #define NE_FLM_SOCKET_DISCONNECT FTK_ERROR_BASE( 0x309) // TCP/IP disconnected + #define NE_FLM_SOCKET_READ_FAIL FTK_ERROR_BASE( 0x30A) // TCP/IP read failed + #define NE_FLM_SOCKET_WRITE_FAIL FTK_ERROR_BASE( 0x30B) // TCP/IP write failed + #define NE_FLM_SOCKET_READ_TIMEOUT FTK_ERROR_BASE( 0x30C) // TCP/IP read timeout + #define NE_FLM_SOCKET_WRITE_TIMEOUT FTK_ERROR_BASE( 0x30D) // TCP/IP write timeout + #define NE_FLM_SOCKET_ALREADY_CLOSED FTK_ERROR_BASE( 0x30E) // Connection already closed + #define NE_FLM_LAST_NET_ERROR FTK_ERROR_BASE( 0x30F) // NOTE: This is not an error code - do not document + + /**************************************************************************** + Desc: Stream Errors + ****************************************************************************/ + + #define NE_FLM_FIRST_STREAM_ERROR FTK_ERROR_BASE( 0x400) // NOTE: This is not an error code - do not document + #define NE_FLM_STREAM_DECOMPRESS_ERROR FTK_ERROR_BASE( 0x401) // Error decompressing data stream. + #define NE_FLM_STREAM_NOT_COMPRESSED FTK_ERROR_BASE( 0x402) // Attempting to decompress a data stream that is not compressed. + #define NE_FLM_STREAM_TOO_MANY_FILES FTK_ERROR_BASE( 0x403) // Too many files in input stream. + #define NE_FLM_LAST_STREAM_ERROR FTK_ERROR_BASE( 0x404) // NOTE: This is not an error code - do not document + +#endif // FTK_H diff --git a/ftk/src/ftkbtree.cpp b/ftk/src/ftkbtree.cpp new file mode 100644 index 0000000..6cb31d1 --- /dev/null +++ b/ftk/src/ftkbtree.cpp @@ -0,0 +1,11299 @@ +//------------------------------------------------------------------------------ +// Desc: This class handles all of operations on a given B-Tree. +// +// Tabs: 3 +// +// Copyright (c) 2002-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: f_btree.cpp 3111 2006-01-19 13:10:50 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +FSTATIC FLMUINT btGetEntryDataLength( + FLMBYTE * pucEntry, + const FLMBYTE ** ppucDataRV, + FLMUINT * puiOADataLengthRV, + FLMBOOL * pbDOBlockRV); + +FSTATIC RCODE btGetEntryData( + FLMBYTE * pucEntry, + FLMBYTE * pucBufferRV, + FLMUINT uiBufferSize, + FLMUINT * puiLenDataRV); + +/**************************************************************************** +Desc: Encrypted B-Tree block header - on-disk format. +****************************************************************************/ +typedef struct +{ + F_BTREE_BLK_HDR btree; + FLMUINT64 ui64Reserved; +} F_ENC_BTREE_BLK_HDR; + +/**************************************************************************** +Desc: Encrypted Data-only block header - on-disk format. +****************************************************************************/ +typedef struct +{ + F_BLK_HDR blk; + FLMUINT32 ui32EncId; + FLMBYTE ucReserved[ 12]; +} F_ENC_DO_BLK_HDR; + +/*************************************************************************** +Desc: +****************************************************************************/ +FINLINE FLMBOOL isRootBlk( + F_BTREE_BLK_HDR * pBlkHdr) +{ + return( (pBlkHdr->ui8BTreeFlags & BLK_IS_ROOT) ? TRUE : FALSE); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FINLINE void setRootBlk( + F_BTREE_BLK_HDR * pBlkHdr) +{ + pBlkHdr->ui8BTreeFlags |= BLK_IS_ROOT; +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FINLINE void unsetRootBlk( + F_BTREE_BLK_HDR * pBlkHdr) +{ + pBlkHdr->ui8BTreeFlags &= (~(BLK_IS_ROOT)); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FINLINE void setBlockEncrypted( + F_BLK_HDR * pBlkHdr) +{ + pBlkHdr->ui8BlkFlags |= BLK_IS_ENCRYPTED; +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FINLINE void unsetBlockEncrypted( + F_BLK_HDR * pBlkHdr) +{ + pBlkHdr->ui8BlkFlags &= (~(BLK_IS_ENCRYPTED)); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FINLINE FLMBOOL isEncryptedBlk( + F_BLK_HDR * pBlkHdr) +{ + return( (pBlkHdr->ui8BlkFlags & BLK_IS_ENCRYPTED) ? TRUE : FALSE); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FINLINE FLMUINT sizeofDOBlkHdr( + F_BLK_HDR * pBlkHdr) +{ + if( !isEncryptedBlk( pBlkHdr)) + { + return sizeof( F_BLK_HDR); + } + else + { + return( sizeof( F_ENC_DO_BLK_HDR)); + } +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FINLINE FLMUINT sizeofBTreeBlkHdr( + F_BTREE_BLK_HDR * pBlkHdr) +{ + if (!isEncryptedBlk( (F_BLK_HDR *)pBlkHdr)) + { + return sizeof( F_BTREE_BLK_HDR); + } + else + { + return sizeof( F_ENC_BTREE_BLK_HDR); + } +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FINLINE FLMBYTE * BtEntry( + FLMBYTE * pBlk, + FLMUINT uiIndex) +{ + FLMBYTE * pucOffsetArray = + pBlk + sizeofBTreeBlkHdr( (F_BTREE_BLK_HDR *)pBlk) + + (uiIndex * 2); // 2 byte offset entries. + + return( pBlk + FB2UW( pucOffsetArray)); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FINLINE FLMBYTE * BtLastEntry( + FLMBYTE * pBlk) +{ + return BtEntry( pBlk, ((F_BTREE_BLK_HDR *)pBlk)->ui16NumKeys - 1); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FINLINE FLMUINT getBlkType( + FLMBYTE * pBlk) +{ + return (FLMUINT)((F_BLK_HDR *)pBlk)->ui8BlkType; +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FINLINE FLMUINT16 * BtOffsetArray( + FLMBYTE * pBlk, + FLMUINT uiIndex) +{ + return( (FLMUINT16 *)(pBlk + sizeofBTreeBlkHdr( (F_BTREE_BLK_HDR *)pBlk) + + (uiIndex * sizeof( FLMUINT16)))); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FINLINE FLMUINT actualEntrySize( + FLMUINT uiEntrySize) +{ + return uiEntrySize - 2; +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FINLINE FLMBYTE * getBlockEnd( + F_BTREE_BLK_HDR * pBlkHdr) +{ + return ((FLMBYTE *)pBlkHdr + sizeofBTreeBlkHdr( pBlkHdr) + + (pBlkHdr->ui16NumKeys * 2) + + pBlkHdr->ui16HeapSize); +} + +/**************************************************************************** +Desc: Test to see if a block type is a B-Tree block type. +****************************************************************************/ +FINLINE FLMBOOL blkIsBTree( + F_BLK_HDR * pBlkHdr) +{ + return( (pBlkHdr->ui8BlkType != BT_FREE && + pBlkHdr->ui8BlkType != BT_LFH_BLK) + ? TRUE + : FALSE); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +F_Btree::F_Btree( void) +{ + m_pBlockMgr = NULL; + m_pPool = NULL; + m_bOpened = FALSE; + m_uiRootBlkAddr = 0; + m_pStack = NULL; + m_uiStackLevels = 0; + m_uiRootLevel = 0; + f_memset( m_Stack, 0, sizeof(m_Stack)); + m_bCounts = FALSE; + m_bData = TRUE; // Default + m_bSetupForRead = FALSE; + m_bSetupForWrite = FALSE; + m_bSetupForReplace = FALSE; + m_uiBlockSize = 0; + m_uiDefragThreshold = 0; + m_uiOverflowThreshold = 0; + m_pReplaceInfo = NULL; + m_pReplaceStruct = NULL; + m_uiReplaceLevels = 0; + m_uiDataLength = 0; + m_uiPrimaryDataLen = 0; + m_uiOADataLength = 0; + m_uiDataRemaining = 0; + m_uiOADataRemaining = 0; + m_uiOffsetAtStart = 0; + m_bDataOnlyBlock = FALSE; + m_bOrigInDOBlocks = FALSE; + m_ui32PrimaryBlkAddr = 0; + m_uiPrimaryOffset = 0; + m_ui32DOBlkAddr = 0; + m_ui32CurBlkAddr = 0; + m_uiCurOffset = 0; + m_pucDataPtr = NULL; + m_bFirstRead = FALSE; + m_pBlock = NULL; + m_pBlkHdr = NULL; + m_uiSearchLevel = BH_MAX_LEVELS; + m_pCompare = NULL; +} + +/*************************************************************************** +Desc: +****************************************************************************/ +F_Btree::~F_Btree( void) +{ + if( m_bOpened) + { + btClose(); + } +} + +/*************************************************************************** +Desc: Function to create a new (empty) B-Tree. To do this, we create the + root block. +****************************************************************************/ +RCODE F_Btree::btCreate( + IF_BlockMgr * pBlockMgr, + FLMUINT16 ui16BtreeId, + FLMBOOL bCounts, + FLMBOOL bData, + FLMUINT * puiRootBlkAddr) +{ + RCODE rc = NE_FLM_OK; + IF_Block * pBlock = NULL; + F_BTREE_BLK_HDR * pBlkHdr = NULL; + FLMUINT16 * pui16Offset; + FLMBYTE * pucEntry; + FLMBYTE ucLEMEntry[ 3]; + FLMUINT uiFlags = 0; + FLMUINT uiLEMSize; + + // We can't create a new Btree if we have already been initialized. + + if (m_bOpened) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_BAD_STATE); + goto Exit; + } + + // Initialize the returned root block address to 0 incase of an error. + + *puiRootBlkAddr = 0; + + // Call createBlock to create a new block + + if (RC_BAD( rc = pBlockMgr->createBlock( &pBlock))) + { + goto Exit; + } + + // Save the new block address as the root block address + + *puiRootBlkAddr = pBlock->getBlockAddress(); + + // Save the block address and identify the block as the root block. + + if( RC_BAD( rc = btOpen( pBlockMgr, *puiRootBlkAddr, bCounts, bData))) + { + goto Exit; + } + + pBlkHdr = (F_BTREE_BLK_HDR *)pBlock->getBlockPtr(); + + setRootBlk( pBlkHdr); + pBlkHdr->ui16BtreeId = ui16BtreeId; + pBlkHdr->ui8BlkLevel = 0; + pBlkHdr->stdBlkHdr.ui8BlkType = (bData ? BT_LEAF_DATA : BT_LEAF); + + pBlkHdr->stdBlkHdr.ui32PrevBlkInChain = 0; + pBlkHdr->stdBlkHdr.ui32NextBlkInChain = 0; + +// if (pLFile->uiEncId) +// { +// setBlockEncrypted( (F_BLK_HDR *)pBlkHdr); +// } + + // Insert a LEM into the block + + uiFlags = BTE_FLAG_FIRST_ELEMENT | BTE_FLAG_LAST_ELEMENT; + + if (RC_BAD( rc = buildAndStoreEntry( (bData ? BT_LEAF_DATA : BT_LEAF), + uiFlags, NULL, 0, NULL, 0, 0, 0, 0, &ucLEMEntry[0], + 3, &uiLEMSize))) + { + goto Exit; + } + + pui16Offset = BtOffsetArray((FLMBYTE *)pBlkHdr, 0); + pucEntry = (FLMBYTE *)pBlkHdr + m_uiBlockSize - uiLEMSize; + + bteSetEntryOffset( pui16Offset, 0, (FLMUINT16)(pucEntry - (FLMBYTE *)pBlkHdr)); + f_memcpy( pucEntry, ucLEMEntry, uiLEMSize); + + // Offset Entry and 2 byte LEM + + pBlkHdr->stdBlkHdr.ui16BlkBytesAvail = + (FLMUINT16)(m_uiBlockSize - sizeofBTreeBlkHdr(pBlkHdr) - uiLEMSize - 2); + pBlkHdr->ui16HeapSize = pBlkHdr->stdBlkHdr.ui16BlkBytesAvail; + + // There is one entry now. + + pBlkHdr->ui16NumKeys = 1; + +Exit: + + if( pBlock) + { + pBlock->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Btree initialization function. +****************************************************************************/ +RCODE F_Btree::btOpen( + IF_BlockMgr * pBlockMgr, + FLMUINT uiRootBlkAddr, + FLMBOOL bCounts, + FLMBOOL bData, + IF_ResultSetCompare * pCompare) +{ + RCODE rc = NE_FLM_OK; + + if( m_bOpened) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_BAD_STATE); + goto Exit; + } + + if( !uiRootBlkAddr) + { + rc = RC_SET_AND_ASSERT( NE_FLM_FAILURE); + goto Exit; + } + + m_uiBlockSize = pBlockMgr->getBlockSize(); + m_uiRootBlkAddr = uiRootBlkAddr; + m_uiDefragThreshold = m_uiBlockSize / 20; + m_uiOverflowThreshold = (m_uiBlockSize * 8) / 5; + m_bCounts = bCounts; + m_bData = bData; + m_pReplaceInfo = NULL; + m_uiReplaceLevels = 0; + m_uiSearchLevel = BH_MAX_LEVELS; + + m_bSetupForRead = FALSE; + m_bSetupForWrite = FALSE; + m_bSetupForReplace = FALSE; + + if( (m_pPool = f_new F_Pool) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + m_pPool->poolInit( m_uiBlockSize); + + if( RC_BAD( rc = m_pPool->poolAlloc( + sizeof( BTREE_REPLACE_STRUCT) * BH_MAX_LEVELS, + (void **)&m_pReplaceStruct))) + { + goto Exit; + } + + flmAssert( !m_pCompare); + if ((m_pCompare = pCompare) != NULL) + { + m_pCompare->AddRef(); + } + + m_bOpened = TRUE; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Btree close function +****************************************************************************/ +void F_Btree::btClose() +{ + FLMUINT uiLoop; + + if( !m_bOpened) + { + return; + } + + for (uiLoop = 0; uiLoop < BH_MAX_LEVELS; uiLoop++) + { + m_Stack[ uiLoop].pucKeyBuf = NULL; + m_Stack[ uiLoop].uiKeyBufSize = 0; + } + + btRelease(); + + if( m_pBlock) + { + flmAssert( 0); + m_pBlock->Release(); + m_pBlock = NULL; + } + + if( m_pCompare) + { + m_pCompare->Release(); + m_pCompare = NULL; + } + + if( m_pBlockMgr) + { + m_pBlockMgr->Release(); + m_pBlockMgr = NULL; + } + + if( m_pPool) + { + m_pPool->Release(); + m_pPool = NULL; + } + + m_uiRootBlkAddr = 0; + m_bOpened = FALSE; +} + +/*************************************************************************** +Desc: Delete the entire tree +****************************************************************************/ +RCODE F_Btree::btDeleteTree( + IF_DeleteStatus * ifpDeleteStatus) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiNumLevels; + FLMUINT puiBlkAddrs[ BH_MAX_LEVELS]; + FLMUINT uiLoop; + + flmAssert( m_bOpened); + + // Fill up uiBlkAddrs and calculate the number of levels. + + if( RC_BAD( rc = btGetBlockChains( puiBlkAddrs, &uiNumLevels))) + { + goto Exit; + } + + // Iterate over the list of block chains and free all of the blocks + + for( uiLoop = 0; uiLoop < uiNumLevels; uiLoop++) + { + if( RC_BAD( rc = btFreeBlockChain( + puiBlkAddrs[ uiLoop], 0, NULL, NULL, ifpDeleteStatus))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Btree::btFreeBlockChain( + FLMUINT uiStartAddr, + FLMUINT uiBlocksToFree, + FLMUINT * puiBlocksFreed, + FLMUINT * puiEndAddr, + IF_DeleteStatus * ifpDeleteStatus) +{ + RCODE rc = NE_FLM_OK; + IF_Block * pCurrentBlk = NULL; + IF_Block * pDOBlock = NULL; + FLMBYTE * pBlk; + FLMBYTE * pucEntry; + FLMUINT uiEntryNum; + FLMUINT uiDOBlkAddr; + FLMBYTE ucDOBlkAddr[ 4]; + FLMUINT uiStatusCounter = 0; + FLMUINT uiNextBlkAddr = 0; + FLMUINT uiCurrentBlkAddr = 0; + FLMUINT uiTreeBlocksFreed = 0; + FLMUINT uiDataBlocksFreed = 0; + FLMBOOL bFreeAll = FALSE; + + if( !uiBlocksToFree) + { + bFreeAll = TRUE; + } + + // Now, go through the chain and delete the blocks... + + uiCurrentBlkAddr = uiStartAddr; + while( uiCurrentBlkAddr) + { + if( !bFreeAll && uiTreeBlocksFreed >= uiBlocksToFree) + { + break; + } + + if( RC_BAD( m_pBlockMgr->getBlock( uiCurrentBlkAddr, &pCurrentBlk))) + { + goto Exit; + } + + pBlk = (FLMBYTE *)pCurrentBlk->getBlockPtr(); + uiNextBlkAddr = ((F_BLK_HDR *)pBlk)->ui32NextBlkInChain; + + // If this is a leaf block, then there may be entries + // with data-only references that will need to be cleaned up too. + + if( getBlkType( pBlk) == BT_LEAF_DATA) + { + for( uiEntryNum = 0; + uiEntryNum < ((F_BTREE_BLK_HDR *)pBlk)->ui16NumKeys; + uiEntryNum++) + { + pucEntry = BtEntry( pBlk, uiEntryNum); + + if( bteDataBlockFlag( pucEntry)) + { + // Get the data-only block address + + if( RC_BAD( rc = btGetEntryData( + pucEntry, &ucDOBlkAddr[ 0], 4, NULL))) + { + goto Exit; + } + + uiDOBlkAddr = bteGetBlkAddr( &ucDOBlkAddr[ 0]); + while( uiDOBlkAddr) + { + if( RC_BAD( rc = m_pBlockMgr->getBlock( + uiDOBlkAddr, &pDOBlock))) + { + goto Exit; + } + + uiDOBlkAddr = ((F_BLK_HDR *)pDOBlock->getBlockPtr())->ui32NextBlkInChain; + if( RC_BAD( rc = m_pBlockMgr->freeBlock( &pDOBlock))) + { + goto Exit; + } + + uiDataBlocksFreed++; + } + } + } + } + + if( RC_BAD( rc = m_pBlockMgr->freeBlock( &pCurrentBlk))) + { + goto Exit; + } + + if( ifpDeleteStatus && ++uiStatusCounter >= 25) + { + uiStatusCounter = 0; + if( RC_BAD( rc = ifpDeleteStatus->reportDelete( + uiTreeBlocksFreed + uiDataBlocksFreed, m_uiBlockSize))) + { + goto Exit; + } + } + + uiTreeBlocksFreed++; + uiCurrentBlkAddr = uiNextBlkAddr; + } + + if( puiBlocksFreed) + { + *puiBlocksFreed = uiTreeBlocksFreed; + } + + if( puiEndAddr) + { + *puiEndAddr = uiCurrentBlkAddr; + } + +Exit: + + if( pDOBlock) + { + pDOBlock->Release(); + } + + if( pCurrentBlk) + { + pCurrentBlk->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Returns the address of the first block at each level of the tree +Note: puiBlockAddrs is assumed to point to a buffer that can store + BH_MAX_LEVELS FLMUINT values +****************************************************************************/ +RCODE F_Btree::btGetBlockChains( + FLMUINT * puiBlockAddrs, + FLMUINT * puiNumLevels) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiNumLevels = 0; + FLMUINT32 ui32NextBlkAddr; + IF_Block * pCurrentBlk = NULL; + FLMBYTE * pucBlk; + FLMBYTE * pucEntry; + + flmAssert( m_bOpened); + + // Fill puiBlockAddrs and calculate the number of levels. + // NOTE: Normally, level 0 is the leaf level. In this function, + // puiBlockAddrs[ 0] is the ROOT and puiBlockAddrs[ uiNumLevels - 1] + // is the LEAF! + + ui32NextBlkAddr = (FLMUINT32)m_uiRootBlkAddr; + + while( ui32NextBlkAddr) + { + puiBlockAddrs[ uiNumLevels++] = ui32NextBlkAddr; + + if( RC_BAD( m_pBlockMgr->getBlock( ui32NextBlkAddr, &pCurrentBlk))) + { + goto Exit; + } + + pucBlk = pCurrentBlk->getBlockPtr(); + + if( getBlkType( pucBlk) == BT_LEAF || getBlkType( pucBlk) == BT_LEAF_DATA) + { + ui32NextBlkAddr = 0; + } + else + { + // The child block address is the first part of the entry + + pucEntry = BtEntry( pucBlk, 0); + ui32NextBlkAddr = bteGetBlkAddr( pucEntry); + } + + // Release the current block + + pCurrentBlk->Release(); + pCurrentBlk = NULL; + } + + *puiNumLevels = uiNumLevels; + +Exit: + + if( pCurrentBlk) + { + pCurrentBlk->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Function to insert an entry into the Btree. +****************************************************************************/ +RCODE F_Btree::btInsertEntry( + const FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT uiKeyLen, + const FLMBYTE * pucData, + FLMUINT uiDataLen, + FLMBOOL bFirst, + FLMBOOL bLast, + FLMUINT32 * pui32BlkAddr, + FLMUINT * puiOffsetIndex) +{ + RCODE rc = NE_FLM_OK; + F_BLK_HDR * pBlkHdr; + FLMBYTE pucDOAddr[ 4]; + + if ( !m_bOpened || m_bSetupForRead || m_bSetupForReplace || + (m_bSetupForWrite && bFirst)) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_BAD_STATE); + goto Exit; + } + + flmAssert( m_uiSearchLevel >= BH_MAX_LEVELS); + + if( !uiKeyLen) + { + rc = RC_SET( NE_FLM_ILLEGAL_OP); + goto Exit; + } + + // Be sure to clear the Data Only flag. + + if( bFirst) + { + m_bDataOnlyBlock = FALSE; + } + + if( bLast) + { + m_Stack[ 0].uiKeyBufSize = uiKeyBufSize; + + // We need to locate where we should insert the new entry. + + rc = findEntry( pucKey, uiKeyLen, FLM_EXACT); + + // Should not find this entry. If we get back anything other than + // an NE_FLM_NOT_FOUND, then there is a problem. + + if( rc != NE_FLM_NOT_FOUND) + { + if( RC_OK( rc)) + { + rc = RC_SET( NE_FLM_NOT_UNIQUE); + } + goto Exit; + } + } + + if( bFirst && (!bLast || (uiKeyLen + uiDataLen > m_uiOverflowThreshold))) + { + // If bLast is not set, then we will setup to store the data in + // data only blocks. The assumption is that whenever we don't see bLast + // set when starting an insert, then the data is so large that it must + // be placed in a chain of Data Only blocks. There is no way for me to + // check the final size of the data ahead of time, so I rely on the + // calling routine to figure this out for me. + + // Get one empty block to begin with. + + flmAssert( m_pBlock == NULL); + if( RC_BAD( rc = m_pBlockMgr->createBlock( &m_pBlock))) + { + goto Exit; + } + + // The data going in will be stored in Data-only blocks. + // Setup the block header... + + pBlkHdr = ((F_BLK_HDR *)m_pBlock->getBlockPtr()); + pBlkHdr->ui8BlkType = BT_DATA_ONLY; + pBlkHdr->ui32PrevBlkInChain = 0; + pBlkHdr->ui32NextBlkInChain = 0; + +// if( m_pLFile->uiEncId) +// { +// ((F_ENC_DO_BLK_HDR *)pBlkHdr)->ui32EncId = (FLMUINT32)m_pLFile->uiEncId; +// setBlockEncrypted( pBlkHdr); +// } + + pBlkHdr->ui16BlkBytesAvail = + (FLMUINT16)(m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr)); + + m_uiDataRemaining = m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr); + m_uiDataLength = 0; + m_uiOADataLength = 0; + m_bDataOnlyBlock = TRUE; + m_bSetupForWrite = TRUE; + m_ui32DOBlkAddr = pBlkHdr->ui32BlkAddr; + m_ui32CurBlkAddr = m_ui32DOBlkAddr; + } + + if( m_bDataOnlyBlock) + { + if( RC_BAD( rc = storeDataOnlyBlocks( pucKey, uiKeyLen, bFirst, + pucData, uiDataLen))) + { + goto Exit; + } + } + + if( bLast) + { + const FLMBYTE * pucLocalData; + FLMUINT uiLocalDataLen; + F_ELM_UPD_ACTION eAction; + + if( m_bDataOnlyBlock) + { + // build an entry that points to the DO block. + + UD2FBA( m_ui32DOBlkAddr, pucDOAddr); + pucLocalData = &pucDOAddr[0]; + uiLocalDataLen = m_uiOADataLength; + eAction = ELM_INSERT_DO; + + } + else + { + pucLocalData = pucData; + uiLocalDataLen = uiDataLen; + eAction = ELM_INSERT; + } + + if( RC_BAD( rc = updateEntry( pucKey, uiKeyLen, pucLocalData, + uiLocalDataLen, eAction))) + { + goto Exit; + } + + if( pui32BlkAddr) + { + *pui32BlkAddr = m_ui32PrimaryBlkAddr; + } + + if( puiOffsetIndex) + { + *puiOffsetIndex = m_uiCurOffset; + } + + m_bSetupForWrite = FALSE; + } + +Exit: + + if( m_pBlock) + { + m_pBlock->Release(); + m_pBlock = NULL; + } + + releaseBlocks( TRUE); + return( rc); +} + +/*************************************************************************** +Desc: Function to remove an entry into the Btree. +****************************************************************************/ +RCODE F_Btree::btRemoveEntry( + const FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT uiKeyLen) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucValue = NULL; + FLMUINT uiLen = 0; + + if ( !m_bOpened) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_BAD_STATE); + goto Exit; + } + + btResetBtree(); + + m_Stack[ 0].uiKeyBufSize = uiKeyBufSize; + + // We need to locate where we should remove the entry. + if (RC_BAD( rc = findEntry( pucKey, + uiKeyLen, + FLM_EXACT))) + { + goto Exit; + } + + if (RC_BAD( rc = updateEntry( pucKey, uiKeyLen, pucValue, uiLen, + ELM_REMOVE))) + { + goto Exit; + } + +Exit: + + releaseBlocks( TRUE); + return( rc); +} + +/*************************************************************************** +Desc: Function to provide a streaming interface for replacing large + data elements. +****************************************************************************/ +RCODE F_Btree::btReplaceEntry( + const FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT uiKeyLen, + const FLMBYTE * pucData, + FLMUINT uiDataLen, + FLMBOOL bFirst, + FLMBOOL bLast, + FLMBOOL bTruncate, + FLMUINT32 * pui32BlkAddr, + FLMUINT * puiOffsetIndex) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucEntry; + F_BLK_HDR * pBlkHdr; + const FLMBYTE * pucLocalData; + FLMUINT uiOADataLength = 0; + FLMBYTE pucDOAddr[ 4]; + + if( !m_bOpened || m_bSetupForRead || m_bSetupForWrite || + (m_bSetupForReplace && bFirst)) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_BAD_STATE); + goto Exit; + } + + flmAssert( m_uiSearchLevel >= BH_MAX_LEVELS); + + if (!uiKeyLen) + { + rc = RC_SET( NE_FLM_ILLEGAL_OP); + goto Exit; + } + + // Be sure to clear the Data Only flags + + if( bFirst) + { + m_bDataOnlyBlock = FALSE; + m_bOrigInDOBlocks = FALSE; + } + + if( bFirst || bLast) + { + m_Stack[ 0].uiKeyBufSize = uiKeyBufSize; + + // We need to locate the entry we want to replace + + if( RC_BAD( rc = findEntry( pucKey, uiKeyLen, FLM_EXACT, NULL, + pui32BlkAddr, puiOffsetIndex))) + { + goto Exit; + } + + // We must first determine if the existing entry is stored + // in data only blocks. + + pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, + m_pStack->uiCurOffset); + + btGetEntryDataLength( pucEntry, &pucLocalData, + &uiOADataLength, &m_bOrigInDOBlocks); + + } + + if( bFirst && (!bLast || (bLast && !bTruncate && m_bOrigInDOBlocks) || + (uiKeyLen + uiDataLen > m_uiOverflowThreshold))) + { + // If bLast is not set, then we will setup to store the data in + // data only blocks. + + m_bDataOnlyBlock = TRUE; + flmAssert( m_pBlock == NULL); + + if( m_bOrigInDOBlocks) + { + // Need to get the first DO block, and work from there. + + m_ui32DOBlkAddr = bteGetBlkAddr( pucLocalData); + + if( RC_BAD( rc = m_pBlockMgr->getBlock( m_ui32DOBlkAddr, &m_pBlock))) + { + goto Exit; + } + + pBlkHdr = ((F_BLK_HDR *)m_pBlock->getBlockPtr()); + } + else + { + // Get one empty block to begin with + + if( RC_BAD( rc = m_pBlockMgr->createBlock( &m_pBlock))) + { + goto Exit; + } + + // The data going in will be stored in Data-only blocks. + // Setup the block header... + + pBlkHdr = ((F_BLK_HDR *)m_pBlock->getBlockPtr()); + pBlkHdr->ui8BlkType = BT_DATA_ONLY; + pBlkHdr->ui32PrevBlkInChain = 0; + pBlkHdr->ui32NextBlkInChain = 0; + +// if (m_pLFile->uiEncId) +// { +// ((F_ENC_DO_BLK_HDR *)pBlkHdr)->ui32EncId = (FLMUINT32)m_pLFile->uiEncId; +// setBlockEncrypted( pBlkHdr); +// } + + pBlkHdr->ui16BlkBytesAvail = + (FLMUINT16)(m_uiBlockSize - + sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr)); + } + + m_uiDataRemaining = m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)m_pBlock->getBlockPtr()); + m_uiDataLength = 0; + m_uiOADataLength = 0; + m_bDataOnlyBlock = TRUE; + m_bSetupForReplace = TRUE; + m_ui32DOBlkAddr = pBlkHdr->ui32BlkAddr; + m_ui32CurBlkAddr = m_ui32DOBlkAddr; + } + + if( m_bDataOnlyBlock) + { + if( !bTruncate && !m_bOrigInDOBlocks) + { + bTruncate = TRUE; + } + + // May need to skip over the key that is stored in the first DO block. + // We only want to do this the first time in here. The test to determine + // if this is our first time in this block is to see if the m_uiDataLength + // is equal to the m_uiDataRemaining. They would only be the same on the + // first time for each DO block. + + pBlkHdr = ((F_BLK_HDR *)m_pBlock->getBlockPtr()); + + if( m_bOrigInDOBlocks && m_pBlock && + pBlkHdr->ui32PrevBlkInChain == 0 && !m_uiDataLength) + { + m_uiDataRemaining -= (uiKeyLen + 2); + } + + if( RC_BAD( rc = replaceDataOnlyBlocks( pucKey, uiKeyLen, + !m_bOrigInDOBlocks && bFirst, pucData, uiDataLen, bLast, + bTruncate))) + { + goto Exit; + } + } + + // If we were writing to Data Only Blocks and we are not truncating the + // data, then we are done here. + + if( m_bDataOnlyBlock && !bTruncate) + { + if( bLast && (uiOADataLength <= m_uiOADataLength)) + { + bTruncate = TRUE; + } + else + { + goto Exit; + } + } + + // Only replace the entry on the last call. + + if( bLast) + { + FLMUINT uiLocalDataLen; + F_ELM_UPD_ACTION eAction; + + if (m_bDataOnlyBlock) + { + // build an entry that points to the DO block. + + UD2FBA( m_ui32DOBlkAddr, pucDOAddr); + + pucLocalData = &pucDOAddr[0]; + uiLocalDataLen = m_uiOADataLength; + eAction = ELM_REPLACE_DO; + + } + else + { + pucLocalData = pucData; + uiLocalDataLen = uiDataLen; + eAction = ELM_REPLACE; + } + + if( RC_BAD( rc = updateEntry( pucKey, uiKeyLen, pucLocalData, + uiLocalDataLen, eAction, bTruncate))) + { + goto Exit; + } + } + +Exit: + + if (RC_OK( rc)) + { + if (pui32BlkAddr) + { + *pui32BlkAddr = m_ui32PrimaryBlkAddr; + } + + if (puiOffsetIndex) + { + *puiOffsetIndex = m_uiCurOffset; + } + } + + if( bLast) + { + m_bSetupForReplace = FALSE; + } + + if (m_pBlock) + { + m_pBlock->Release(); + m_pBlock = NULL; + } + + releaseBlocks( TRUE); + return( rc); +} + +/*************************************************************************** +Desc: Function to search the Btree for a specific key. +****************************************************************************/ +RCODE F_Btree::btLocateEntry( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMUINT uiMatch, + FLMUINT * puiPosition, // May be NULL + FLMUINT * puiDataLength, + FLMUINT32 * pui32BlkAddr, + FLMUINT * puiOffsetIndex) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucEntry = NULL; + F_BLK_HDR * pBlkHdr = NULL; + + flmAssert( pucKey && uiKeyBufSize && puiKeyLen); + + if (!m_bOpened || m_bSetupForWrite || m_bSetupForReplace) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_BAD_STATE); + goto Exit; + } + + m_bSetupForRead = FALSE; + + m_Stack[ 0].uiKeyBufSize = uiKeyBufSize; + + // Find the entry we are interested in. + + if (RC_BAD(rc = findEntry( pucKey, *puiKeyLen, uiMatch, puiPosition, + pui32BlkAddr, puiOffsetIndex))) + { + goto Exit; + } + + m_ui32PrimaryBlkAddr = m_pStack->ui32BlkAddr; + m_uiPrimaryOffset = m_pStack->uiCurOffset; + m_ui32CurBlkAddr = m_ui32PrimaryBlkAddr; + m_uiCurOffset = m_uiPrimaryOffset; + + // Point to the entry... + + pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, + m_pStack->uiCurOffset); + + // Return the optional data length - get the overall data length only. + + pBlkHdr = ((F_BLK_HDR *)m_pStack->pBlock->getBlockPtr()); + + if (puiDataLength && pBlkHdr->ui8BlkType == BT_LEAF_DATA) + { + btGetEntryDataLength( pucEntry, NULL, puiDataLength, NULL); + } + else if (puiDataLength) + { + *puiDataLength = 0; + } + + if( RC_BAD( rc = setupReadState( pBlkHdr, pucEntry))) + { + goto Exit; + } + + // In case the returning key is not what was originally requested, such as + // in the case of FLM_FIRST, FLM_LAST, FLM_EXCL and possibly FLM_INCL, + // we will pass back the key we actually found. + + if( uiMatch != FLM_EXACT) + { + if( RC_BAD( rc = setReturnKey( pucEntry, + m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType, pucKey, puiKeyLen, + uiKeyBufSize))) + { + goto Exit; + } + } + + m_bFirstRead = FALSE; + m_bSetupForRead = TRUE; + +Exit: + + releaseBlocks( FALSE); + return( rc); +} + +/*************************************************************************** +Desc: Method to get the data after a call to btLocateEntry, btNextEntry, + btPrevEntry, btFirstEntry or btLastEntry. +****************************************************************************/ +RCODE F_Btree::btGetEntry( + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMBYTE * pucData, + FLMUINT uiDataBufSize, + FLMUINT * puiDataLen) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucEntry; + F_BLK_HDR * pBlkHdr; + + if( !m_bOpened || !m_bSetupForRead || + m_bSetupForWrite || m_bSetupForReplace) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_BAD_STATE); + goto Exit; + } + + if( puiDataLen) + { + *puiDataLen = 0; + } + + // Is there anything there to get? + + if( m_uiOADataRemaining == 0) + { + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + + // Get the current block. It is either a DO or a Btree block. + + if( !m_pBlock) + { + if( RC_BAD( rc = m_pBlockMgr->getBlock( m_ui32CurBlkAddr, &m_pBlock))) + { + goto Exit; + } + } + + pBlkHdr = ((F_BLK_HDR *)m_pBlock->getBlockPtr()); + + // Now to find where we were the last time through. + + if( !m_bDataOnlyBlock) + { + pucEntry = BtEntry( (FLMBYTE *)pBlkHdr, m_uiCurOffset); + + btGetEntryDataLength( pucEntry, &m_pucDataPtr, NULL, NULL); + } + else + { + m_pucDataPtr = ((FLMBYTE *)pBlkHdr) + sizeofDOBlkHdr( pBlkHdr); + + // May need to skip over the key that is stored in the first DO block. + // We only want to do this the first time in here. The test to determine + // if this is our first time in this block is to see if the m_uiDataLength + // is equal to the m_uiDataRemaining. They would only be the same on the + // first time for each DO block. + + if( m_pBlock && pBlkHdr->ui32PrevBlkInChain == 0) + { + FLMUINT16 ui16KeyLen = FB2UW( m_pucDataPtr); + + // Key lengths should be the same + + flmAssert( uiKeyLen == (FLMUINT)ui16KeyLen); + + m_pucDataPtr += (ui16KeyLen + 2); + } + } + + m_pucDataPtr += (m_uiDataLength - m_uiDataRemaining); + + if( RC_BAD( rc = extractEntryData( pucKey, uiKeyLen, pucData, + uiDataBufSize, puiDataLen))) + { + goto Exit; + } + + // Mark that we have completed our first read operation. + // No more read synchronization allowed. + + m_bFirstRead = TRUE; + +Exit: + + if( m_pBlock) + { + m_pBlock->Release(); + m_pBlock = NULL; + } + + releaseBlocks( FALSE); + return( rc); +} + +/*************************************************************************** +Desc: Function to locate the next entry in the Btree. The key buffer and + actual size is passed in. +****************************************************************************/ +RCODE F_Btree::btNextEntry( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMUINT * puiDataLength, + FLMUINT32 * pui32BlkAddr, + FLMUINT * puiOffsetIndex) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucEntry = NULL; + FLMBOOL bAdvanced = FALSE; + F_BLK_HDR * pBlkHdr; + + if( !m_bOpened || !m_bSetupForRead) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_BAD_STATE); + goto Exit; + } + + // Make sure we are looking at btree block. If the m_bDataOnlyBlock + // flag is set, then the block address in m_ui32CurBlkAddr is a + // data only block. We must reset it to the primary block address. + + if( m_bDataOnlyBlock) + { + m_ui32CurBlkAddr = m_ui32PrimaryBlkAddr; + } + else + { + // If the entry did not reference a DO block, then we need to + // reset the primary block and offset with where we currently + // are incase the current block is further ahead. This saves time + // so that we don't have to scan past old blocks we are not intereseted + // in. + + m_ui32PrimaryBlkAddr = m_ui32CurBlkAddr; + m_uiPrimaryOffset = m_uiCurOffset; + } + + // Get the current block if we need it. + + if( !m_pBlock) + { + if( RC_BAD( rc = m_pBlockMgr->getBlock( m_ui32CurBlkAddr, &m_pBlock))) + { + goto Exit; + } + } + + // If we have already advanced due to a resynch, then we don't need to call + // the advanceToNextElement function, however, we do need to get the + // current entry. + + if( bAdvanced) + { + pucEntry = BtEntry( m_pBlock->getBlockPtr(), m_uiCurOffset); + } + else + { + for (;;) + { + // Advance to the next entry in the block. We don't have a stack so + // don't advance it. + + if( RC_BAD( rc = advanceToNextElement( FALSE))) + { + goto Exit; + } + + pucEntry = BtEntry( m_pBlock->getBlockPtr(), m_uiCurOffset); + + if( m_bData) + { + if( bteFirstElementFlag(pucEntry)) + { + break; + } + } + else + { + break; + } + } + } + + // Return the optional data length - get the overall data length only. + + if( puiDataLength) + { + btGetEntryDataLength( pucEntry, NULL, puiDataLength, NULL); + } + + pBlkHdr = ((F_BLK_HDR *)m_pBlock->getBlockPtr()); + + if( RC_BAD( rc = setupReadState( pBlkHdr, pucEntry))) + { + goto Exit; + } + + // Incase the returning key is not what was originally requested, such as in + // the case of FLM_FIRST, FLM_LAST, FLM_EXCL and possibly FLM_INCL, + // we will pass back the key we actually found. + + if( RC_BAD( rc = setReturnKey( pucEntry, pBlkHdr->ui8BlkType, + pucKey, puiKeyLen, uiKeyBufSize))) + { + goto Exit; + } + + if( pui32BlkAddr) + { + *pui32BlkAddr = pBlkHdr->ui32BlkAddr; + } + + if( puiOffsetIndex) + { + *puiOffsetIndex = m_uiCurOffset; + } + + m_bFirstRead = FALSE; + +Exit: + + if( m_pBlock) + { + m_pBlock->Release(); + m_pBlock = NULL; + } + + releaseBlocks( FALSE); + return( rc); +} + +/*************************************************************************** +Desc: Function to get the previous entry in the Btree. +****************************************************************************/ +RCODE F_Btree::btPrevEntry( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMUINT * puiDataLength, + FLMUINT32 * pui32BlkAddr, + FLMUINT * puiOffsetIndex) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucEntry = NULL; + F_BLK_HDR * pBlkHdr; + + if( !m_bOpened || !m_bSetupForRead) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_BAD_STATE); + goto Exit; + } + + // Make sure we are looking at the first block of the + // current entry. Reading of the entry could have moved us + // to another block, or if it was in a DO block, we would be + // looking at the wrong block altogether. + + m_ui32CurBlkAddr = m_ui32PrimaryBlkAddr; + m_uiCurOffset = m_uiPrimaryOffset; + + if( !m_pBlock) + { + // Fetch the current block, then backup from there. + + if( RC_BAD( rc = m_pBlockMgr->getBlock( m_ui32CurBlkAddr, &m_pBlock))) + { + goto Exit; + } + } + + for (;;) + { + // Backup to the previous entry in the block. + + if( RC_BAD( rc = backupToPrevElement( FALSE))) + { + goto Exit; + } + + // Get the entry, size etc. + + pucEntry = BtEntry( m_pBlock->getBlockPtr(), m_uiCurOffset); + + if( m_bData) + { + if( bteFirstElementFlag( pucEntry)) + { + break; + } + } + else + { + break; + } + } + + // Return the optional data length - get the overall data length only. + + if( puiDataLength) + { + btGetEntryDataLength( pucEntry, NULL, puiDataLength, NULL); + } + + pBlkHdr = ((F_BLK_HDR *)m_pBlock->getBlockPtr()); + + if( RC_BAD( rc = setupReadState( pBlkHdr, pucEntry))) + { + goto Exit; + } + + // In case the returning key is not what was originally requested, such as in + // the case of FLM_FIRST, FLM_LAST, FLM_EXCL and possibly FLM_INCL, + // we will pass back the key we actually found. + + if( RC_BAD( rc = setReturnKey( pucEntry, pBlkHdr->ui8BlkType, + pucKey, puiKeyLen, uiKeyBufSize))) + { + goto Exit; + } + + if( pui32BlkAddr) + { + *pui32BlkAddr = pBlkHdr->ui32BlkAddr; + } + + if( puiOffsetIndex) + { + *puiOffsetIndex = m_uiCurOffset; + } + + m_bFirstRead = FALSE; + +Exit: + + if( m_pBlock) + { + m_pBlock->Release(); + m_pBlock = NULL; + } + + releaseBlocks( FALSE); + return( rc); +} + +/*************************************************************************** +Desc: Locate the first entry in the Btree and return the key. +****************************************************************************/ +RCODE F_Btree::btFirstEntry( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMUINT * puiDataLength, + FLMUINT32 * pui32BlkAddr, + FLMUINT * puiOffsetIndex) +{ + RCODE rc = NE_FLM_OK; + + m_Stack[ 0].pucKeyBuf = pucKey; + m_Stack[ 0].uiKeyBufSize = uiKeyBufSize; + + if( RC_BAD( rc = btLocateEntry( pucKey, uiKeyBufSize, puiKeyLen, + FLM_FIRST, NULL, puiDataLength, pui32BlkAddr, puiOffsetIndex))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Locate the last entry in the Btree and return the key. +****************************************************************************/ +RCODE F_Btree::btLastEntry( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMUINT * puiDataLength, + FLMUINT32 * pui32BlkAddr, + FLMUINT * puiOffsetIndex) +{ + RCODE rc = NE_FLM_OK; + + m_Stack[ 0].pucKeyBuf = pucKey; + m_Stack[ 0].uiKeyBufSize = uiKeyBufSize; + + if( RC_BAD( rc = btLocateEntry( pucKey, uiKeyBufSize, puiKeyLen, + FLM_LAST, NULL, puiDataLength, pui32BlkAddr, puiOffsetIndex))) + { + if( rc == NE_FLM_BOF_HIT) + { + rc = RC_SET( NE_FLM_EOF_HIT); + } + + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Function to search the Btree for a specific key. +****************************************************************************/ +RCODE F_Btree::btPositionTo( + FLMUINT uiPosition, + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucEntry = NULL; + + flmAssert( pucKey && uiKeyBufSize && puiKeyLen); + + m_bSetupForRead = FALSE; + + if( !m_bOpened || !m_bCounts) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_BAD_STATE); + goto Exit; + } + + // Find the entry we are interested in. + + if( RC_BAD(rc = positionToEntry( uiPosition))) + { + goto Exit; + } + + m_ui32PrimaryBlkAddr = m_pStack->ui32BlkAddr; + m_uiPrimaryOffset = m_pStack->uiCurOffset; + m_ui32CurBlkAddr = m_ui32PrimaryBlkAddr; + m_uiCurOffset = m_uiPrimaryOffset; + + // Point to the entry ... + + pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); + + if( RC_BAD( rc = setupReadState( (F_BLK_HDR *)m_pStack->pBlkHdr, pucEntry))) + { + goto Exit; + } + + // In case the returning key is not what was originally requested, such + // as in the case of FLM_FIRST, FLM_LAST, FLM_EXCL and + // possibly FLM_INCL, we will pass back the key we actually found. + + if( RC_BAD( rc = setReturnKey( pucEntry, + m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType, pucKey, puiKeyLen, + uiKeyBufSize))) + { + goto Exit; + } + + m_bFirstRead = FALSE; + m_bSetupForRead = TRUE; + +Exit: + + if( m_pBlock) + { + m_pBlock->Release(); + m_pBlock = NULL; + } + + releaseBlocks( FALSE); + return( rc); +} + +/*************************************************************************** +Desc: Method to get the actual poisition of the entry. Note: Must be + maintaining counts in the Btree AND also have located to an entry + first. The key that is passed in is used only if we have to + resynchronize due to a transaction change. +****************************************************************************/ +RCODE F_Btree::btGetPosition( + FLMUINT * puiPosition) +{ + RCODE rc = NE_FLM_OK; + + if( !m_bOpened || !m_bSetupForRead || !m_bCounts) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_BAD_STATE); + goto Exit; + } + + *puiPosition = 0; + + m_ui32CurBlkAddr = m_ui32PrimaryBlkAddr; + m_uiCurOffset = m_uiPrimaryOffset; + + // To calculate the position, we will have to reconstruct the stack. + + m_pStack = &m_Stack[ m_uiStackLevels - 1]; + for (;;) + { + // Get the block at this level. + + flmAssert( m_pStack->ui32BlkAddr); + flmAssert( m_pStack->pBlock == NULL); + + if( RC_BAD( rc = m_pBlockMgr->getBlock( m_pStack->ui32BlkAddr, + &m_pStack->pBlock))) + { + goto Exit; + } + + m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pBlock->getBlockPtr(); + + *puiPosition += countRangeOfKeys( m_pStack, 0, m_pStack->uiCurOffset); + + if( (getBlkType( (FLMBYTE *)m_pStack->pBlkHdr) == BT_LEAF) || + (getBlkType( (FLMBYTE *)m_pStack->pBlkHdr) == BT_LEAF_DATA)) + { + break; + } + else + { + // Next level down. (stack is inverted). + + m_pStack--; + } + } + +Exit: + + releaseBlocks( FALSE); + return( rc); +} + +/*************************************************************************** +Desc: Method to rewind back to the beginning of the current entry. +****************************************************************************/ +RCODE F_Btree::btRewind( void) +{ + RCODE rc = NE_FLM_OK; + IF_Block * pBlock = NULL; + F_BLK_HDR * pBlkHdr; + + if( !m_bSetupForRead) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_BAD_STATE); + goto Exit; + } + + m_ui32CurBlkAddr = m_ui32PrimaryBlkAddr; + m_uiCurOffset = m_uiPrimaryOffset; + + m_uiOADataRemaining = m_uiOADataLength; // Track the overall length progress + m_uiDataLength = m_uiPrimaryDataLen; // Restore the primary block data length + m_uiDataRemaining = m_uiDataLength; // Track the local entry progress + + if( m_bDataOnlyBlock) + { + m_ui32CurBlkAddr = m_ui32DOBlkAddr; + + if( RC_BAD( rc = m_pBlockMgr->getBlock( m_ui32DOBlkAddr, &pBlock))) + { + goto Exit; + } + + // Local amount of data in this block + + pBlkHdr = ((F_BLK_HDR *)pBlock->getBlockPtr()); + + m_uiDataRemaining = m_uiBlockSize - sizeofDOBlkHdr(pBlkHdr) - + pBlkHdr->ui16BlkBytesAvail; + + // Keep the actual local data size for later. + + m_uiDataLength = m_uiDataRemaining; + + // Now release the DO Block. We will get it again when we need it. + + pBlock->Release(); + pBlock = NULL; + } + + m_bFirstRead = FALSE; + m_bSetupForRead = TRUE; + +Exit: + + if( m_pBlock) + { + m_pBlock->Release(); + m_pBlock = NULL; + } + + releaseBlocks( FALSE); + return( rc); +} + +/*************************************************************************** +Desc: Method for computing the number of keys and blocks between two points + in the Btree. The key count is inclusive of the two end points and + the block count is exclusive of the two end points. +****************************************************************************/ +RCODE F_Btree::btComputeCounts( + F_Btree * pUntilBtree, + FLMUINT * puiBlkCount, + FLMUINT * puiKeyCount, + FLMBOOL * pbTotalsEstimated, + FLMUINT uiAvgBlkFullness) +{ + RCODE rc = NE_FLM_OK; + + if( !m_bSetupForRead || !pUntilBtree->m_bSetupForRead) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_BAD_STATE); + goto Exit; + } + + // Ensure that both Btrees are from the same container. + + if( m_uiRootBlkAddr != pUntilBtree->m_uiRootBlkAddr) + { + rc = RC_SET_AND_ASSERT( NE_FLM_FAILURE); + goto Exit; + } + + rc = computeCounts( m_pStack, pUntilBtree->m_pStack, puiBlkCount, + puiKeyCount, pbTotalsEstimated, uiAvgBlkFullness); + +Exit: + + releaseBlocks( FALSE); + pUntilBtree->releaseBlocks( FALSE); + return( rc); +} + +/*************************************************************************** +Desc: Function to release the blocks in the stack, and optionally, reset + the stack +****************************************************************************/ +void F_Btree::releaseBlocks( + FLMBOOL bResetStack) +{ + FLMUINT uiLevel; + + // Release any blocks still held in the stack. + + for( uiLevel = 0; uiLevel <= m_uiRootLevel; uiLevel++) + { + if( m_Stack[ uiLevel].pBlock) + { + if( m_Stack[ uiLevel].pBlock) + { + m_Stack[ uiLevel].pBlock->Release(); + } + + m_Stack[ uiLevel].pBlock = NULL; + m_Stack[ uiLevel].pBlkHdr = NULL; + } + + if( bResetStack) + { + m_Stack[ uiLevel].ui32BlkAddr = 0; + m_Stack[ uiLevel].uiKeyLen = 0; + m_Stack[ uiLevel].uiCurOffset = 0; + m_Stack[ uiLevel].uiLevel = 0; + } + } + + if( bResetStack) + { + m_uiStackLevels = 0; + m_uiRootLevel = 0; + m_bStackSetup = FALSE; + m_pStack = NULL; + } +} + +/*************************************************************************** +Desc: Function to create a new block at the current level. The new block + will always be inserted previous to the current block. All entries + that sort ahead of the current insertion point will be moved into + the new block. If there is room, the new entry will be inserted + into the current block. Otherwise, if there is room, the new entry + will be inserted into the new block. If there is still not enough + room, then if possible, it try to store a partial entry in the new + block. If we still cannot store anything, we will see if we can + store a partial entry in the current block. If that does not work, + then it will set the remaining amount and return. Another block + split will be needed before we store this entry. +****************************************************************************/ +RCODE F_Btree::splitBlock( + const FLMBYTE * pucKey, + FLMUINT uiKeyLen, + const FLMBYTE * pucValue, + FLMUINT uiLen, + FLMUINT uiFlags, + FLMUINT uiOADataLen, + FLMUINT uiChildBlkAddr, + FLMUINT uiCounts, + const FLMBYTE ** ppucRemainingValue, + FLMUINT * puiRemainingLen, + FLMBOOL * pbBlockSplit) +{ + RCODE rc = NE_FLM_OK; + IF_Block * pNewBlock = NULL; + IF_Block * pPrevBlock = NULL; + F_BTREE_BLK_HDR * pBlkHdr = NULL; + FLMUINT uiBlkAddr; + FLMUINT uiEntrySize; + FLMBOOL bHaveRoom; + FLMBOOL bMovedToPrev = FALSE; + FLMBOOL bLastEntry; + FLMUINT uiMinEntrySize; + FLMBOOL bDefragBlk = FALSE; + FLMBOOL bSavedReplaceInfo = FALSE; + + // If the current block is a root block, then we will have to introduce + // a new level into the B-Tree. + + if( isRootBlk( m_pStack->pBlkHdr)) + { + if( RC_BAD( rc = createNewLevel())) + { + goto Exit; + } + } + + // If the current block is empty we must insert what we can here. + // This scenario only occurs when we are engaged in a ReplaceByInsert + // operation. Normal inserts would never result in an empty block. + // Since we know we are part of a replace operation, we know that the + // parent of this block only needs the counts updated, not the key. + + if( m_pStack->uiLevel == 0 && m_pStack->pBlkHdr->ui16NumKeys == 0) + { + if( RC_BAD( rc = storePartialEntry( pucKey, uiKeyLen, pucValue, + uiLen, uiFlags, uiChildBlkAddr, uiCounts, ppucRemainingValue, + puiRemainingLen, FALSE))) + { + goto Exit; + } + + *pbBlockSplit = FALSE; + goto MoveToPrev; + } + + // Create a new block and insert it as previous to this block. + + if( RC_BAD( rc = m_pBlockMgr->createBlock( &pNewBlock))) + { + goto Exit; + } + + *pbBlockSplit = TRUE; + + // Setup the header ... + + pBlkHdr = (F_BTREE_BLK_HDR *)pNewBlock->getBlockPtr(); + + unsetRootBlk( pBlkHdr); + pBlkHdr->ui16NumKeys = 0; + pBlkHdr->ui8BlkLevel = (FLMUINT8)m_pStack->uiLevel; + pBlkHdr->stdBlkHdr.ui8BlkType = m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType; + pBlkHdr->ui16BtreeId = m_pStack->pBlkHdr->ui16BtreeId; + + // Check for encrypted block. + + if( isEncryptedBlk( (F_BLK_HDR *)m_pStack->pBlkHdr)) + { + setBlockEncrypted( (F_BLK_HDR *)pBlkHdr); + } + + pBlkHdr->stdBlkHdr.ui16BlkBytesAvail = + (FLMUINT16)(m_uiBlockSize - sizeofBTreeBlkHdr(pBlkHdr)); + + pBlkHdr->ui16HeapSize = + (FLMUINT16)(m_uiBlockSize - sizeofBTreeBlkHdr(pBlkHdr)); + + // We are going to make changes to the current block. The pBlock could + // have changed since making this call, so we need to update the block + // header + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &m_pStack->pBlock))) + { + goto Exit; + } + + m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pBlock->getBlockPtr(); + m_pStack->pui16OffsetArray = + BtOffsetArray( (FLMBYTE *)m_pStack->pBlkHdr, 0); + + // Get the current previous block if there is one. + + uiBlkAddr = m_pStack->pBlkHdr->stdBlkHdr.ui32PrevBlkInChain; + + if( uiBlkAddr) + { + if( RC_BAD( rc = m_pBlockMgr->getBlock( uiBlkAddr, &pPrevBlock))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &pPrevBlock))) + { + goto Exit; + } + } + + // Link the new block between the current and it's previous + + pBlkHdr->stdBlkHdr.ui32NextBlkInChain = m_pStack->ui32BlkAddr; + pBlkHdr->stdBlkHdr.ui32PrevBlkInChain = (FLMUINT32)uiBlkAddr; + + m_pStack->pBlkHdr->stdBlkHdr.ui32PrevBlkInChain = + pBlkHdr->stdBlkHdr.ui32BlkAddr; + + // There may not be a previous block. + + if( pPrevBlock) + { + F_BLK_HDR * pTmpBlkHdr = ((F_BLK_HDR *)pPrevBlock->getBlockPtr()); + pTmpBlkHdr->ui32NextBlkInChain = pBlkHdr->stdBlkHdr.ui32BlkAddr; + + // Release the old previous block since we no longer need it. + + pPrevBlock->Release(); + pPrevBlock = NULL; + } + + // We will move all entries in the current block up to but NOT including + // the entry pointed to by uiCurOffset to the new block. + + if( m_pStack->uiCurOffset > 0) + { + if( RC_BAD( rc = moveToPrev( 0, m_pStack->uiCurOffset - 1, &pNewBlock))) + { + goto Exit; + } + + // All entries prior to the old insertion point were moved. + // Therefore, the new insertion point must be at the beginning. + + m_pStack->uiCurOffset = 0; + + // If we emptied the block. This will require us to update the parent. + + if( m_pStack->pBlkHdr->ui16NumKeys == 0) + { + if (RC_BAD( rc = saveReplaceInfo( pucKey, uiKeyLen))) + { + goto Exit; + } + + bSavedReplaceInfo = TRUE; + } + } + + // If the block is now empty, we will store a partial entry in it here. + // This scenario only occurs when we are engaged in a ReplaceByInsert + // operation. Normal inserts would never result in an empty block. + // Since we know we are part of a replace operation, we know that the + // parent of this block only needs the counts updated, not the key. + + if( m_pStack->uiLevel == 0 && m_pStack->pBlkHdr->ui16NumKeys == 0) + { + if( RC_BAD( rc = storePartialEntry( pucKey, uiKeyLen, pucValue, uiLen, + uiFlags, uiChildBlkAddr, uiCounts, ppucRemainingValue, + puiRemainingLen, FALSE))) + { + goto Exit; + } + + goto MoveToPrev; + } + + // Is there room for the new entry now in the current block? + + if( RC_BAD( rc = calcNewEntrySize( uiKeyLen, uiLen, &uiEntrySize, + &bHaveRoom, &bDefragBlk))) + { + goto Exit; + } + + if( bHaveRoom) + { + if( bDefragBlk) + { + if( RC_BAD( rc = defragmentBlock( &m_pStack->pBlock))) + { + goto Exit; + } + } + + if( RC_BAD( rc = storeEntry( pucKey, uiKeyLen, pucValue, uiLen, + uiFlags, uiOADataLen, uiChildBlkAddr, uiCounts, uiEntrySize, + &bLastEntry))) + { + goto Exit; + } + + if( bLastEntry && !bSavedReplaceInfo) + { + // Since we just added/replaced an entry to the last position of the + // current block. we will need to preserve the current stack so that + // we can finish updating the parentage later. Should only happen as + // a result of a replace operation where the new entry is larger than + // the existing one while in the upper levels. + + if( RC_BAD( rc = saveReplaceInfo( pucKey, uiKeyLen))) + { + goto Exit; + } + } + + // If we are keeping counts, we must update those too. + + if( m_bCounts && !isRootBlk( m_pStack->pBlkHdr)) + { + if( RC_BAD( rc = updateCounts())) + { + goto Exit; + } + } + + if( m_pStack->uiLevel == 0) + { + *ppucRemainingValue = NULL; + *puiRemainingLen = 0; + } + + goto MoveToPrev; + } + + // Can we store the whole thing in the new block? + + if( uiEntrySize <= pBlkHdr->stdBlkHdr.ui16BlkBytesAvail) + { + // If this block has a parent block, and the btree is maintaining counts + // we will want to update the counts on the parent block. + + if( m_bCounts && !isRootBlk( m_pStack->pBlkHdr)) + { + if (RC_BAD( rc = updateCounts())) + { + goto Exit; + } + } + + // We can release the current block since it is no longer needed. + + m_pStack->pBlock->Release(); + m_pStack->pBlock = pNewBlock; + pNewBlock = NULL; + m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)pBlkHdr; + m_pStack->ui32BlkAddr = pBlkHdr->stdBlkHdr.ui32BlkAddr; + m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); + + // Setting the uiCurOffset to the actual number of keys will cause the + // new entry to go in as the last element. + + m_pStack->uiCurOffset = m_pStack->pBlkHdr->ui16NumKeys; + + // We don't need to check to see if we need to defragment this block + // because it is "new". Anything that just got written to it will + // be contiguous already. + + if( RC_BAD( rc = storeEntry( pucKey, uiKeyLen, pucValue, uiLen, + uiFlags, uiOADataLen, uiChildBlkAddr, uiCounts, uiEntrySize, + &bLastEntry))) + { + goto Exit; + } + + flmAssert( bLastEntry); + + if( m_pStack->uiLevel == 0) + { + *ppucRemainingValue = NULL; + *puiRemainingLen = 0; + } + + bMovedToPrev = TRUE; + goto MoveToPrev; + } + + // Can we store part of the new entry into the new block? + // Calculate the minimum entry size to store. + + if( RC_BAD( rc = calcNewEntrySize( uiKeyLen, 1, &uiMinEntrySize, + &bHaveRoom, &bDefragBlk))) + { + goto Exit; + } + + // bHaveRoom refers to the current block, and we want to put this into + // the previous block. + + if( uiMinEntrySize <= pBlkHdr->stdBlkHdr.ui16BlkBytesAvail) + { + // If this block has a parent block, and the btree is maintaining counts + // we will want to update the counts on the parent block. + + if( !isRootBlk( m_pStack->pBlkHdr)) + { + if( m_bCounts) + { + if( RC_BAD( rc = updateCounts())) + { + goto Exit; + } + } + } + + // We can release the current block since it is no longer needed. + + m_pStack->pBlock->Release(); + m_pStack->pBlock = pNewBlock; + pNewBlock = NULL; + m_pStack->pBlkHdr = pBlkHdr; + m_pStack->ui32BlkAddr = pBlkHdr->stdBlkHdr.ui32BlkAddr; + m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); + + // Setting the uiCurOffset to the actual number of keys will cause the + // new entry to go in as the last element. + + m_pStack->uiCurOffset = m_pStack->pBlkHdr->ui16NumKeys; + + if( RC_BAD( rc = storePartialEntry( pucKey, uiKeyLen, pucValue, + uiLen, uiFlags, uiChildBlkAddr, uiCounts, ppucRemainingValue, + puiRemainingLen, TRUE))) + { + goto Exit; + } + + bMovedToPrev = TRUE; + } + else if( uiMinEntrySize <= m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail) + { + // We will store part of the entry in the current block + + if( RC_BAD( rc = storePartialEntry( + pucKey, uiKeyLen, pucValue, uiLen, uiFlags, uiChildBlkAddr, uiCounts, + ppucRemainingValue, puiRemainingLen, FALSE))) + { + goto Exit; + } + } + else + { + // Couldn't store anything, so try again after updating the parents. + + *ppucRemainingValue = pucValue; + *puiRemainingLen = uiLen; + } + +MoveToPrev: + + if( *pbBlockSplit) + { + // Release the current entry if it hasn't already been released. + + if( !bMovedToPrev && RC_OK( rc)) + { + + // If this block has a parent block, and the btree is maintaining counts + // we will want to update the counts on the parent block. + + if( !isRootBlk( m_pStack->pBlkHdr) && m_bCounts) + { + if( RC_BAD( rc = updateCounts())) + { + goto Exit; + } + } + + flmAssert( pNewBlock); + + m_pStack->pBlock->Release(); + m_pStack->pBlock = pNewBlock; + pNewBlock = NULL; + m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)pBlkHdr; + m_pStack->ui32BlkAddr = pBlkHdr->stdBlkHdr.ui32BlkAddr; + m_pStack->uiCurOffset = m_pStack->pBlkHdr->ui16NumKeys - 1; + m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); + } + } + +Exit: + + if( pPrevBlock) + { + pPrevBlock->Release(); + } + + if( pNewBlock) + { + pNewBlock->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Function to create a new level in the Btree. + This function will ensure that the F_BTSK stack is consistent with + the way it was configured before the function was called. + + This function will create a new block and copy the current contents + of the root block into it. It will then insert a single entry into + the root block to point to the new child. + + Note that there is a maximum of BH_MAX_LEVELS levels to the Btree. + Any effort to exceed that level will result in an error. +****************************************************************************/ +RCODE F_Btree::createNewLevel( void) +{ + RCODE rc = NE_FLM_OK; + IF_Block * pNewBlock = NULL; + FLMBYTE * pSrcBlk; + FLMBYTE * pDstBlk; + F_BTREE_BLK_HDR * pNewBlkHdr; + FLMUINT uiCounts = 0; + FLMBYTE * pucEntry; + FLMBYTE * pucNull = NULL; + FLMBYTE ucBuffer[ FLM_MAX_KEY_SIZE + BTE_NLC_KEY_START]; + FLMUINT uiMaxNLKey = FLM_MAX_KEY_SIZE + BTE_NLC_KEY_START; + FLMUINT uiEntrySize; + F_BTSK * pRootStack; + FLMUINT uiFlags; + + // Assert that we are looking at the root block! + + flmAssert( isRootBlk( m_pStack->pBlkHdr)); + + // Check the root level + + if( m_pStack->uiLevel >= BH_MAX_LEVELS - 1) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_FULL); + goto Exit; + } + + // Create a new block to copy the contents of the root block into + + if( RC_BAD( rc = m_pBlockMgr->createBlock( &pNewBlock))) + { + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + + // Log that we are about to change the root block + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &m_pStack->pBlock))) + { + goto Exit; + } + + // Update the stack since the pBlock could have changed + + m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pBlock->getBlockPtr(); + m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)m_pStack->pBlkHdr, 0); + + // Copy the data from the root block to the new block + + pSrcBlk = (FLMBYTE *)m_pStack->pui16OffsetArray; + pNewBlkHdr = (F_BTREE_BLK_HDR *)pNewBlock->getBlockPtr(); + + // Check for encryption + + if( isEncryptedBlk( (F_BLK_HDR *)m_pStack->pBlkHdr)) + { + setBlockEncrypted( (F_BLK_HDR *)pNewBlkHdr); + } + + pDstBlk = (FLMBYTE *)BtOffsetArray( (FLMBYTE *)pNewBlkHdr, 0); + + unsetRootBlk( pNewBlkHdr); + pNewBlkHdr->ui16BtreeId = m_pStack->pBlkHdr->ui16BtreeId; + pNewBlkHdr->ui16NumKeys = m_pStack->pBlkHdr->ui16NumKeys; + pNewBlkHdr->ui8BlkLevel = m_pStack->pBlkHdr->ui8BlkLevel; + pNewBlkHdr->ui16HeapSize = m_pStack->pBlkHdr->ui16HeapSize; + + pNewBlkHdr->stdBlkHdr.ui8BlkType = + ((F_BLK_HDR *)m_pStack->pBlkHdr)->ui8BlkType; + + pNewBlkHdr->stdBlkHdr.ui16BlkBytesAvail = + ((F_BLK_HDR *)m_pStack->pBlkHdr)->ui16BlkBytesAvail; + + pNewBlkHdr->stdBlkHdr.ui32PrevBlkInChain = 0; + pNewBlkHdr->stdBlkHdr.ui32NextBlkInChain = 0; + + // Copy the data from the root block to the new block. + + f_memcpy( pDstBlk, pSrcBlk, m_uiBlockSize - sizeofBTreeBlkHdr( pNewBlkHdr)); + + // Empty out the root block data. + +#ifdef FLM_DEBUG + f_memset( BtOffsetArray( (FLMBYTE *)m_pStack->pBlkHdr, 0), + 0, m_uiBlockSize - sizeofBTreeBlkHdr( m_pStack->pBlkHdr)); +#endif + + m_pStack->pBlkHdr->ui16NumKeys = 0; + m_pStack->pBlkHdr->ui16HeapSize = + ((F_BLK_HDR *)m_pStack->pBlkHdr)->ui16BlkBytesAvail = + (FLMUINT16)(m_uiBlockSize - sizeofBTreeBlkHdr( m_pStack->pBlkHdr)); + + // Check the root block type to see if we need to change it. The root + // block may have been a leaf node. + + if( (m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF) || + (m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF_DATA)) + { + // Need to set the block type to either + // BT_NON_LEAF or BT_NON_LEAF_COUNTS + + if( m_bCounts) + { + m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType = BT_NON_LEAF_COUNTS; + } + else + { + m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType = BT_NON_LEAF; + } + } + + // Now add a new entry to the stack. + + pRootStack = m_pStack; + pRootStack++; + + f_memcpy( pRootStack, m_pStack, sizeof( F_BTSK)); + + // Now fix the entries in the stack. + + pRootStack->uiLevel++; + pRootStack->pBlkHdr->ui8BlkLevel++; + pRootStack->uiCurOffset = 0; // First entry + pRootStack->pui16OffsetArray = BtOffsetArray( + (FLMBYTE *)pRootStack->pBlkHdr, 0); + + m_pStack->pBlkHdr = pNewBlkHdr; + m_pStack->ui32BlkAddr = (FLMUINT32)((F_BLK_HDR *)pNewBlkHdr)->ui32BlkAddr; + m_pStack->pBlock = pNewBlock; + pNewBlock = NULL; + m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pNewBlkHdr, 0); + + // Build a new entry for the root block that will point to the newly created + // child block. If the root block type is BT_NON_LEAF_COUNTS, then we + // need to sum the counts from the child block + + if( m_bCounts) + { + uiCounts = countKeys( (FLMBYTE *)m_pStack->pBlkHdr); + } + + // Create and insert a LEM entry to mark the last position in the block. + + uiFlags = BTE_FLAG_LAST_ELEMENT | BTE_FLAG_FIRST_ELEMENT; + + if( RC_BAD( rc = buildAndStoreEntry( + ((F_BLK_HDR *)pRootStack->pBlkHdr)->ui8BlkType, + uiFlags, pucNull, 0, pucNull, 0, 0, m_pStack->ui32BlkAddr, + uiCounts, &ucBuffer[ 0], uiMaxNLKey, &uiEntrySize))) + { + goto Exit; + } + + // Copy the entry into the root block. + + pucEntry = (FLMBYTE *)pRootStack->pBlkHdr + m_uiBlockSize - uiEntrySize; + f_memcpy( pucEntry, &ucBuffer[ 0], uiEntrySize); + bteSetEntryOffset( pRootStack->pui16OffsetArray, 0, + (FLMUINT16)(pucEntry - (FLMBYTE *)pRootStack->pBlkHdr)); + + pRootStack->pBlkHdr->ui16NumKeys++; + + pRootStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail -= + (FLMUINT16)(uiEntrySize + 2); + + pRootStack->pBlkHdr->ui16HeapSize -= + (FLMUINT16)(uiEntrySize + 2); + + m_uiStackLevels++; + m_uiRootLevel++; + +Exit: + + if( pNewBlock) + { + pNewBlock->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Method to calculate the optimal data length size to store. This + method is called when storing a partial entry, and we need to know + what the largest data size we c an store is. +****************************************************************************/ +RCODE F_Btree::calcOptimalDataLength( + FLMUINT uiKeyLen, + FLMUINT uiDataLen, + FLMUINT uiBytesAvail, + FLMUINT * puiNewDataLen) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiFixedAmounts; + FLMUINT uiRemainder; + + switch( ((F_BLK_HDR *)m_pStack->pBlkHdr)->ui8BlkType) + { + case BT_LEAF: + case BT_NON_LEAF: + case BT_NON_LEAF_COUNTS: + { + // These blocks do not have any data. + + *puiNewDataLen = 0; + break; + } + + case BT_LEAF_DATA: + { + // These amounts don't change. Note that the overhead includes the + // Overall Data Length Field, even though it may not be there in + // the end. + + uiFixedAmounts = BTE_LEAF_DATA_OVHD + + (uiKeyLen > ONE_BYTE_SIZE ? 2 : 1) + + uiKeyLen; + + uiRemainder = uiBytesAvail - uiFixedAmounts; + + if (uiRemainder >= (ONE_BYTE_SIZE + 2)) + { + *puiNewDataLen = uiRemainder - 2; + } + else + { + *puiNewDataLen = uiRemainder - 1; + } + break; + } + + default: + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + } + + if( uiDataLen < *puiNewDataLen) + { + *puiNewDataLen = uiDataLen; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: This function will count the total number of keys in the block. + Typically the value ui16NumKeys will yield this number, however, if + the block type is BT_NON_LEAF_COUNTS, we also want to include the + counts in each entry. +****************************************************************************/ +RCODE F_Btree::updateParentCounts( + IF_Block * pChildBlock, + IF_Block ** ppParentBlock, + FLMUINT uiParentElm) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiCounts; + FLMBYTE * pucCounts; + IF_Block * pParentBlock; + FLMBYTE * pBlk = pChildBlock->getBlockPtr(); + + flmAssert( getBlkType( pBlk) == BT_NON_LEAF_COUNTS); + uiCounts = countKeys( pBlk); + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( ppParentBlock))) + { + goto Exit; + } + + pParentBlock = *ppParentBlock; + pucCounts = BtEntry( pParentBlock->getBlockPtr(), uiParentElm); + pucCounts += 4; + UD2FBA( uiCounts, pucCounts); + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: This function will count the total number of keys in the block. + Typically the value ui16NumKeys will yield this number, however, if + the block type is BT_NON_LEAF_COUNTS, we also want to include the + counts in each entry. +****************************************************************************/ +FLMUINT F_Btree::countKeys( + FLMBYTE * pBlk) +{ + FLMUINT uiTotal = 0; + FLMUINT uiIndex; + FLMBYTE * pucEntry; + FLMUINT16 * puiOffsetArray; + + puiOffsetArray = BtOffsetArray( pBlk, 0); + + if( getBlkType(pBlk) != BT_NON_LEAF_COUNTS) + { + uiTotal = ((F_BTREE_BLK_HDR *)pBlk)->ui16NumKeys; + } + else + { + for (uiIndex = 0; uiIndex < + ((F_BTREE_BLK_HDR *)pBlk)->ui16NumKeys; uiIndex++) + { + pucEntry = BtEntry( pBlk, uiIndex); + uiTotal += FB2UD( &pucEntry[ BTE_NLC_COUNTS]); + } + } + + return( uiTotal); +} + +/*************************************************************************** +Desc: Function to store an entry in a Data-only block. +****************************************************************************/ +RCODE F_Btree::storeDataOnlyBlocks( + const FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMBOOL bSaveKey, + const FLMBYTE * pucData, + FLMUINT uiDataLen) +{ + RCODE rc = NE_FLM_OK; + IF_Block * pPrevBlock = NULL; + const FLMBYTE * pucLocalData = pucData; + FLMUINT uiDataToWrite = uiDataLen; + F_BLK_HDR * pBlkHdr = NULL; + FLMBYTE * pDestPtr = NULL; + FLMUINT uiAmtToCopy; + F_BLK_HDR * pPrevBlkHdr; + + if( bSaveKey) + { + if( !m_pBlock) + { + if( RC_BAD( rc = m_pBlockMgr->getBlock( m_ui32CurBlkAddr, &m_pBlock))) + { + goto Exit; + } + } + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &m_pBlock))) + { + goto Exit; + } + + pBlkHdr = ((F_BLK_HDR *)m_pBlock->getBlockPtr()); + + // Assert that the current block is empty and has no previous link. + + flmAssert( pBlkHdr->ui16BlkBytesAvail == + m_uiBlockSize - sizeofDOBlkHdr( pBlkHdr)); + flmAssert( pBlkHdr->ui32PrevBlkInChain == 0); + + pDestPtr = (FLMBYTE *)pBlkHdr + + sizeofDOBlkHdr( (F_BLK_HDR *)m_pBlock->getBlockPtr()); + + UW2FBA( (FLMUINT16)uiKeyLen, pDestPtr); + pDestPtr += sizeof( FLMUINT16); + + f_memcpy( pDestPtr, pucKey, uiKeyLen); + pDestPtr += uiKeyLen; + + m_uiDataRemaining -= (uiKeyLen + sizeof( FLMUINT16)); + pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)m_uiDataRemaining; + } + + while( uiDataToWrite > 0) + { + if( !m_pBlock) + { + if( RC_BAD( rc = m_pBlockMgr->getBlock( m_ui32CurBlkAddr, &m_pBlock))) + { + goto Exit; + } + } + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &m_pBlock))) + { + goto Exit; + } + + if( !bSaveKey) + { + pBlkHdr = ((F_BLK_HDR *)m_pBlock->getBlockPtr()); + + // Now copy as much of the remaining data as we can into the new block. + + pDestPtr = (FLMBYTE *)pBlkHdr + sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr ); + pDestPtr += (m_uiBlockSize - + sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr ) - + m_uiDataRemaining); + } + else + { + bSaveKey = FALSE; + } + + uiAmtToCopy = (uiDataToWrite <= m_uiDataRemaining + ? uiDataToWrite + : m_uiDataRemaining); + + f_memcpy( pDestPtr, pucLocalData, uiAmtToCopy); + + m_uiDataRemaining -= uiAmtToCopy; + m_uiOADataLength += uiAmtToCopy; + uiDataToWrite -= uiAmtToCopy; + pucLocalData += uiAmtToCopy; + pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)m_uiDataRemaining; + + // Now get the next block (if needed) + + if( uiDataToWrite) + { + pPrevBlock = m_pBlock; + m_pBlock = NULL; + + // Now create a new block + + if( RC_BAD( rc = m_pBlockMgr->createBlock( &m_pBlock))) + { + goto Exit; + } + + pPrevBlkHdr = ((F_BLK_HDR *)pPrevBlock->getBlockPtr()); + + pBlkHdr = ((F_BLK_HDR *)m_pBlock->getBlockPtr()); + pBlkHdr->ui8BlkType = BT_DATA_ONLY; + pBlkHdr->ui32PrevBlkInChain = pPrevBlkHdr->ui32BlkAddr; + pBlkHdr->ui32NextBlkInChain = 0; + +// if( m_pLFile->uiEncId) +// { +// ((F_ENC_DO_BLK_HDR *)pBlkHdr)->ui32EncId = (FLMUINT32)m_pLFile->uiEncId; +// setBlockEncrypted( pBlkHdr); +// } + + pBlkHdr->ui16BlkBytesAvail = + (FLMUINT16)(m_uiBlockSize - sizeofDOBlkHdr( pBlkHdr)); + + pPrevBlkHdr->ui32NextBlkInChain = pBlkHdr->ui32BlkAddr; + + m_ui32CurBlkAddr = pBlkHdr->ui32BlkAddr; + m_uiDataRemaining = m_uiBlockSize - sizeofDOBlkHdr( pBlkHdr); + + if( pPrevBlock) + { + pPrevBlock->Release(); + pPrevBlock = NULL; + } + } + } + +Exit: + + if( m_pBlock) + { + m_pBlock->Release(); + m_pBlock = NULL; + } + + if( pPrevBlock) + { + pPrevBlock->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Function to Replace data in data only blocks. +****************************************************************************/ +RCODE F_Btree::replaceDataOnlyBlocks( + const FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMBOOL bSaveKey, + const FLMBYTE * pucData, + FLMUINT uiDataLen, + FLMBOOL bLast, + FLMBOOL bTruncate) +{ + RCODE rc = NE_FLM_OK; + IF_Block * pPrevBlock = NULL; + const FLMBYTE * pucLocalData = pucData; + FLMUINT uiDataToWrite = uiDataLen; + F_BLK_HDR * pBlkHdr = NULL; + FLMBYTE * pDestPtr = NULL; + FLMUINT uiAmtToCopy; + FLMUINT32 ui32NextBlkAddr; + F_BLK_HDR * pPrevBlkHdr = NULL; + + // Do we need to store the key too? + + if( bSaveKey) + { + if( !m_pBlock) + { + if( RC_BAD( rc = m_pBlockMgr->getBlock( m_ui32CurBlkAddr, &m_pBlock))) + { + goto Exit; + } + } + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &m_pBlock))) + { + goto Exit; + } + + pBlkHdr = ((F_BLK_HDR *)m_pBlock->getBlockPtr()); + + // Assert that the current block is empty and has no previous link. + + flmAssert( pBlkHdr->ui16BlkBytesAvail == + m_uiBlockSize - sizeofDOBlkHdr( pBlkHdr)); + flmAssert( pBlkHdr->ui32PrevBlkInChain == 0); + + pDestPtr = (FLMBYTE *)pBlkHdr + + sizeofDOBlkHdr( (F_BLK_HDR *)m_pBlock->getBlockPtr()); + + UW2FBA( (FLMUINT16)uiKeyLen, pDestPtr); + pDestPtr += sizeof( FLMUINT16); + + f_memcpy( pDestPtr, pucKey, uiKeyLen); + pDestPtr += uiKeyLen; + + m_uiDataRemaining -= (uiKeyLen + sizeof( FLMUINT16)); + pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)m_uiDataRemaining; + } + + while( uiDataToWrite > 0) + { + if( !m_pBlock) + { + if( RC_BAD( rc = m_pBlockMgr->getBlock( m_ui32CurBlkAddr, &m_pBlock))) + { + goto Exit; + } + } + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &m_pBlock))) + { + goto Exit; + } + + if( !bSaveKey) + { + pBlkHdr = ((F_BLK_HDR *)m_pBlock->getBlockPtr()); + + // Now copy as much of the remaining data as we can into the new block. + + pDestPtr = (FLMBYTE *)pBlkHdr + sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr); + pDestPtr += (m_uiBlockSize - sizeofDOBlkHdr( + (F_BLK_HDR *)pBlkHdr ) - m_uiDataRemaining); + } + else + { + bSaveKey = FALSE; + } + + uiAmtToCopy = (uiDataToWrite <= m_uiDataRemaining + ? uiDataToWrite + : m_uiDataRemaining); + + f_memcpy( pDestPtr, pucLocalData, uiAmtToCopy); + + m_uiDataRemaining -= uiAmtToCopy; + m_uiOADataLength += uiAmtToCopy; + uiDataToWrite -= uiAmtToCopy; + pucLocalData += uiAmtToCopy; + + if( bTruncate || (m_uiDataRemaining < pBlkHdr->ui16BlkBytesAvail)) + { + pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)m_uiDataRemaining; + } + + // Now get the next block (if needed) + + if( uiDataToWrite) + { + pPrevBlock = m_pBlock; + m_pBlock = NULL; + pPrevBlkHdr = ((F_BLK_HDR *)pPrevBlock->getBlockPtr()); + ui32NextBlkAddr = pPrevBlkHdr->ui32NextBlkInChain; + + if( ui32NextBlkAddr) + { + if( RC_BAD( rc = m_pBlockMgr->getBlock( ui32NextBlkAddr, &m_pBlock))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &m_pBlock))) + { + goto Exit; + } + + pBlkHdr = ((F_BLK_HDR *)m_pBlock->getBlockPtr()); + } + else + { + // Now create a new block + + if( RC_BAD( rc = m_pBlockMgr->createBlock( &m_pBlock))) + { + goto Exit; + } + + pBlkHdr = ((F_BLK_HDR *)m_pBlock->getBlockPtr()); + pBlkHdr->ui8BlkType = BT_DATA_ONLY; + pBlkHdr->ui32PrevBlkInChain = pPrevBlkHdr->ui32BlkAddr; + pBlkHdr->ui32NextBlkInChain = 0; + +// if( m_pLFile->uiEncId) +// { +// setBlockEncrypted( pBlkHdr); +// ((F_ENC_DO_BLK_HDR *)pBlkHdr)->ui32EncId = (FLMUINT32)m_pLFile->uiEncId; +// } + + pBlkHdr->ui16BlkBytesAvail = + (FLMUINT16)(m_uiBlockSize - sizeofDOBlkHdr( pBlkHdr)); + } + + pPrevBlkHdr->ui32NextBlkInChain = pBlkHdr->ui32BlkAddr; + m_ui32CurBlkAddr = pBlkHdr->ui32BlkAddr; + m_uiDataRemaining = m_uiBlockSize - sizeofDOBlkHdr( pBlkHdr); + + if( pPrevBlock) + { + pPrevBlock->Release(); + pPrevBlock = NULL; + } + } + } + + // If this was the last pass to store the data, then see if we need to + // remove any left over blocks. We will not truncate the data if + // the bTruncate parameter is not set. + + if( bLast && bTruncate) + { + flmAssert( m_pBlock); + pBlkHdr = ((F_BLK_HDR *)m_pBlock->getBlockPtr()); + + ui32NextBlkAddr = pBlkHdr->ui32NextBlkInChain; + pBlkHdr->ui32NextBlkInChain = 0; + + m_pBlock->Release(); + m_pBlock = NULL; + + // If there are any blocks left over, they must be freed. + + while( ui32NextBlkAddr) + { + if( RC_BAD( rc = m_pBlockMgr->getBlock( ui32NextBlkAddr, &m_pBlock))) + { + goto Exit; + } + + pBlkHdr = ((F_BLK_HDR *)m_pBlock->getBlockPtr()); + ui32NextBlkAddr = pBlkHdr->ui32NextBlkInChain; + + if( RC_BAD( rc = m_pBlockMgr->freeBlock( &m_pBlock))) + { + goto Exit; + } + } + } + +Exit: + + if( m_pBlock) + { + m_pBlock->Release(); + m_pBlock = NULL; + } + + if( pPrevBlock) + { + pPrevBlock->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Method to construct a new leaf entry using the key and value + information passed in. +****************************************************************************/ +RCODE F_Btree::buildAndStoreEntry( + FLMUINT uiBlkType, + FLMUINT uiFlags, + const FLMBYTE * pucKey, + FLMUINT uiKeyLen, + const FLMBYTE * pucData, + FLMUINT uiDataLen, + FLMUINT uiOADataLen, // If zero, it will not be used. + FLMUINT uiChildBlkAddr, + FLMUINT uiCounts, + FLMBYTE * pucBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiEntrySize) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucTemp = pucBuffer; + + if( puiEntrySize) + { + *puiEntrySize = calcEntrySize( uiBlkType, uiFlags, + uiKeyLen, uiDataLen, uiOADataLen); + + if( !(*puiEntrySize) || *puiEntrySize > uiBufferSize) + { + rc = RC_SET_AND_ASSERT( NE_FLM_CONV_DEST_OVERFLOW); + goto Exit; + } + } + + switch( uiBlkType) + { + case BT_LEAF: + { + // No Data in this entry, so it is easy to make. + + UW2FBA( uiKeyLen, pucTemp); + pucTemp += 2; + + f_memcpy( pucTemp, pucKey, uiKeyLen); + break; + } + + case BT_LEAF_DATA: + { + // Make sure the correct flags are set... + + if( uiKeyLen > ONE_BYTE_SIZE) + { + uiFlags |= BTE_FLAG_KEY_LEN; + } + else + { + uiFlags &= ~BTE_FLAG_KEY_LEN; + } + + if( uiDataLen > ONE_BYTE_SIZE) + { + uiFlags |= BTE_FLAG_DATA_LEN; + } + else + { + uiFlags &= ~BTE_FLAG_DATA_LEN; + } + + // Only the first element of an entry that spans elements + // will hold an OADataLen field. + + if( uiOADataLen && (uiFlags & BTE_FLAG_FIRST_ELEMENT)) + { + uiFlags |= BTE_FLAG_OA_DATA_LEN; + } + else + { + uiFlags &= ~BTE_FLAG_OA_DATA_LEN; + } + + // Now start setting the elements of the entry. + // Flags first. + + *pucTemp = (FLMBYTE)uiFlags; + pucTemp++; + + // KeyLen + + if( uiFlags & BTE_FLAG_KEY_LEN) + { + UW2FBA( uiKeyLen, pucTemp); + pucTemp += 2; + } + else + { + *pucTemp = (FLMBYTE)uiKeyLen; + pucTemp++; + } + + if( uiFlags & BTE_FLAG_DATA_LEN) + { + UW2FBA( uiDataLen, pucTemp); + pucTemp += 2; + } + else + { + *pucTemp = (FLMBYTE)uiDataLen; + pucTemp++; + } + + if( uiFlags & BTE_FLAG_OA_DATA_LEN) + { + UD2FBA( uiOADataLen, pucTemp); + pucTemp += 4; + } + + // Key + + f_memcpy( pucTemp, pucKey, uiKeyLen); + pucTemp += uiKeyLen; + + // Data + + f_memcpy( pucTemp, pucData, uiDataLen); + break; + } + + case BT_NON_LEAF: + case BT_NON_LEAF_COUNTS: + { + // Child block address - 4 bytes + + pucTemp = pucBuffer; + + flmAssert( uiChildBlkAddr); + UD2FBA( uiChildBlkAddr, pucTemp); + pucTemp += 4; + + // Counts - 4 bytes + + if( uiBlkType == BT_NON_LEAF_COUNTS) + { + UD2FBA( uiCounts, pucTemp); + pucTemp += 4; + } + + // KeyLen field - 2 bytes + + UW2FBA( uiKeyLen, pucTemp); + pucTemp += 2; + + // Key - variable length (uiKeyLen) + + f_memcpy( pucTemp, pucKey, uiKeyLen); + break; + } + + default: + { + // Invalid block type + + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Method to remove an entry from a block. This method will delete the + entry pointed to by the current Stack. This method does NOT defragment + the block. If the entry points to any data only blocks, they will + also be removed from circulation if the parameter bDeleteDOBlocks is + set to true. Otherwise, they will not be freed. This is so we can + call this method when we are moving entries between blocks or + replacing entries etc. +****************************************************************************/ +RCODE F_Btree::remove( + FLMBOOL bDeleteDOBlocks) +{ + RCODE rc = NE_FLM_OK; + FLMUINT16 * pui16OffsetArray; + FLMUINT uiNumKeys; + FLMUINT uiEntrySize; + FLMUINT uiTmp; + FLMBYTE * pucEntry; + FLMBOOL bDOBlock; + IF_Block * pBlock = NULL; + FLMUINT uiBlkAddr; + FLMBYTE * pucEndOfHeap; + F_BTREE_BLK_HDR * pBlkHdr; + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &m_pStack->pBlock))) + { + goto Exit; + } + + pBlkHdr = m_pStack->pBlkHdr = + (F_BTREE_BLK_HDR *)m_pStack->pBlock->getBlockPtr(); + + m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); + + uiNumKeys = pBlkHdr->ui16NumKeys; + + if( !uiNumKeys) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + // Point to the entry... + + pucEntry = BtEntry( (FLMBYTE *)pBlkHdr, m_pStack->uiCurOffset); + uiEntrySize = getEntrySize( (FLMBYTE *)pBlkHdr, m_pStack->uiCurOffset); + + pucEndOfHeap = (FLMBYTE *)pBlkHdr + sizeofBTreeBlkHdr(pBlkHdr) + + (uiNumKeys * 2) + pBlkHdr->ui16HeapSize; + + // We are only going to have data only blocks if we are storing data + // in the btree. + + if( m_bData) + { + bDOBlock = bteDataBlockFlag( pucEntry); + + // If the data for this entry is in one or more Data Only blocks, then + // we must delete those blocks first. + + if( bDOBlock && bDeleteDOBlocks) + { + FLMBYTE ucDOBlkAddr[ 4]; + + // Get the block address of the DO Block. + + if( RC_BAD( rc = btGetEntryData( pucEntry, ucDOBlkAddr, + sizeof( FLMUINT), NULL))) + { + goto Exit; + } + + uiBlkAddr = bteGetBlkAddr( (FLMBYTE *)&ucDOBlkAddr[ 0]); + while( uiBlkAddr) + { + // We need to delete the data only blocks first. + + if( RC_BAD( rc = m_pBlockMgr->getBlock( uiBlkAddr, &pBlock))) + { + goto Exit; + } + + // Get the next block address (if any) + + uiBlkAddr = ((F_BLK_HDR *)pBlock->getBlockPtr())->ui32NextBlkInChain; + + // Now put the block into the Avail list. + + if( RC_BAD( rc = m_pBlockMgr->freeBlock( &pBlock))) + { + goto Exit; + } + } + } + } + + pui16OffsetArray = m_pStack->pui16OffsetArray; + + // Move the offsets around to effectively remove the entry. + + for( uiTmp = m_pStack->uiCurOffset; (uiTmp + 1) < uiNumKeys; uiTmp++) + { + bteSetEntryOffset( pui16OffsetArray, uiTmp, + bteGetEntryOffset( pui16OffsetArray, (uiTmp + 1))); + } + +#ifdef FLM_DEBUG + // Erase the last offset entry. + + bteSetEntryOffset( pui16OffsetArray, uiTmp, 0); +#endif + + pBlkHdr->ui16NumKeys--; + pBlkHdr->stdBlkHdr.ui16BlkBytesAvail += (FLMUINT16)uiEntrySize; + pBlkHdr->ui16HeapSize += 2; // One offset was removed. + + // Was this entry we just removed adjacent to the heap space? If + // so then we can increase the heap space. + + if( pucEndOfHeap == pucEntry) + { + pBlkHdr->ui16HeapSize += (FLMUINT16)actualEntrySize( uiEntrySize); + } + +#ifdef FLM_DEBUG + // Let's erase whatever was in the entry space. + + f_memset( pucEntry, 0, actualEntrySize(uiEntrySize)); +#endif + +Exit: + + if( pBlock) + { + pBlock->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Method to remove multiple entries from a block. The entries must be + contiguous. If any entries store data in data-only blocks, they will + be freed and put into the avail list. +****************************************************************************/ +RCODE F_Btree::removeRange( + FLMUINT uiStartElm, + FLMUINT uiEndElm, + FLMBOOL bDeleteDOBlocks) +{ + RCODE rc = NE_FLM_OK; + FLMUINT16 * pui16OffsetArray; + FLMUINT uiNumKeys; + FLMUINT uiEntrySize; + FLMBYTE * pucEntry; + FLMBOOL bDOBlock; + IF_Block * pBlock = NULL; + FLMUINT uiBlkAddr; + FLMUINT uiCurOffset; + FLMUINT uiCounter; + FLMBYTE * pucEndOfHeap; + FLMBYTE * pucStartOfHeap; + F_BTREE_BLK_HDR * pBlkHdr; + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &m_pStack->pBlock))) + { + goto Exit; + } + + pBlkHdr = m_pStack->pBlkHdr = + (F_BTREE_BLK_HDR *)m_pStack->pBlock->getBlockPtr(); + m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); + uiNumKeys = pBlkHdr->ui16NumKeys; + + if( !uiNumKeys) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + flmAssert( uiEndElm < uiNumKeys); + + // Point to the entry ... + + for( uiCurOffset = uiStartElm; uiCurOffset <= uiEndElm; uiCurOffset++) + { + pucEntry = BtEntry( (FLMBYTE *)pBlkHdr, uiCurOffset); + uiEntrySize = getEntrySize( (FLMBYTE *)pBlkHdr, uiCurOffset); + pBlkHdr->stdBlkHdr.ui16BlkBytesAvail += (FLMUINT16)uiEntrySize; + pBlkHdr->ui16NumKeys--; + + bDOBlock = bteDataBlockFlag(pucEntry); + + // If the data for this entry is in a Data Only block, then we must delete + // those blocks first. + + if( bDOBlock && bDeleteDOBlocks) + { + FLMBYTE ucDOBlkAddr[ 4]; + + // Get the block address of the DO Block. + + if( RC_BAD( rc = btGetEntryData( pucEntry, ucDOBlkAddr, 4, NULL))) + { + goto Exit; + } + + uiBlkAddr = bteGetBlkAddr( (FLMBYTE *)&ucDOBlkAddr[ 0]); + while( uiBlkAddr) + { + // We need to delete the data only blocks first. + + if( RC_BAD( rc = m_pBlockMgr->getBlock( uiBlkAddr, &pBlock))) + { + goto Exit; + } + + // Get the next block address (if any) + + uiBlkAddr = ((F_BLK_HDR *)pBlock->getBlockPtr())->ui32NextBlkInChain; + + // Now put the block into the Avail list. + + if( RC_BAD( rc = m_pBlockMgr->freeBlock( &pBlock))) + { + goto Exit; + } + } + } + + // Now erase the old entry + +#ifdef FLM_DEBUG + f_memset( pucEntry, 0, actualEntrySize(uiEntrySize)); +#endif + } + + // Move the offsets around to effectively remove the entries. + + pui16OffsetArray = m_pStack->pui16OffsetArray; + if( uiEndElm < (uiNumKeys - 1)) + { + // We will need to move the remaining offsets forward + // to delete the desired range. + + for (uiCurOffset = uiStartElm, uiCounter = 0; + uiCounter < (uiNumKeys - (uiEndElm + 1)); + uiCounter++, uiCurOffset++) + { + bteSetEntryOffset( pui16OffsetArray, uiCurOffset, + bteGetEntryOffset( pui16OffsetArray, + (uiEndElm + uiCounter + 1))); + } + } + +#ifdef FLM_DEBUG + // Erase the remaining offsets + + while (uiCurOffset < (uiNumKeys - 1)) + { + bteSetEntryOffset( pui16OffsetArray, uiCurOffset++, 0); + } + +#endif + + // We need to determine if we have gained any more heap space. We start + // by pointing to the end of the block, them moving forward until we reach + // the closest entry. + + pucEndOfHeap = (FLMBYTE *)pBlkHdr + m_uiBlockSize; + + for ( uiCurOffset = 0; uiCurOffset < pBlkHdr->ui16NumKeys; uiCurOffset++) + { + pucEntry = BtEntry( (FLMBYTE *)pBlkHdr, uiCurOffset); + + if (pucEntry < pucEndOfHeap) + { + pucEndOfHeap = pucEntry; + } + } + + // Now clean up the heap space. + + pucStartOfHeap = (FLMBYTE *)pBlkHdr + sizeofBTreeBlkHdr( pBlkHdr) + + (pBlkHdr->ui16NumKeys * 2); + + pBlkHdr->ui16HeapSize = (FLMUINT16)(pucEndOfHeap - pucStartOfHeap); + +#ifdef FLM_DEBUG + f_memset( pucStartOfHeap, 0, pBlkHdr->ui16HeapSize); +#endif + +Exit: + + if( pBlock) + { + pBlock->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Method to try to move entries (whole) from the target block to the + previous block. The entries may be moved, up to but not including + the current entry position. We do not want to change the parentage + of this block. We need to use the stack to fix up the parentage of + the previous block. Entries are moved from the lowest to highest. +****************************************************************************/ +RCODE F_Btree::moveEntriesToPrevBlk( + FLMUINT uiNewEntrySize, + IF_Block ** ppPrevBlock, + FLMBOOL * pbEntriesWereMoved) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiLocalAvailSpace; + FLMUINT uiAvailSpace; + FLMUINT uiHeapSize; + IF_Block * pPrevBlock = NULL; + FLMUINT uiPrevBlkAddr; + FLMUINT uiOAEntrySize = 0; + FLMUINT uiStart; + FLMUINT uiFinish; + FLMUINT uiCount; + FLMUINT uiOffset; + FLMBYTE * pucPrevBlk; + + // Assume nothing to move. + + *pbEntriesWereMoved = FALSE; + + // If we are already at the first entry in the block, there + // is nothing that we can move since we will always insert ahead of + // the current position. + + if( !m_pStack->uiCurOffset) + { + goto Exit; + } + + // Get the previous block. + + if( (uiPrevBlkAddr = + ((F_BLK_HDR *)m_pStack->pBlock->getBlockPtr())->ui32PrevBlkInChain) == 0) + { + goto Exit; + } + + if( RC_BAD( rc = m_pBlockMgr->getBlock( uiPrevBlkAddr, &pPrevBlock))) + { + goto Exit; + } + + uiLocalAvailSpace = m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail; + + pucPrevBlk = pPrevBlock->getBlockPtr(); + uiAvailSpace = ((F_BLK_HDR *)pucPrevBlk)->ui16BlkBytesAvail; + uiHeapSize = ((F_BTREE_BLK_HDR *)pucPrevBlk)->ui16HeapSize; + + // If we add the available space in this block and the previous block, would + // it be enough to make room for the new entry? If so, then we will + // see if we can make that room by moving ( whole) entries. + + if( (uiAvailSpace + uiLocalAvailSpace) < uiNewEntrySize) + { + goto Exit; + } + + uiStart = 0; + uiFinish = m_pStack->uiCurOffset; + + // Get the size of each entry until we are over the available size limit + + for( uiOffset = 0, uiCount = 0 ; uiOffset < uiFinish; uiOffset++) + { + FLMUINT uiLocalEntrySize; + + uiLocalEntrySize = getEntrySize( (FLMBYTE *)m_pStack->pBlkHdr, uiOffset); + + if( (uiLocalEntrySize + uiOAEntrySize) < uiAvailSpace) + { + uiOAEntrySize += uiLocalEntrySize; + uiLocalAvailSpace += uiLocalEntrySize; + uiCount++; + } + else + { + break; + } + } + + if( !uiCount) + { + goto Exit; + } + + // It looks like we can move at least one entry. + // Will this give use enough room to store the new entry? + + if( uiLocalAvailSpace < uiNewEntrySize) + { + // Moving these entries will not benefit us, so don't bother + + goto Exit; + } + + // Do we need to defragment the block first? + + if( uiHeapSize < uiOAEntrySize) + { + flmAssert( uiHeapSize != uiAvailSpace); + if( RC_BAD( rc = defragmentBlock( &pPrevBlock))) + { + goto Exit; + } + } + + // We are going to get some benefit from moving, so let's do it... + + if (RC_BAD( rc = moveToPrev( uiStart, uiStart + uiCount - 1, &pPrevBlock))) + { + goto Exit; + } + + // We will need to return this block. + + *ppPrevBlock = pPrevBlock; + pPrevBlock = NULL; + + // Adjust the current offset in the stack so we are still pointing to the + // same entry. + + m_pStack->uiCurOffset -= uiCount; + + // If this block has a parent block, and the btree is maintaining counts + // we will want to update the counts on the parent block. + + if( !isRootBlk( m_pStack->pBlkHdr)) + { + if( m_bCounts) + { + if( RC_BAD( rc = updateCounts())) + { + goto Exit; + } + } + } + + *pbEntriesWereMoved = TRUE; + +Exit: + + if (pPrevBlock) + { + pPrevBlock->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: This method will move entries beginning at uiStart, up to and + including uiFinish from the current block (m_pStack) to pPrevBlock. + As a part of this operation, both the target block and the source + block will be changed. A call to logPhysBlock will be made before + each block is changed. Never move the highest key in the block + because we don't want to have to update the parentage of the + current block... +****************************************************************************/ +RCODE F_Btree::moveToPrev( + FLMUINT uiStart, + FLMUINT uiFinish, + IF_Block ** ppPrevBlock) +{ + RCODE rc = NE_FLM_OK; + FLMUINT16 * pui16DstOffsetA = NULL; + F_BTREE_BLK_HDR * pSrcBlkHdr = NULL; + F_BTREE_BLK_HDR * pDstBlkHdr = NULL; + FLMBYTE * pucSrcEntry; + FLMBYTE * pucDstEntry; + FLMUINT uiEntrySize; + FLMUINT uiIndex; + IF_Block * pPrevBlock; + FLMBOOL bEntriesCombined = FALSE; + FLMBYTE * pucTempBlk = NULL; + void * pvPoolMark = m_pPool->poolMark(); + + // Make sure we have logged the block we are changing. + // Note that the source block will be logged in the removeRange method. + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( ppPrevBlock))) + { + goto Exit; + } + + pPrevBlock = *ppPrevBlock; + pSrcBlkHdr = m_pStack->pBlkHdr; + pDstBlkHdr = (F_BTREE_BLK_HDR *)pPrevBlock->getBlockPtr(); + pui16DstOffsetA = BtOffsetArray( (FLMBYTE *)pDstBlkHdr, 0); + pucDstEntry = getBlockEnd( pDstBlkHdr); + + if( RC_BAD( rc = m_pPool->poolAlloc( m_uiBlockSize, (void **)&pucTempBlk))) + { + goto Exit; + } + + // Beginning at the start, copy each entry over from the source + // to the destination block. + + for( uiIndex = uiStart; uiIndex <= uiFinish; uiIndex++) + { + if( RC_BAD( rc = combineEntries( pSrcBlkHdr, uiIndex, pDstBlkHdr, + (pDstBlkHdr->ui16NumKeys + ? pDstBlkHdr->ui16NumKeys - 1 + : 0), + &bEntriesCombined, &uiEntrySize, + pucTempBlk))) + { + goto Exit; + } + + if( bEntriesCombined) + { + F_BTSK tmpStack; + F_BTSK * pTmpStack; + + tmpStack.pBlock = pPrevBlock; + tmpStack.pBlkHdr = pDstBlkHdr; + tmpStack.uiCurOffset = pDstBlkHdr->ui16NumKeys - 1; // Last entry + + pTmpStack = m_pStack; + m_pStack = &tmpStack; + + rc = remove( FALSE); + m_pStack = pTmpStack; + + if( RC_BAD( rc)) + { + goto Exit; + } + + if( pDstBlkHdr->ui16HeapSize != + pDstBlkHdr->stdBlkHdr.ui16BlkBytesAvail) + { + if( RC_BAD( rc = defragmentBlock( &pPrevBlock))) + { + goto Exit; + } + } + + pucDstEntry = getBlockEnd( pDstBlkHdr) - uiEntrySize; + f_memcpy( pucDstEntry, pucTempBlk, uiEntrySize); + + bteSetEntryOffset( pui16DstOffsetA, + pDstBlkHdr->ui16NumKeys++, + (FLMUINT16)(pucDstEntry - (FLMBYTE *)pDstBlkHdr)); + + pDstBlkHdr->stdBlkHdr.ui16BlkBytesAvail -= + ((FLMUINT16)uiEntrySize + 2); + + pDstBlkHdr->ui16HeapSize -= ((FLMUINT16)uiEntrySize + 2); + bEntriesCombined = FALSE; + } + else + { + pucSrcEntry = BtEntry( (FLMBYTE *)pSrcBlkHdr, uiIndex); + uiEntrySize = getEntrySize( (FLMBYTE *)pSrcBlkHdr, uiIndex); + pucDstEntry -= actualEntrySize(uiEntrySize); + + f_memcpy( pucDstEntry, pucSrcEntry, actualEntrySize(uiEntrySize)); + + bteSetEntryOffset( pui16DstOffsetA, + pDstBlkHdr->ui16NumKeys++, + (FLMUINT16)(pucDstEntry - (FLMBYTE *)pDstBlkHdr)); + + pDstBlkHdr->stdBlkHdr.ui16BlkBytesAvail -= (FLMUINT16)uiEntrySize; + pDstBlkHdr->ui16HeapSize -= (FLMUINT16)uiEntrySize; + } + } + + // Now remove the entries from the Src block. + + if( RC_BAD( rc = removeRange( uiStart, uiFinish, FALSE))) + { + goto Exit; + } + +Exit: + + m_pPool->poolReset( pvPoolMark); + return( rc); +} + +/*************************************************************************** +Desc: Method to try to move entries (whole) from the target block to the + next block. The entries may be moved up to but not including + the current entry position depending on how much room is available if + any. Entries are moved from the highest to lowest. +****************************************************************************/ +RCODE F_Btree::moveEntriesToNextBlk( + FLMUINT uiNewEntrySize, + FLMBOOL * pbEntriesWereMoved) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiLocalAvailSpace; + FLMUINT uiAvailSpace; + FLMUINT uiHeapSize; + IF_Block * pNextBlock = NULL; + FLMUINT uiNextBlkAddr; + FLMUINT uiOAEntrySize = 0; + FLMUINT uiStart; + FLMUINT uiFinish; + FLMUINT uiCount; + FLMUINT uiOffset; + IF_Block * pChildBlock = NULL; + IF_Block * pParentBlock = NULL; + F_BTSK * pParentStack; + FLMUINT uiLevel; + FLMBOOL bReleaseChild = FALSE; + FLMBOOL bReleaseParent = FALSE; + FLMBOOL bCommonParent = FALSE; + + // Assume nothing to move. + + *pbEntriesWereMoved = FALSE; + + // Get the next block. + + if( (uiNextBlkAddr = + ((F_BLK_HDR *)m_pStack->pBlock->getBlockPtr())->ui32NextBlkInChain) == 0) + { + goto Exit; + } + + if( (FLMUINT16)m_pStack->uiCurOffset >= (m_pStack->pBlkHdr->ui16NumKeys - 1)) + { + goto Exit; + } + + if( RC_BAD( rc = m_pBlockMgr->getBlock( uiNextBlkAddr, &pNextBlock))) + { + goto Exit; + } + + // Our first task is to determine if we can move anything at all. + // How much free space is there in the next block? + + uiLocalAvailSpace = m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail; + uiAvailSpace = ((F_BLK_HDR *)pNextBlock->getBlockPtr())->ui16BlkBytesAvail; + uiHeapSize = ((F_BTREE_BLK_HDR *)pNextBlock->getBlockPtr())->ui16HeapSize; + + // If we add the available space in this block and the next block, would + // it be enough to make room for the new entry? If so, then we will + // see if we can make that room by moving ( whole) entries. + + if( (uiAvailSpace + uiLocalAvailSpace) < uiNewEntrySize) + { + goto Exit; + } + + // Begin at the last entry and work backward. + + uiStart = m_pStack->pBlkHdr->ui16NumKeys - 1; + uiFinish = m_pStack->uiCurOffset; + + // Get the size of each entry (plus 2 for the offset entry) until we are + // over the available size limit. + + for( uiOffset = uiStart, uiCount = 0 ; uiOffset > uiFinish; uiOffset--) + { + FLMUINT uiLocalEntrySize; + + uiLocalEntrySize = getEntrySize( (FLMBYTE *)m_pStack->pBlkHdr, + uiOffset); + + if( (uiLocalEntrySize + uiOAEntrySize) < uiAvailSpace) + { + uiOAEntrySize += uiLocalEntrySize; + uiLocalAvailSpace += uiLocalEntrySize; + uiCount++; + } + else + { + break; + } + } + + if( uiCount == 0) + { + goto Exit; + } + + // It looks like we can move at least one entry. + // Will this give use enough room to store the new entry? + + if( uiLocalAvailSpace < uiNewEntrySize) + { + goto Exit; + } + + flmAssert( uiStart > uiFinish); + + // Do we need to defragment the block first before we do the move? + + if( uiHeapSize < uiOAEntrySize) + { + flmAssert( uiHeapSize != uiAvailSpace); + if( RC_BAD( rc = defragmentBlock( &pNextBlock))) + { + goto Exit; + } + } + + // We are going to get some benefit from moving, so let's do it... + + if( RC_BAD( rc = moveToNext( uiStart, uiStart - uiCount + 1, + &pNextBlock))) + { + goto Exit; + } + + // If this block has a parent block, and the btree is maintaining counts + // we will need to update the counts on the parent blocks. + + if( m_bCounts) + { + for( uiLevel = m_pStack->uiLevel; + uiLevel < m_uiStackLevels - 1; + uiLevel++) + { + pParentStack = &m_Stack[ uiLevel + 1]; + + // If we are at "current" level, then we want to use the pNextBlock + // block as the child. Otherwise, we want to use the previous parent + // block as the child. + + if( uiLevel == m_pStack->uiLevel) + { + pChildBlock = pNextBlock; + bReleaseChild = TRUE; + pNextBlock = NULL; + } + else + { + pChildBlock = pParentBlock; + bReleaseChild = bReleaseParent; + bReleaseParent = FALSE; + } + + // Check to see if the parent entry is the last entry in the + // block. If it is, then we will need to get the next block. + // If the parent block is the same for both blocks, then we + // only need to reference the next entry. We don't want to release + // the parent as it is referenced in the stack. + + if( bCommonParent || + (pParentStack->uiCurOffset < + (FLMUINT)(pParentStack->pBlkHdr->ui16NumKeys - 1))) + { + pParentBlock = pParentStack->pBlock; + bReleaseParent = FALSE; + + if (RC_BAD( rc = updateParentCounts( pChildBlock, &pParentBlock, + (bCommonParent + ? pParentStack->uiCurOffset + : pParentStack->uiCurOffset + 1)))) + { + goto Exit; + } + + // The parent has changed, so update the stack. + + pParentStack->pBlkHdr = + (F_BTREE_BLK_HDR *)pParentBlock->getBlockPtr(); + + pParentStack->pBlock = pParentBlock; + bCommonParent = TRUE; + } + else + { + // We need to get the next block at the parent level first. We + // release the previous parent if there was one. + + uiNextBlkAddr = pParentStack->pBlkHdr->stdBlkHdr.ui32NextBlkInChain; + + flmAssert( uiNextBlkAddr); + + if( RC_BAD( rc = m_pBlockMgr->getBlock( + uiNextBlkAddr, &pParentBlock))) + { + goto Exit; + } + + bReleaseParent = TRUE; + + if( RC_BAD( rc = updateParentCounts( pChildBlock, + &pParentBlock, 0))) + { + goto Exit; + } + } + + if( bReleaseChild) + { + pChildBlock->Release(); + pChildBlock = NULL; + bReleaseChild = FALSE; + } + } + } + + *pbEntriesWereMoved = TRUE; + +Exit: + + if( pChildBlock && bReleaseChild) + { + pChildBlock->Release(); + } + + if( pParentBlock && bReleaseParent) + { + pParentBlock->Release(); + } + + if( pNextBlock) + { + pNextBlock->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: This method will move entries beginning at uiStart, down to and + including uiFinish from the current block (m_pStack) to pNextBlock. + As a part of this operation, both the target block and the source + block will be changed. +****************************************************************************/ +RCODE F_Btree::moveToNext( + FLMUINT uiStart, + FLMUINT uiFinish, + IF_Block ** ppNextBlock) +{ + RCODE rc = NE_FLM_OK; + FLMUINT16 * pui16DstOffsetA = NULL; + F_BTREE_BLK_HDR * pSrcBlkHdr = NULL; + F_BTREE_BLK_HDR * pDstBlkHdr = NULL; + FLMBYTE * pucSrcEntry; + FLMBYTE * pucDstEntry; + FLMUINT uiEntrySize; + FLMINT iIndex; + FLMUINT uiBytesToCopy; + FLMUINT uiNumKeysToAdd; + IF_Block * pNextBlock = *ppNextBlock; + FLMBOOL bEntriesCombined; + FLMBYTE * pucOffsetArray; + FLMBYTE * pucBuffer = NULL; + FLMBYTE * pucTmpBlk = NULL; + FLMUINT uiBufferSize = 0; + void * pvPoolMark = m_pPool->poolMark(); + + uiBufferSize = m_uiBlockSize * 2; + + if( RC_BAD( rc = m_pPool->poolAlloc( uiBufferSize, (void **)&pucBuffer))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pPool->poolAlloc( m_uiBlockSize, (void **)&pucTmpBlk))) + { + goto Exit; + } + + // Make sure we have logged the block we are changing. + // Note that the source block will be logged in the removeRange method. + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &pNextBlock))) + { + goto Exit; + } + + // Block may have changed. Need to pass it back. + + *ppNextBlock = pNextBlock; + + pSrcBlkHdr = m_pStack->pBlkHdr; + pDstBlkHdr = (F_BTREE_BLK_HDR *)pNextBlock->getBlockPtr(); + + // We will need to save off the current offset array. We will do this + // by copying it into our temporary block. + + uiBytesToCopy = pDstBlkHdr->ui16NumKeys * 2; + if( uiBytesToCopy > uiBufferSize) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + pui16DstOffsetA = BtOffsetArray((FLMBYTE *)pDstBlkHdr, 0); + pucOffsetArray = &pucBuffer[ uiBufferSize] - uiBytesToCopy; + + f_memcpy( pucOffsetArray, (FLMBYTE *)pui16DstOffsetA, uiBytesToCopy); + + // Point to the last entry in the block. + + pucDstEntry = getBlockEnd( pDstBlkHdr); + + // Beginning at the start, copy each entry over from the Src to the Dst + // block. Note that the uiStart parameter represents a higher position + // in the block. In otherwords, we are actually copying from the end or + // highest position to a lower position in the block. Therefore we want + // to make sure the offset array is copied in the same way, otherwise it + // would reverse the order of the entries. + + uiNumKeysToAdd = uiStart - uiFinish + 1; + pui16DstOffsetA = (FLMUINT16 *)pucOffsetArray; + + for( iIndex = uiStart; iIndex >= (FLMINT)uiFinish; iIndex--) + { + if( RC_BAD( rc = combineEntries( pSrcBlkHdr, iIndex, pDstBlkHdr, + 0, &bEntriesCombined, &uiEntrySize, pucTmpBlk))) + { + goto Exit; + } + + if( bEntriesCombined) + { + F_BTSK tmpStack; + F_BTSK * pTmpStack; + + tmpStack.pBlock = pNextBlock; + tmpStack.pBlkHdr = pDstBlkHdr; + tmpStack.uiCurOffset = 0; // 1st entry. + + pTmpStack = m_pStack; + m_pStack = &tmpStack; + + rc = remove( FALSE); + m_pStack = pTmpStack; + + if (RC_BAD( rc)) + { + goto Exit; + } + + if( pDstBlkHdr->ui16HeapSize != + pDstBlkHdr->stdBlkHdr.ui16BlkBytesAvail) + { + if( RC_BAD( rc = defragmentBlock( &pNextBlock))) + { + goto Exit; + } + + // Refresh the saved offset array. + + uiBytesToCopy -= 2; + pucOffsetArray = &pucBuffer[ uiBufferSize] - uiBytesToCopy; + + f_memcpy( pucOffsetArray, + (FLMBYTE *)BtOffsetArray( (FLMBYTE *)pDstBlkHdr, 0), + uiBytesToCopy); + } + + pucDstEntry = getBlockEnd( pDstBlkHdr) - uiEntrySize; + f_memcpy( pucDstEntry, pucTmpBlk, uiEntrySize); + + bteSetEntryOffset( pui16DstOffsetA, 0, + pucDstEntry - (FLMBYTE *)pDstBlkHdr); + + pDstBlkHdr->ui16NumKeys++; + + pDstBlkHdr->stdBlkHdr.ui16BlkBytesAvail -= + ((FLMUINT16)uiEntrySize + 2); + + pDstBlkHdr->ui16HeapSize -= ((FLMUINT16)uiEntrySize + 2); + + bEntriesCombined = FALSE; + } + else + { + pucSrcEntry = BtEntry( (FLMBYTE *)pSrcBlkHdr, iIndex); + uiEntrySize = getEntrySize( (FLMBYTE *)pSrcBlkHdr, iIndex); + + pucDstEntry -= actualEntrySize(uiEntrySize); + + f_memcpy( pucDstEntry, pucSrcEntry, + actualEntrySize(uiEntrySize)); + + pui16DstOffsetA--; + + bteSetEntryOffset( pui16DstOffsetA, 0, + pucDstEntry - (FLMBYTE *)pDstBlkHdr); + + pDstBlkHdr->ui16NumKeys++; + pDstBlkHdr->stdBlkHdr.ui16BlkBytesAvail -= (FLMUINT16)uiEntrySize; + pDstBlkHdr->ui16HeapSize -= (FLMUINT16)uiEntrySize; + } + } + + // Now put the new offset array into the block. + + f_memcpy( BtOffsetArray( (FLMBYTE *)pDstBlkHdr, 0), + pui16DstOffsetA, + &pucBuffer[ uiBufferSize] - (FLMBYTE *)pui16DstOffsetA); + + // Now remove the entries from the Src block. + + if( RC_BAD( rc = removeRange( uiFinish, uiStart, FALSE))) + { + goto Exit; + } + +Exit: + + m_pPool->poolReset( pvPoolMark); + return( rc); +} + +/*************************************************************************** +Desc: Method to advance to the next entry. If there are no more entries + in the block, it will release the current block and get the next in + the chain. If there are no more entries, i.e. no more blocks in + the chain, NE_FLM_EOF_HIT will be returned. +****************************************************************************/ +RCODE F_Btree::advanceToNextElement( + FLMBOOL bAdvanceStack) +{ + RCODE rc = NE_FLM_OK; + F_BTREE_BLK_HDR * pBlkHdr; + + flmAssert( m_pBlock); + + pBlkHdr = (F_BTREE_BLK_HDR *)m_pBlock->getBlockPtr(); + + if( m_uiCurOffset + 1 >= pBlkHdr->ui16NumKeys) + { + // We are out of entries in this block, so we will release it + // and get the next block in the chain (if any). + + if( RC_BAD( rc = getNextBlock( &m_pBlock))) + { + goto Exit; + } + + m_ui32PrimaryBlkAddr = ((F_BLK_HDR *)m_pBlock->getBlockPtr())->ui32BlkAddr; + m_uiPrimaryOffset = 0; + m_ui32CurBlkAddr = m_ui32PrimaryBlkAddr; + m_uiCurOffset = 0; + + if( bAdvanceStack) + { + if( RC_BAD( rc = moveStackToNext( m_pBlock))) + { + goto Exit; + } + + m_pBlock->AddRef(); + } + } + else + { + m_uiPrimaryOffset++; + m_uiCurOffset++; + m_pStack->uiCurOffset++; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Method to backup the stack to the previous entry. If there are no + more entries in the block, it will release the current block and get + the previous in the chain. If there are no more entries, i.e. no + more blocks in the chain, NE_FLM_BOF_HIT will be returned. +****************************************************************************/ +RCODE F_Btree::backupToPrevElement( + FLMBOOL bBackupStack) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucBlk; + + flmAssert( m_pBlock); + + pucBlk = m_pBlock->getBlockPtr(); + + if( !m_uiCurOffset) + { + // We are out of entries in this block, so we will release it + // and get the previous block in the chain (if any). + + if( RC_BAD( rc = getPrevBlock( &m_pBlock))) + { + goto Exit; + } + + m_ui32PrimaryBlkAddr = ((F_BLK_HDR *)pucBlk)->ui32BlkAddr; + + m_uiPrimaryOffset = ((F_BTREE_BLK_HDR *)pucBlk)->ui16NumKeys - 1; + + m_ui32CurBlkAddr = m_ui32PrimaryBlkAddr; + m_uiCurOffset = m_uiPrimaryOffset; + + if( bBackupStack) + { + if( RC_BAD( rc = moveStackToPrev( m_pBlock))) + { + goto Exit; + } + + m_pBlock->AddRef(); + } + } + else + { + m_uiPrimaryOffset--; + m_uiCurOffset--; + m_pStack->uiCurOffset--; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Method to extract the key length from a given entry. The optional + pucKeyRV is a buffer where we can return the address of the start of + the actual key. +****************************************************************************/ +FLMUINT F_Btree::getEntryKeyLength( + FLMBYTE * pucEntry, + FLMUINT uiBlockType, + const FLMBYTE ** ppucKeyRV) +{ + FLMUINT uiKeyLength; + FLMBYTE * pucTmp; + + // The way we get the key length depends on the type of block we have. + + switch( uiBlockType) + { + case BT_LEAF_DATA: + { + pucTmp = &pucEntry[ 1]; // skip past the flags + + if( bteKeyLenFlag( pucEntry)) + { + uiKeyLength = FB2UW( pucTmp); + pucTmp += 2; + } + else + { + uiKeyLength = *pucTmp; + pucTmp += 1; + } + + if( bteDataLenFlag(pucEntry)) + { + pucTmp += 2; + } + else + { + pucTmp += 1; + } + + // Check for the presence of the OverallDataLength field (4 bytes). + + if( bteOADataLenFlag( pucEntry)) + { + pucTmp += 4; + } + + break; + } + + case BT_LEAF: + { + uiKeyLength = FB2UW( pucEntry); + + if( ppucKeyRV) + { + pucTmp = &pucEntry[ BTE_KEY_START]; + } + + break; + } + + case BT_NON_LEAF: + { + uiKeyLength = FB2UW( &pucEntry[ BTE_NL_KEY_LEN]); + + if( ppucKeyRV) + { + pucTmp = &pucEntry[ BTE_NL_KEY_START]; + } + + break; + } + + case BT_NON_LEAF_COUNTS: + { + uiKeyLength = FB2UW( &pucEntry[ BTE_NLC_KEY_LEN]); + + if( ppucKeyRV) + { + pucTmp = &pucEntry[ BTE_NLC_KEY_START]; + } + + break; + } + + default: + { + flmAssert( 0); + uiKeyLength = 0; + pucTmp = NULL; + break; + } + } + + // Do we need to return the key pointer? + + if( ppucKeyRV) + { + *ppucKeyRV = pucTmp; + } + + return( uiKeyLength); +} + +/*************************************************************************** +Desc: Method to extract the data length from a given entry. The parameter + pucDataRV is an optional return value that will hold the address + of the beginning of the data in the entry. This method + ** assumes ** the entry is from a BT_LEAF_DATA block. No other block + type has any data. +****************************************************************************/ +FSTATIC FLMUINT btGetEntryDataLength( + FLMBYTE * pucEntry, + const FLMBYTE ** ppucDataRV, // Optional + FLMUINT * puiOADataLengthRV, // Optional + FLMBOOL * pbDOBlockRV) // Optional +{ + const FLMBYTE * pucTmp; + FLMUINT uiDataLength; + FLMUINT uiKeyLength; + + pucTmp = &pucEntry[ 1]; // skip past the flags + + if( bteKeyLenFlag( pucEntry)) + { + uiKeyLength = FB2UW( pucTmp); + pucTmp += 2; + } + else + { + uiKeyLength = *pucTmp; + pucTmp += 1; + } + + if( bteDataLenFlag(pucEntry)) + { + uiDataLength = FB2UW( pucTmp); + pucTmp += 2; + } + else + { + uiDataLength = *pucTmp; + pucTmp += 1; + } + + // Check for the presence of the OverallDataLength field (4 bytes). + + if( bteOADataLenFlag(pucEntry)) + { + if( puiOADataLengthRV) + { + *puiOADataLengthRV = FB2UD( pucTmp); + } + pucTmp += 4; + } + else if (puiOADataLengthRV) + { + *puiOADataLengthRV = uiDataLength; + } + + // Are we to return a pointer to the data? + + if( ppucDataRV) + { + // Advance to the Data since we are currently pointing to the Key. + + *ppucDataRV = (FLMBYTE *)(pucTmp + uiKeyLength); + } + + if( pbDOBlockRV) + { + *pbDOBlockRV = bteDataBlockFlag( pucEntry); + } + + return( uiDataLength); +} + +/*************************************************************************** +Desc: Method to extract the data value from a given block. This method + expects to receive a buffer to copy the data into. This method does + not read data across blocks. The puiLenDataRV is an optional + parameter that will hold the actual data size returned. +****************************************************************************/ +FSTATIC RCODE btGetEntryData( + FLMBYTE * pucEntry, // Pointer to the entry containing the data + FLMBYTE * pucBufferRV, + FLMUINT uiBufferSize, + FLMUINT * puiLenDataRV) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiDataLength; + const FLMBYTE * pucData; + + // Get the data length + + uiDataLength = btGetEntryDataLength( pucEntry, &pucData, NULL, NULL); + + if( uiDataLength > uiBufferSize) + { + rc = RC_SET_AND_ASSERT( NE_FLM_CONV_DEST_OVERFLOW); + goto Exit; + } + +#ifdef FLM_DEBUG + f_memset( pucBufferRV, 0, uiBufferSize); +#endif + f_memcpy( pucBufferRV, pucData, uiDataLength); + + // Do we need to return the data length? + + if( puiLenDataRV) + { + *puiLenDataRV = uiDataLength; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: This method will return the overall size of the entry at uiOffset in + pBlk. The size returned includes a two byte allowance for the offset + entry used by this entry. +****************************************************************************/ +FLMUINT F_Btree::getEntrySize( + FLMBYTE * pBlk, + FLMUINT uiOffset, + FLMBYTE ** ppucEntry) +{ + FLMBYTE * pucEntry; + FLMUINT uiEntrySize; + + // Point to the entry ... + + pucEntry = BtEntry( pBlk, uiOffset); + + if( ppucEntry) + { + *ppucEntry = pucEntry; + } + + // Different block types have different entry formats. + + switch( getBlkType( pBlk)) + { + case BT_LEAF: + { + uiEntrySize = 4 + FB2UW( pucEntry); + break; + } + case BT_LEAF_DATA: + { + FLMBYTE * pucTmp = &pucEntry[ 1]; + + // Stuff we know + + uiEntrySize = 3; + + // Get the key length + + if( *pucEntry & BTE_FLAG_KEY_LEN) + { + uiEntrySize += FB2UW( pucTmp) + 2; + pucTmp += 2; + } + else + { + uiEntrySize += (*pucTmp + 1); + pucTmp++; + } + + // Get the data length + + if( *pucEntry & BTE_FLAG_DATA_LEN) + { + // 2 byte data length field + + uiEntrySize += (FB2UW( pucTmp) + 2); + } + else + { + // 1 byte data length field + + uiEntrySize += (FLMUINT)*pucTmp + 1; + } + + // Get the Overall Data length (if present) + + if( *pucEntry & BTE_FLAG_OA_DATA_LEN) + { + uiEntrySize += 4; + } + + break; + } + + case BT_NON_LEAF: + { + uiEntrySize = 8 + FB2UW( &pucEntry[ BTE_NL_KEY_LEN]); + break; + } + + case BT_NON_LEAF_COUNTS: + { + uiEntrySize = 12 + FB2UW( &pucEntry[ BTE_NLC_KEY_LEN]); + break; + } + + default: + { + flmAssert( 0); + uiEntrySize = 0; + break; + } + } + + return( uiEntrySize); +} + +/*************************************************************************** +Desc: Method to search the BTree for a specific entry. Upon a successful + return from this method, the local stack will be setup and pointing + to either the desired entry, or if the entry does not exist, it will + be pointing to the entry that would be immediately after the desired + entry. This method therefore can be used both for reads and updates + where we want to insert a new entry into the BTree. +****************************************************************************/ +RCODE F_Btree::findEntry( + const FLMBYTE * pucKey, // In + FLMUINT uiKeyLen, // In + FLMUINT uiMatch, // In + FLMUINT * puiPosition, // Out + FLMUINT32 * pui32BlkAddr, // In/Out + FLMUINT * puiOffsetIndex) // In/Out +{ + RCODE rc = NE_FLM_OK; + F_BTSK * pStack = NULL; + FLMUINT32 ui32BlkAddress; + IF_Block * pBlock = NULL; + FLMBYTE * pucEntry; + FLMUINT uiPrevCounts = 0; + FLMUINT uiLevel; + + // Make sure the stack is clean before we start. + + btRelease(); + + // No input key is needed to get the first or last key. + + if( uiMatch == FLM_FIRST || uiMatch == FLM_LAST) + { + uiKeyLen = 0; + } + + if( uiKeyLen > FLM_MAX_KEY_SIZE) + { + rc = RC_SET( NE_FLM_BTREE_KEY_SIZE); + goto Exit; + } + + // Have we been passed a block address to look in? + + if( pui32BlkAddr && *pui32BlkAddr) + { + if( RC_OK( rc = findInBlock( pucKey, uiKeyLen, uiMatch, puiPosition, + pui32BlkAddr, puiOffsetIndex))) + { + goto Exit; + } + } + + // Beginning at the root node, we will scan until we find the first key + // that is greater than or equal to our target key. If we don't find any + // key that is larger than our target key, we will use the last block found. + + ui32BlkAddress = (FLMUINT32)m_uiRootBlkAddr; + + for( ;;) + { + // Get the block - Note that this will place a use on the block. + // It must be properly released when done. + + if( RC_BAD( rc = m_pBlockMgr->getBlock( ui32BlkAddress, &pBlock))) + { + goto Exit; + } + + // We are building the stack inverted to make traversing it a bit easier. + + uiLevel = ((F_BTREE_BLK_HDR *)pBlock->getBlockPtr())->ui8BlkLevel; + pStack = &m_Stack[ uiLevel]; + + m_uiStackLevels++; + + pStack->pBlkHdr = (F_BTREE_BLK_HDR *)pBlock->getBlockPtr(); + pStack->ui32BlkAddr = ui32BlkAddress; + pStack->pBlock = pBlock; + pBlock = NULL; + pStack->uiLevel = uiLevel; + pStack->uiKeyLen = uiKeyLen; + pStack->pucKeyBuf = pucKey; + pStack->uiKeyBufSize = m_Stack[0].uiKeyBufSize; + pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pStack->pBlkHdr, 0); + + if( isRootBlk( pStack->pBlkHdr)) + { + m_uiRootLevel = uiLevel; + } + + // Search the block for the key. When we return from this method + // the pStack will be pointing to the last entry we looked at. + + if( RC_BAD( rc = scanBlock( pStack, uiMatch))) + { + // It is okay if we couldn't find the key. Especially if + // we are still in the upper levels of the B-tree. + + if( (rc != NE_FLM_NOT_FOUND) && (rc != NE_FLM_EOF_HIT)) + { + goto Exit; + } + } + + // Are we at the end of our search? + + if( (pStack->pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF_DATA) || + (pStack->pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF) || + (m_uiStackLevels - 1 >= m_uiSearchLevel)) + { + if( m_bCounts && puiPosition) + { + flmAssert( m_uiSearchLevel >= BH_MAX_LEVELS); + *puiPosition = uiPrevCounts + pStack->uiCurOffset; + } + + // If this is a search for the last entry, then we should adjust the + // uiCurOffset so that it points to a valid entry. + + if( uiMatch == FLM_LAST) + { + m_pStack = pStack; + + for (;;) + { + if( RC_BAD( rc = moveStackToPrev( NULL))) + { + goto Exit; + } + + // If we are on the leaf level, we need to make sure we are + // looking at a first occurrence of an entry. + + if( pStack->pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF_DATA) + { + pucEntry = BtEntry( + (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); + + if( bteFirstElementFlag( pucEntry)) + { + break; + } + } + else + { + break; + } + } + } + + break; + } + else + { + if( m_bCounts && puiPosition) + { + uiPrevCounts += countRangeOfKeys( pStack, 0, pStack->uiCurOffset); + } + + // Get the Child Block Address + + pucEntry = BtEntry( + pStack->pBlock->getBlockPtr(), pStack->uiCurOffset); + + ui32BlkAddress = bteGetBlkAddr( pucEntry); + } + } + + // Return the block and offset if needed. + + if( pui32BlkAddr) + { + *pui32BlkAddr = pStack->ui32BlkAddr; + } + + if( puiOffsetIndex) + { + *puiOffsetIndex = pStack->uiCurOffset; + } + + m_bStackSetup = TRUE; + +Exit: + + if( RC_OK( rc) || (rc == NE_FLM_NOT_FOUND) || (rc == NE_FLM_EOF_HIT)) + { + if( pStack) + { + m_pStack = pStack; + } + } + + if( pBlock) + { + pBlock->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Private method to search for a particular key in a pre-designted + block offset. If we don't find it at the given offset, we will do a + binary search for it. Note that a uiMatch of FLM_FIRST & FLM_LAST + will be ignored if we locate the entry by the puiOffsetIndex parameter. + Also, this method does not setup the full stack. Only the level where + the block address passed in resides. +****************************************************************************/ +RCODE F_Btree::findInBlock( + const FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMUINT uiMatch, + FLMUINT * puiPosition, + FLMUINT32 * pui32BlkAddr, + FLMUINT * puiOffsetIndex) +{ + RCODE rc = NE_FLM_OK; + F_BTSK * pStack; + IF_Block * pBlock = NULL; + FLMBYTE * pucEntry; + const FLMBYTE * pucBlkKey; + FLMUINT uiBlkKeyLen; + F_BLK_HDR * pBlkHdr; + + // Get the block - Note that this will place a use on the block. + // It must be properly released when done. + + if( RC_BAD( rc = m_pBlockMgr->getBlock( *pui32BlkAddr, &pBlock))) + { + goto Exit; + } + + pBlkHdr = ((F_BLK_HDR *)pBlock->getBlockPtr()); + + if( !blkIsBTree( pBlkHdr)) + { + rc = RC_SET( NE_FLM_NOT_FOUND); + goto Exit; + } + + // If the block is not a leaf block, the caller will + // need to do a full search down the B-Tree + + if( ((F_BTREE_BLK_HDR *)pBlkHdr)->ui8BlkLevel != 0) + { + rc = RC_SET( NE_FLM_NOT_FOUND); + goto Exit; + } + + pStack = &m_Stack[ 0]; + m_uiStackLevels++; + + pStack->pBlkHdr = (F_BTREE_BLK_HDR *)pBlkHdr; + pStack->ui32BlkAddr = *pui32BlkAddr; + pStack->pBlock = pBlock; + pBlock = NULL; + pStack->uiLevel = 0; + pStack->uiKeyLen = uiKeyLen; + pStack->pucKeyBuf = pucKey; + pStack->uiKeyBufSize = m_Stack[0].uiKeyBufSize; + pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pStack->pBlkHdr, 0); + pStack->uiCurOffset = puiOffsetIndex ? *puiOffsetIndex : 0; + + if( isRootBlk( pStack->pBlkHdr)) + { + m_uiRootLevel = 0; + } + + // See if the entry we are looking for is at the passed offset + + if( puiOffsetIndex) + { + if( *puiOffsetIndex < pStack->pBlkHdr->ui16NumKeys) + { + pucEntry = BtEntry( (FLMBYTE *)pStack->pBlkHdr, *puiOffsetIndex); + + uiBlkKeyLen = getEntryKeyLength( pucEntry, + getBlkType( (FLMBYTE *)pStack->pBlkHdr), &pucBlkKey); + + if( uiKeyLen == uiBlkKeyLen) + { + if( f_memcmp( pucKey, pucBlkKey, uiKeyLen) == 0) + { + goto GotEntry; + } + } + } + } + + // Search the block for the key. When we return from this method + // the pStack will be pointing to the last entry we looked at. + + if( RC_BAD( rc = scanBlock( pStack, uiMatch))) + { + goto Exit; + } + +GotEntry: + + if( m_bCounts && puiPosition) + { + flmAssert( m_uiSearchLevel >= BH_MAX_LEVELS); + *puiPosition = pStack->uiCurOffset; + } + + // Verify that we are looking at an entry with the firstElement flag set. + + m_pStack = pStack; + + for (;;) + { + // If we are on the leaf level, we need to make sure we are + // looking at a first occurrence of an entry. + + if( m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF_DATA) + { + pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, + m_pStack->uiCurOffset); + + if( bteFirstElementFlag( pucEntry)) + { + break; + } + } + else + { + break; + } + + if( RC_BAD( rc = moveStackToPrev( NULL))) + { + goto Exit; + } + } + + *pui32BlkAddr = m_pStack->ui32BlkAddr; + + if( puiOffsetIndex) + { + *puiOffsetIndex = m_pStack->uiCurOffset; + } + +Exit: + + if( pBlock) + { + pBlock->Release(); + } + + if( RC_BAD( rc)) + { + btRelease(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Method to search through a BTree block to find a specific key. If + that key cannot be found, then the pStack will be positioned right + after the last entry in the block. The search is a binary search that + is looking for the first key that is >= the target key. The uiMatch + parameter further qualifies the search. The FLM_FIRST & FLM_LAST + values will ignore the key altogether and just return the first or last + key respectively. The FLM_INCL value will return the key if found or the + first key following if not found. The FLM_EXACT will return an + NE_FLM_NOT_FOUND if the key cannot be found. FLM_EXCL will return + the first key following the target key. +****************************************************************************/ +RCODE F_Btree::scanBlock( + F_BTSK * pStack, + FLMUINT uiMatch) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiTop; + FLMUINT uiMid; + FLMUINT uiBottom; + FLMINT iResult; + IF_Block * pBlock = NULL; + const FLMBYTE * pucBlockKey; + FLMBYTE * pucEntry; + FLMUINT uiBlockKeyLen; + + if( pStack->pBlkHdr->ui16NumKeys == 0) + { + rc = RC_SET( NE_FLM_BOF_HIT); + goto Exit; + } + + uiTop = 0; + uiBottom = (FLMUINT)(pStack->pBlkHdr->ui16NumKeys - 1); + + if( uiMatch == FLM_FIRST) + { + pStack->uiCurOffset = uiTop; + goto Exit; + } + + if( uiMatch == FLM_LAST || pStack->uiKeyLen == 0) + { + pStack->uiCurOffset = uiBottom; + goto Exit; + } + + flmAssert( uiMatch == FLM_INCL || uiMatch == FLM_EXCL || + uiMatch == FLM_EXACT); + + // Test the first entry + + pucEntry = (FLMBYTE *)pStack->pBlkHdr + + bteGetEntryOffset( pStack->pui16OffsetArray, + uiTop); + + uiBlockKeyLen = getEntryKeyLength( pucEntry, + ((F_BLK_HDR *)pStack->pBlkHdr)->ui8BlkType, + &pucBlockKey); + + // Compare the entries ... + + if( !uiBlockKeyLen) + { + // The LEM entry will always sort last!! + + iResult = 1; + goto ResultGreater1; + } + else + { + if( RC_BAD( rc = compareBlkKeys( pucBlockKey, uiBlockKeyLen, + pStack->pucKeyBuf, pStack->uiKeyLen, &iResult))) + { + goto Exit; + } + } + + if( iResult >= 0) + { +ResultGreater1: + + if( iResult && uiMatch == FLM_EXACT) + { + rc = RC_SET( NE_FLM_NOT_FOUND); + } + + uiMid = uiTop; + goto VerifyPosition; + } + + // If there is more than one entry in the block, we can skip the first + // one since we have already seen it. + + if( uiTop < uiBottom) + { + uiTop++; + } + + // Test the last + + pucEntry = (FLMBYTE *)pStack->pBlkHdr + + bteGetEntryOffset( pStack->pui16OffsetArray, + uiBottom); + + uiBlockKeyLen = getEntryKeyLength( pucEntry, + ((F_BLK_HDR *)pStack->pBlkHdr)->ui8BlkType, + &pucBlockKey); + + if( !uiBlockKeyLen) + { + // The LEM entry will always sort last!! + + iResult = 1; + goto ResultGreater2; + } + else + { + if( RC_BAD( rc = compareBlkKeys( pucBlockKey, uiBlockKeyLen, + pStack->pucKeyBuf, pStack->uiKeyLen, &iResult))) + { + goto Exit; + } + } + + if( iResult <= 0) + { + if( iResult < 0 && uiMatch != FLM_INCL) + { + rc = RC_SET( NE_FLM_NOT_FOUND); + } + + uiMid = uiBottom; + goto VerifyPosition; + } + +ResultGreater2: + + for( ;;) + { + + if( uiTop == uiBottom) + { + // We're done - didn't find it. + + if( uiMatch == FLM_EXACT) + { + rc = RC_SET( NE_FLM_NOT_FOUND); + } + + uiMid = uiTop; + break; + } + + // Get the midpoint + + uiMid = (uiTop + uiBottom) / 2; + + pucEntry = (FLMBYTE *)pStack->pBlkHdr + + bteGetEntryOffset( pStack->pui16OffsetArray, + uiMid); + + uiBlockKeyLen = getEntryKeyLength( pucEntry, + ((F_BLK_HDR *)pStack->pBlkHdr)->ui8BlkType, + &pucBlockKey); + + // Compare the entries + + if( !uiBlockKeyLen) + { + // The LEM entry will always sort last!! + + iResult = 1; + goto ResultGreater; + } + else + { + if( RC_BAD( rc = compareBlkKeys( pucBlockKey, uiBlockKeyLen, + pStack->pucKeyBuf, pStack->uiKeyLen, &iResult))) + { + goto Exit; + } + } + + if( iResult > 0) + { +ResultGreater: + + // Midpoint (block key) is > Target key + + uiBottom = uiMid; + continue; + } + + if( iResult < 0) + { + // Midpoint (block key) is < Target key + // Since we want to find the first key that is >= to the target key, + // and we have aleady visited the key at uiMid and know that it is < + // our target key, we can skip it and advance to the key that is one + // beyond it. + + flmAssert( uiMid < uiBottom); + uiTop = uiMid + 1; + continue; + } + + break; + } + +VerifyPosition: + + if( uiMatch != FLM_EXCL) + { + // Verify that we are looking at the first occurrence of this key. + + while( iResult == 0) + { + if( uiMid > 0) + { + pucEntry = (FLMBYTE *)pStack->pBlkHdr + + bteGetEntryOffset( pStack->pui16OffsetArray, + (uiMid - 1)); + + uiBlockKeyLen = getEntryKeyLength( pucEntry, + ((F_BLK_HDR *)pStack->pBlkHdr)->ui8BlkType, + &pucBlockKey); + + if( !uiBlockKeyLen) + { + // The LEM entry will always sort last!! + + iResult = 1; + } + else + { + if( RC_BAD( rc = compareBlkKeys( pucBlockKey, uiBlockKeyLen, + pStack->pucKeyBuf, pStack->uiKeyLen, &iResult))) + { + goto Exit; + } + + if( iResult == 0) + { + uiMid--; + } + } + } + else + { + break; + } + } + + pStack->uiCurOffset = uiMid; + } + else if( uiMatch == FLM_EXCL) + { + // If we are at the leaf level, then we want to see if + // this is the last entry in the last block. + // If it is, then we cannot satisfy the request, otherwise + // we will position to the next key and return ok. + + if( pStack->pBlkHdr->ui8BlkLevel == 0 && + pStack->pBlkHdr->stdBlkHdr.ui32NextBlkInChain == 0 && + uiMid == (FLMUINT)pStack->pBlkHdr->ui16NumKeys - 1 && + iResult == 0) + { + rc = RC_SET( NE_FLM_EOF_HIT); + } + else if( pStack->pBlkHdr->ui8BlkLevel == 0) + { + // Check for the next entry at leaf level + + while( iResult == 0) + { + // Are we on the last key? + + if( uiMid == (FLMUINT)(pStack->pBlkHdr->ui16NumKeys - 1)) + { + if( pStack->pBlkHdr->stdBlkHdr.ui32NextBlkInChain == 0) + { + rc = RC_SET( NE_FLM_NOT_FOUND); + } + else + { + pStack->uiCurOffset = uiMid; + m_pStack = pStack; + + if( RC_BAD( rc = moveStackToNext( NULL))) + { + goto Exit; + } + + uiMid = 0; + } + } + else + { + uiMid++; + } + + pucEntry = (FLMBYTE *)pStack->pBlkHdr + + bteGetEntryOffset( pStack->pui16OffsetArray, + uiMid); + + uiBlockKeyLen = getEntryKeyLength( pucEntry, + ((F_BLK_HDR *)pStack->pBlkHdr)->ui8BlkType, + &pucBlockKey); + + if( !uiBlockKeyLen) + { + // The LEM entry will always sort last!! + + iResult = 1; + } + else + { + if( RC_BAD( rc = compareBlkKeys( pucBlockKey, uiBlockKeyLen, + pStack->pucKeyBuf, pStack->uiKeyLen, &iResult))) + { + goto Exit; + } + } + } + + pStack->uiCurOffset = uiMid; + if( uiMid == (FLMUINT)(pStack->pBlkHdr->ui16NumKeys - 1) && + pStack->pBlkHdr->stdBlkHdr.ui32NextBlkInChain == 0) + { + rc = RC_SET( NE_FLM_EOF_HIT); + } + } + else + { + pStack->uiCurOffset = uiMid; + } + } + +Exit: + + if( pBlock) + { + pBlock->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: This method will compare two key fields. + Returned values: 0 - Keys are equal + 1 - Key in Block is > Target key + -1 - Key in Block is < Target key +****************************************************************************/ +RCODE F_Btree::compareKeys( + const FLMBYTE * pucKey1, + FLMUINT uiKeyLen1, + const FLMBYTE * pucKey2, + FLMUINT uiKeyLen2, + FLMINT * piCompare) +{ + RCODE rc = NE_FLM_OK; + + if( !m_pCompare) + { + if( (*piCompare = f_memcmp( pucKey1, pucKey2, + f_min( uiKeyLen1, uiKeyLen2))) == 0) + { + *piCompare = uiKeyLen1 == uiKeyLen2 + ? 0 + : uiKeyLen1 < uiKeyLen2 + ? -1 + : 1; + } + } + else + { + if( RC_BAD( rc = m_pCompare->compare( pucKey1, uiKeyLen1, + pucKey2, uiKeyLen2, piCompare))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Method for positioning to a specific entry. +****************************************************************************/ +RCODE F_Btree::positionToEntry( + FLMUINT uiPosition) +{ + RCODE rc = NE_FLM_OK; + F_BTSK * pStack = NULL; + FLMUINT32 ui32BlkAddress; + IF_Block * pBlock = NULL; + FLMUINT uiLevel; + FLMBYTE * pucEntry; + FLMUINT uiPrevCounts = 0; + + // Make sure the stack is clean before we start. + + btRelease(); + + // Beginning at the root node. + + ui32BlkAddress = (FLMUINT32)m_uiRootBlkAddr; + + // Get the block - Note that this will place a use on the block. + // It must be properly released when done. + + while( ui32BlkAddress) + { + if( RC_BAD( rc = m_pBlockMgr->getBlock( ui32BlkAddress, &pBlock))) + { + goto Exit; + } + + uiLevel = ((F_BTREE_BLK_HDR *)pBlock->getBlockPtr())->ui8BlkLevel; + pStack = &m_Stack[ uiLevel]; + + pStack->pBlkHdr = (F_BTREE_BLK_HDR *)pBlock->getBlockPtr(); + pStack->ui32BlkAddr = ui32BlkAddress; + pStack->pBlock = pBlock; + pBlock = NULL; + pStack->uiLevel = uiLevel; + pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pStack->pBlkHdr, 0); + + m_uiStackLevels++; + + if( RC_BAD( rc = searchBlock( pStack->pBlkHdr, &uiPrevCounts, + uiPosition, &pStack->uiCurOffset))) + { + goto Exit; + } + + if( (pStack->pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF_DATA) || + (pStack->pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF)) + { + ui32BlkAddress = 0; + } + else + { + // Get the next child block address + + pucEntry = BtEntry( (FLMBYTE *)pStack->pBlkHdr, + pStack->uiCurOffset); + + ui32BlkAddress = bteGetBlkAddr( pucEntry); + } + } + + m_uiRootLevel = m_uiStackLevels - 1; + +Exit: + + if( RC_OK( rc) || (rc == NE_FLM_NOT_FOUND) || (rc == NE_FLM_EOF_HIT)) + { + m_pStack = pStack; + } + + if( pBlock) + { + pBlock->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Btree::searchBlock( + F_BTREE_BLK_HDR * pBlkHdr, + FLMUINT * puiPrevCounts, + FLMUINT uiPosition, + FLMUINT * puiOffset) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiOffset; + FLMUINT uiNumKeys; + FLMUINT uiCounts; + FLMBYTE * pucEntry; + + uiNumKeys = pBlkHdr->ui16NumKeys; + + if( getBlkType( (FLMBYTE *)pBlkHdr) != BT_NON_LEAF_COUNTS) + { + flmAssert( uiPosition >= *puiPrevCounts); + + uiOffset = uiPosition - *puiPrevCounts; + *puiPrevCounts = uiPosition; + } + else + { + for( uiOffset = 0; uiOffset < uiNumKeys; uiOffset++) + { + pucEntry = BtEntry( (FLMBYTE *)pBlkHdr, uiOffset); + pucEntry += 4; + + uiCounts = FB2UD( pucEntry); + + if( *puiPrevCounts + uiCounts >= (uiPosition + 1)) + { + break; + } + else + { + *puiPrevCounts += uiCounts; + } + } + } + + if( uiOffset >= uiNumKeys) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + } + + *puiOffset = uiOffset; + return( rc); +} + +/*************************************************************************** +Desc: Method to move all the data in the block into a contiguous space. +****************************************************************************/ +RCODE F_Btree::defragmentBlock( + IF_Block ** ppBlock) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiNumKeys; + FLMBOOL bSorted; + FLMBYTE * pucCurEntry; + FLMBYTE * pucPrevEntry; + FLMBYTE * pucTempEntry; + FLMUINT uiTempToMove; + FLMUINT uiIndex; + FLMUINT uiAmtToMove; + FLMUINT uiFirstHole; + FLMUINT16 ui16BlkBytesAvail; + FLMUINT16 * pui16OffsetArray; + IF_Block * pBlock = *ppBlock; + F_BTREE_BLK_HDR * pBlk = (F_BTREE_BLK_HDR *)pBlock->getBlockPtr(); + F_BTREE_BLK_HDR * pOldBlk = NULL; + FLMBYTE * pucHeap; + FLMBYTE * pucBlkEnd; + IF_Block * pOldBlock = NULL; + void * pvPoolMark = m_pPool->poolMark(); + + flmAssert( pBlk->stdBlkHdr.ui16BlkBytesAvail != pBlk->ui16HeapSize); + + pOldBlock = pBlock; + pOldBlock->AddRef(); + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &pBlock))) + { + goto Exit; + } + + pBlk = (F_BTREE_BLK_HDR *)pBlock->getBlockPtr(); + *ppBlock = pBlock; + uiNumKeys = getBlkEntryCount( (FLMBYTE *)pBlk); + + // Determine if the entries are sorted + + pucPrevEntry = (FLMBYTE *)pBlk + m_uiBlockSize; + bSorted = TRUE; + uiFirstHole = 0; + pucHeap = (FLMBYTE *)pBlk + m_uiBlockSize; + + for( uiIndex = 0; uiIndex < uiNumKeys; uiIndex++) + { + pucCurEntry = BtEntry( (FLMBYTE *)pBlk, uiIndex); + + if( pucPrevEntry < pucCurEntry) + { + bSorted = FALSE; + break; + } + else + { + uiAmtToMove = actualEntrySize( + getEntrySize( (FLMBYTE *)pBlk, uiIndex)); + pucHeap -= uiAmtToMove; + + if( !uiFirstHole && pucHeap != pucCurEntry) + { + uiFirstHole = uiIndex + 1; + } + } + + pucPrevEntry = pucCurEntry; + } + + ui16BlkBytesAvail = (FLMUINT16)(m_uiBlockSize - sizeofBTreeBlkHdr( pBlk)) - + (FLMUINT16)(uiNumKeys * 2); + pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlk, 0); + pucBlkEnd = (FLMBYTE *)pBlk + m_uiBlockSize; + + if( uiFirstHole > 1) + { + uiFirstHole--; + pucHeap = BtEntry( (FLMBYTE *)pBlk, uiFirstHole - 1); + ui16BlkBytesAvail -= (FLMUINT16)(pucBlkEnd - pucHeap); + } + else + { + uiFirstHole = 0; + pucHeap = pucBlkEnd; + } + + if( !bSorted) + { + FLMBYTE * pucTempDefragBlk; + FLMUINT16 * pui16OldOffsetArray; + + // If old and new blocks are the same (because of a + // prior call to logBlock), we need to save a copy of the block + // before making changes. + + if( pOldBlock == pBlock) + { + if( RC_BAD( rc = m_pPool->poolAlloc( m_uiBlockSize, + (void **)&pucTempDefragBlk))) + { + goto Exit; + } + + f_memcpy( pucTempDefragBlk, pBlock->getBlockPtr(), m_uiBlockSize); + pOldBlk = (F_BTREE_BLK_HDR *)pucTempDefragBlk; + } + else + { + pOldBlk = (F_BTREE_BLK_HDR *)pOldBlock->getBlockPtr(); + } + + pui16OldOffsetArray = BtOffsetArray( (FLMBYTE *)pOldBlk, 0); + + // Rebuild the block so that all of the entries are in order + + for( uiIndex = uiFirstHole; uiIndex < uiNumKeys; uiIndex++) + { + pucCurEntry = BtEntry( (FLMBYTE *)pOldBlk, uiIndex); + uiAmtToMove = actualEntrySize( getEntrySize( (FLMBYTE *)pOldBlk, uiIndex)); + pucHeap -= uiAmtToMove; + bteSetEntryOffset( pui16OffsetArray, uiIndex, pucHeap - (FLMBYTE *)pBlk); + uiIndex++; + + while( uiIndex < uiNumKeys) + { + pucTempEntry = BtEntry( (FLMBYTE *)pOldBlk, uiIndex); + uiTempToMove = actualEntrySize( getEntrySize( (FLMBYTE *)pOldBlk, uiIndex)); + + if ((pucCurEntry - uiTempToMove) != pucTempEntry) + { + uiIndex--; + break; + } + else + { + pucCurEntry -= uiTempToMove; + pucHeap -= uiTempToMove; + uiAmtToMove += uiTempToMove; + bteSetEntryOffset( pui16OffsetArray, uiIndex, + pucHeap - (FLMBYTE *)pBlk); + uiIndex++; + } + } + + f_memcpy( pucHeap, pucCurEntry, uiAmtToMove); + ui16BlkBytesAvail -= (FLMUINT16)uiAmtToMove; + } + } + else + { + // Work back from the first hole. Move entries to fill all of the + // holes in the block. + + for( uiIndex = uiFirstHole; uiIndex < uiNumKeys; uiIndex++) + { + pucCurEntry = BtEntry( (FLMBYTE *)pBlk, uiIndex); + uiAmtToMove = actualEntrySize( getEntrySize( (FLMBYTE *)pBlk, uiIndex)); + pucHeap -= uiAmtToMove; + + if( pucHeap != pucCurEntry) + { + // We have a hole. We don't want to move just one entry + // if we can avoid it. We would like to continue searching + // until we find either the end, or another hole. Then we + // can move a larger block of data instead of one entry. + + bteSetEntryOffset( pui16OffsetArray, uiIndex, + pucHeap - (FLMBYTE *)pBlk); + uiIndex++; + + while( uiIndex < uiNumKeys) + { + pucTempEntry = BtEntry( (FLMBYTE *)pBlk, uiIndex); + uiTempToMove = actualEntrySize( + getEntrySize( (FLMBYTE *)pBlk, uiIndex)); + + if( (pucCurEntry - uiTempToMove) != pucTempEntry) + { + uiIndex--; + break; + } + else + { + pucCurEntry -= uiTempToMove; + pucHeap -= uiTempToMove; + uiAmtToMove += uiTempToMove; + bteSetEntryOffset( pui16OffsetArray, + uiIndex, (pucHeap - (FLMBYTE *)pBlk)); + uiIndex++; + } + } + } + + // Now move the range we have determined. + + f_memmove( pucHeap, pucCurEntry, uiAmtToMove); + ui16BlkBytesAvail -= (FLMUINT16)(uiAmtToMove); + } + } + + // Set the available space. If there are no keys in this block, we should + // set the it to the calculated available space + + if( !uiNumKeys) + { + pBlk->stdBlkHdr.ui16BlkBytesAvail = ui16BlkBytesAvail; + } + + flmAssert( pBlk->stdBlkHdr.ui16BlkBytesAvail == ui16BlkBytesAvail); + pBlk->ui16HeapSize = ui16BlkBytesAvail; + + // Clean up the heap space. + +#ifdef FLM_DEBUG + f_memset( getBlockEnd( pBlk) - ui16BlkBytesAvail, 0, ui16BlkBytesAvail); +#endif + +Exit: + + if( pOldBlock) + { + pOldBlock->Release(); + } + + m_pPool->poolReset( pvPoolMark); + return( rc); +} + +/*************************************************************************** +Desc: Method to handle the insertion, deletion and replacment of a single + entry in a block. + Assumption: The find method has already been called to locate the + insertion point, so the stack has already been setup. +****************************************************************************/ +RCODE F_Btree::updateEntry( + const FLMBYTE * pucKey, // In + FLMUINT uiKeyLen, // In + const FLMBYTE * pucValue, // In + FLMUINT uiLen, // In + F_ELM_UPD_ACTION eAction, + FLMBOOL bTruncate) +{ + RCODE rc = NE_FLM_OK; + const FLMBYTE * pucRemainingValue = NULL; + FLMUINT uiRemainingLen = 0; + const FLMBYTE * pucSavKey = pucKey; + FLMUINT uiSavKeyLen = uiKeyLen; + FLMUINT uiChildBlkAddr = 0; + FLMUINT uiCounts = 0; + FLMUINT uiFlags = BTE_FLAG_FIRST_ELEMENT | BTE_FLAG_LAST_ELEMENT; + FLMBOOL bMoreToRemove = FALSE; + FLMBOOL bDone = FALSE; + FLMUINT uiOrigDataLen = uiLen; + FLMBOOL bOrigTruncate = bTruncate; + + flmAssert( m_pReplaceInfo == NULL); + + // For each level that needs modifying... + + while( !bDone) + { + + switch( eAction) + { + case ELM_INSERT_DO: + { + // In this case, the uiLen parameter represents the OADataLength. + + uiFlags = BTE_FLAG_DATA_BLOCK | + BTE_FLAG_FIRST_ELEMENT | + BTE_FLAG_LAST_ELEMENT | + BTE_FLAG_OA_DATA_LEN; + + if( RC_BAD( rc = insertEntry( &pucKey, &uiKeyLen, pucValue, + uiLen, uiFlags, &uiChildBlkAddr, &uiCounts, &pucRemainingValue, + &uiRemainingLen, &eAction))) + { + goto Exit; + } + + // Not needed for upper levels of the Btree. + + pucValue = NULL; + uiLen = 0; + break; + } + + case ELM_INSERT: + { + // This function will return all info needed to handle the next + // level up in the Btree (if anything), including setting up + // the stack. pucKey & uiKeyLen will be pointing to the key that + // the upper level needs to insert, replace or delete. + // + // It will be pointing to an entry in a lower level block, so that + // block must not be released until after we are all done. + + if( RC_BAD( rc = insertEntry( &pucKey, &uiKeyLen, pucValue, + uiLen, uiFlags, &uiChildBlkAddr, &uiCounts, &pucRemainingValue, + &uiRemainingLen, &eAction))) + { + goto Exit; + } + + // Not needed for upper levels of the Btree. + + pucValue = NULL; + uiLen = 0; + break; + } + + case ELM_REPLACE_DO: + { + // In this case, the uiLen parameter represents the OADataLength. + + uiFlags = BTE_FLAG_DATA_BLOCK | + BTE_FLAG_FIRST_ELEMENT | + BTE_FLAG_LAST_ELEMENT | + BTE_FLAG_OA_DATA_LEN; + + // Should only get here if we are able to truncate the data. + + flmAssert( bTruncate); + + if( RC_BAD( rc = replaceEntry( &pucKey, &uiKeyLen, pucValue, + uiLen, uiFlags, &uiChildBlkAddr, &uiCounts, + &pucRemainingValue, &uiRemainingLen, &eAction))) + { + goto Exit; + } + + // Not needed for upper levels of the Btree. + + pucValue = NULL; + uiLen = 0; + bTruncate = TRUE; + break; + } + + case ELM_REPLACE: + { + if( RC_BAD( rc = replaceEntry( &pucKey, &uiKeyLen, pucValue, + uiLen, uiFlags, &uiChildBlkAddr, &uiCounts, &pucRemainingValue, + &uiRemainingLen, &eAction, bTruncate))) + { + goto Exit; + } + + // Not needed for upper levels of the Btree. + + pucValue = NULL; + uiLen = 0; + bTruncate = TRUE; + break; + } + + case ELM_REMOVE: + { + if (RC_BAD( rc = removeEntry( &pucKey, &uiKeyLen, &uiChildBlkAddr, + &uiCounts, &bMoreToRemove, &eAction))) + { + goto Exit; + } + + // Not needed for upper levels of the B-Tree. + + pucValue = NULL; + uiLen = 0; + + break; + } + + case ELM_DONE: + { + if( m_pReplaceInfo) + { + // This info structure gets generated when the replaced entry in + // the upper levels is the last entry in the block and we had to + // move entries to a previous block to accommodate it. + // We will therefore need to update the parent block with this + // new information. We need to take care of this before we check + // for any additional data to store. + + if( RC_BAD( rc = restoreReplaceInfo( &pucKey, &uiKeyLen, + &uiChildBlkAddr, &uiCounts))) + { + goto Exit; + } + + bTruncate = bOrigTruncate; + eAction = ELM_REPLACE; + } + else if( bMoreToRemove) + { + eAction = ELM_REMOVE; + + // We need to locate where we should remove the entry. + + if( RC_BAD( rc = findEntry( pucSavKey, uiSavKeyLen, FLM_EXACT))) + { + goto Exit; + } + + } + else if( pucRemainingValue && uiRemainingLen) + { + eAction = ELM_INSERT; + + // We need to locate where we should insert the new entry. + + rc = findEntry( pucSavKey, uiSavKeyLen, FLM_EXCL); + + // We could find this entry. If we get back anything other than + // an NE_FLM_EOF_HIT or NE_FLM_OK, then there is a problem. + + if( rc != NE_FLM_OK && rc != NE_FLM_EOF_HIT && + rc != NE_FLM_NOT_FOUND) + { + goto Exit; + } + + pucValue = pucRemainingValue; + uiLen = uiRemainingLen; + pucKey = pucSavKey; + uiKeyLen = uiSavKeyLen; + + // Make certain that the BTE_FIRST_ELEMENT flag is NOT set if + // the first part of the data was stored. + + if( uiOrigDataLen != uiLen) + { + uiFlags = BTE_FLAG_LAST_ELEMENT; + } + else + { + uiFlags = BTE_FLAG_FIRST_ELEMENT | BTE_FLAG_LAST_ELEMENT; + } + } + else + { + bDone = TRUE; + } + + break; + } + + // Should never get this! + + case ELM_BLK_MERGE: + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: This method will coordinate inserting an entry into a block. If it + cannot fit it all in, then it may have to break the entry up so that + it spans more than one block. It will also setup for the next level + before returning. +****************************************************************************/ +RCODE F_Btree::insertEntry( + const FLMBYTE ** ppucKey, + FLMUINT * puiKeyLen, + const FLMBYTE * pucValue, + FLMUINT uiLen, + FLMUINT uiFlags, + FLMUINT * puiChildBlkAddr, + FLMUINT * puiCounts, + const FLMBYTE ** ppucRemainingValue, + FLMUINT * puiRemainingLen, + F_ELM_UPD_ACTION * peAction) +{ + RCODE rc = NE_FLM_OK; + const FLMBYTE * pucDataValue = pucValue; + FLMUINT uiDataLen = uiLen; + FLMUINT uiOADataLen = 0; + FLMUINT uiEntrySize = 0; + FLMBOOL bEntriesWereMoved = FALSE; + FLMBOOL bHaveRoom; + FLMBOOL bLastEntry; + const FLMBYTE * pucKey = *ppucKey; + FLMUINT uiKeyLen = *puiKeyLen; + FLMUINT uiChildBlkAddr = *puiChildBlkAddr; + FLMUINT uiCounts = *puiCounts; + IF_Block * pPrevBlock = NULL; + FLMBYTE * pucEntry; + FLMBOOL bDefragBlk = FALSE; + FLMBOOL bBlockSplit; + + if( m_pStack->uiLevel == 0) + { + // We are only safe to do this when we are working on level 0 + // (leaf level) of the Btree. + + *ppucRemainingValue = NULL; + *puiRemainingLen = 0; + } + + if( *peAction == ELM_INSERT_DO) + { + // Adjust the data entry sizes as the data passed in is the + // OA Data Length. + + uiOADataLen = uiLen; + uiDataLen = 4; + } + + // Process until we are done + +StartOver: + + if( RC_BAD( rc = calcNewEntrySize( uiKeyLen, uiDataLen, &uiEntrySize, + &bHaveRoom, &bDefragBlk))) + { + goto Exit; + } + + // Does the entry fit into the block? + + if( bHaveRoom) + { + if( bDefragBlk) + { + // We will have to defragment the block before we can store the data + + if( RC_BAD( rc = defragmentBlock( &m_pStack->pBlock))) + { + goto Exit; + } + } + + if( RC_BAD( rc = storeEntry( pucKey, uiKeyLen, pucDataValue, + uiDataLen, uiFlags, uiOADataLen, uiChildBlkAddr, uiCounts, + uiEntrySize, &bLastEntry))) + { + goto Exit; + } + + if( (bLastEntry || m_bCounts) && !isRootBlk( m_pStack->pBlkHdr)) + { + // Are we in here because of the counts only? If so, then we + // can update the counts right here, no need to continue. + + if( !bLastEntry) + { + if( RC_BAD( rc = updateCounts())) + { + goto Exit; + } + + *peAction = ELM_DONE; + } + else + { + // Ensure we are updating with the correct key. + + pucEntry = BtLastEntry( (FLMBYTE *)m_pStack->pBlkHdr); + + *puiKeyLen = getEntryKeyLength( pucEntry, + m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType, ppucKey); + + *puiChildBlkAddr = m_pStack->ui32BlkAddr; + + // Do we need counts for the next level? + + if( m_bCounts) + { + *puiCounts = countKeys( (FLMBYTE *)m_pStack->pBlkHdr); + } + + m_pStack++; + *peAction = ELM_REPLACE; + } + } + else + { + *peAction = ELM_DONE; + } + + goto Exit; + } + + // Can we move entries around at all to make some room? + + if( RC_BAD( rc = moveEntriesToPrevBlk( uiEntrySize, &pPrevBlock, + &bEntriesWereMoved))) + { + goto Exit; + } + + if( bEntriesWereMoved) + { + // Only defragment the block if the heap size is not big enough. + + if( uiEntrySize > m_pStack->pBlkHdr->ui16HeapSize) + { + if( RC_BAD( rc = defragmentBlock( &m_pStack->pBlock))) + { + goto Exit; + } + } + + // Store the entry now because we know there is enough room + + if( RC_BAD( rc = storeEntry( pucKey, uiKeyLen, pucDataValue, + uiDataLen, uiFlags, uiOADataLen, uiChildBlkAddr, uiCounts, + uiEntrySize, &bLastEntry))) + { + goto Exit; + } + + // Ordinarily, this would NEVER be the last element in the + // block because we need to adjust the stack to take care of the + // elements we just moved! There is only one condition where we would + // insert as the last entry in the block, and that is when this + // insert is actually a part of a replace operation where the data + // is too large to fit in the block. We had to remove the entry, then + // insert the new one and we are in the upper levels of the + // btree. (i.e. not at the leaf). + + if( bLastEntry) + { + // Since we just added an entry to the last position of the + // current block. We will need to preserve the current stack so + // that we can finish updating the parentage later. Should only + // happen as a result of a replace operation where the new entry + // is larger than the existing one while in the upper levels. + + if( RC_BAD( rc = saveReplaceInfo( pucKey, uiKeyLen))) + { + goto Exit; + } + } + + // Need to update the counts of the parents if we are maintining + // counts before we abandon + + if( m_bCounts) + { + if( RC_BAD( rc = updateCounts())) + { + goto Exit; + } + } + + // This method will release any blocks no longer referenced + // in the stack. Then pull in the previous block information into + // the stack. + + if( RC_BAD( rc = moveStackToPrev( pPrevBlock))) + { + goto Exit; + } + + // If we are maintaining counts, then lets return a count of the + // current number of keys referenced below this point. + + if( m_bCounts) + { + *puiCounts = countKeys( (FLMBYTE *)m_pStack->pBlkHdr); + } + + flmAssert( !isRootBlk( m_pStack->pBlkHdr)); + + // Return the key to the last entry in the prevous block. + // Recall that we have changed that stack now so that it + // is referencing the changed block (pPrevBlock). + + pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); + + *puiKeyLen = getEntryKeyLength( pucEntry, + ((F_BLK_HDR *)pPrevBlock->getBlockPtr())->ui8BlkType, + ppucKey); + + // Return the new child block address + + *puiChildBlkAddr = m_pStack->ui32BlkAddr; + + // Set up to fixup the parentage of the previous block on return... + + m_pStack++; + + // Return the new action for the parent block. + + *peAction = ELM_REPLACE; + goto Exit; + } + + // Try moving to the next block... + + if( RC_BAD( rc = moveEntriesToNextBlk( uiEntrySize, &bEntriesWereMoved))) + { + goto Exit; + } + + if( bEntriesWereMoved) + { + // Only defragment the block if the heap size is not big enough. + + if( uiEntrySize > m_pStack->pBlkHdr->ui16HeapSize) + { + if( RC_BAD( rc = defragmentBlock( &m_pStack->pBlock))) + { + goto Exit; + } + } + + // Store the entry now because we know there is enough room + + if( RC_BAD( rc = storeEntry( pucKey, uiKeyLen, pucDataValue, + uiDataLen, uiFlags, uiOADataLen, uiChildBlkAddr, uiCounts, + uiEntrySize, &bLastEntry))) + { + goto Exit; + } + + // Return the key to the last entry in the current block. + // Note: If bLastEntry is TRUE, we already know what the key is. + + if( !bLastEntry) + { + // Get the last key from the block. + + pucEntry = BtLastEntry( (FLMBYTE *)m_pStack->pBlkHdr); + + *puiKeyLen = getEntryKeyLength( pucEntry, + m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType, ppucKey); + + } + + flmAssert( !isRootBlk( m_pStack->pBlkHdr)); + + // if we are maintaining counts, then lets return a count of the + // current number of keys referenced below this point. + + if( m_bCounts) + { + *puiCounts = countKeys( (FLMBYTE *)m_pStack->pBlkHdr); + } + + // Return the new child block address + + *puiChildBlkAddr = m_pStack->ui32BlkAddr; + + // Set up to fixup the parentage of the this block on return... + + m_pStack++; + *peAction = ELM_REPLACE; + + goto Exit; + } + + // Before we incur the expense of a block split, see if we can store this + // entry in the previous block. If we can, we will save some space. This + // will only happen if we are trying to insert at the first position in + // this block. We would only ever get into this block of code once for + // each level of the btree. + + if( m_pStack->uiCurOffset == 0 && + m_pStack->pBlkHdr->stdBlkHdr.ui32PrevBlkInChain) + { + if( RC_BAD( rc = m_pBlockMgr->getBlock( + m_pStack->pBlkHdr->stdBlkHdr.ui32PrevBlkInChain, &pPrevBlock))) + { + goto Exit; + } + + if( RC_BAD( rc = moveStackToPrev( pPrevBlock))) + { + goto Exit; + } + + // Increment so we point to one past the last entry. + + m_pStack->uiCurOffset++; + goto StartOver; + } + + // We will have to split the block to make room for this entry. + + if( RC_BAD( rc = splitBlock( *ppucKey, *puiKeyLen, pucDataValue, + uiDataLen, uiFlags, uiOADataLen, uiChildBlkAddr, uiCounts, + ppucRemainingValue, puiRemainingLen, &bBlockSplit))) + { + goto Exit; + } + + // Return the new key value. + + pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); + + *puiKeyLen = getEntryKeyLength( pucEntry, + ((F_BLK_HDR *)m_pStack->pBlkHdr)->ui8BlkType, ppucKey); + + // Return the child block address and the counts (if needed). + + *puiChildBlkAddr = m_pStack->ui32BlkAddr; + + // Return the counts if we are maintaining them + + if( m_bCounts) + { + *puiCounts = countKeys( (FLMBYTE *)m_pStack->pBlkHdr); + } + + // The bBlockSplit boolean will only be FALSE if we were involved in a + // ReplaceByInsert operation and the call to split resulted in an empty + // block. Thus we were able to store the new entry. In such cases, + // only the count (if any) need to be updated, not the keys. + + if( bBlockSplit) + { + *peAction = ELM_INSERT; + m_pStack++; + } + else + { + *peAction = ELM_DONE; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Method to handle the insertion of a single entry into a block. + Assumption: The find method has already been called to locate the + insertion point, so the stack has already been setup. +****************************************************************************/ +RCODE F_Btree::storeEntry( + const FLMBYTE * pucKey, + FLMUINT uiKeyLen, + const FLMBYTE * pucValue, + FLMUINT uiLen, + FLMUINT uiFlags, + FLMUINT uiOADataLen, + FLMUINT uiChildBlkAddr, + FLMUINT uiCounts, + FLMUINT uiEntrySize, + FLMBOOL * pbLastEntry) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiBlkType = ((F_BLK_HDR *)m_pStack->pBlock->getBlockPtr())->ui8BlkType; + FLMBYTE * pucInsertAt; + FLMUINT16 * pui16OffsetArray; + FLMUINT uiNumKeys; + FLMUINT uiTmp; + F_BTREE_BLK_HDR * pBlk; + + // Assume this is not the last entry for now. + // We will change it later if needed. + + *pbLastEntry = FALSE; + + // We can go ahead and insert this entry as it is. All checking has been + // made before getting to this point. + + uiEntrySize = calcEntrySize( uiBlkType, uiFlags, + uiKeyLen, uiLen, uiOADataLen); + + // Log this block before making any changes to it. Since the + // pBlock could change, we must update the block header after the call. + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &m_pStack->pBlock))) + { + goto Exit; + } + + pBlk = m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pBlock->getBlockPtr(); + m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlk, 0); + uiNumKeys = getBlkEntryCount( (FLMBYTE *)pBlk); + pucInsertAt = getBlockEnd( pBlk) - uiEntrySize; + pui16OffsetArray = m_pStack->pui16OffsetArray; + + if( RC_BAD( rc = buildAndStoreEntry( uiBlkType, uiFlags, pucKey, uiKeyLen, + pucValue, uiLen, uiOADataLen, uiChildBlkAddr, uiCounts, + pucInsertAt, uiEntrySize, NULL))) + { + goto Exit; + } + + // Now to update the offset in the offset array. This will move all + // entries that sort after the new entry down by one position. + + for( uiTmp = uiNumKeys; uiTmp > m_pStack->uiCurOffset; uiTmp--) + { + bteSetEntryOffset( pui16OffsetArray, uiTmp, + bteGetEntryOffset( pui16OffsetArray, uiTmp - 1)); + } + + bteSetEntryOffset( pui16OffsetArray, m_pStack->uiCurOffset, + (FLMUINT16)(pucInsertAt - (FLMBYTE *)pBlk)); + + // Update the available space and the number of keys. + // Account for the new offset entry too. + + m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail -= + (FLMUINT16)(uiEntrySize + 2); + m_pStack->pBlkHdr->ui16HeapSize -= (FLMUINT16)(uiEntrySize + 2); + m_pStack->pBlkHdr->ui16NumKeys++; + + // Check to see if this was the last entry + + if( m_pStack->uiCurOffset == (FLMUINT)(m_pStack->pBlkHdr->ui16NumKeys - 1)) + { + *pbLastEntry = TRUE; + } + + if( !m_pStack->uiLevel && (uiFlags & BTE_FLAG_FIRST_ELEMENT)) + { + m_ui32PrimaryBlkAddr = m_pStack->ui32BlkAddr; + m_uiCurOffset = m_pStack->uiCurOffset; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: This method will coordinate removing an entry from a block. If the + entry spans more than one block, it will set the flag pbMoreToRemove. + It will also setup for the next level before returning. +****************************************************************************/ +RCODE F_Btree::removeEntry( + const FLMBYTE ** ppucKey, + FLMUINT * puiKeyLen, + FLMUINT * puiChildBlkAddr, + FLMUINT * puiCounts, + FLMBOOL * pbMoreToRemove, + F_ELM_UPD_ACTION * peAction) +{ + RCODE rc = NE_FLM_OK; + FLMBOOL bLastEntry = FALSE; + FLMBYTE * pucEntry; + FLMBOOL bMergedWithPrev = FALSE; + FLMBOOL bMergedWithNext = FALSE; + + if( m_pStack->uiLevel == 0) + { + // We are only safe to do this when we are working on level 0 + // (leaf level) of the Btree. + + *pbMoreToRemove = FALSE; + } + + // Check the current entry to see if it spans more than a single block. + + pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, + m_pStack->uiCurOffset); + + // We only need to worry about data spanning more than one block if it is + // at level zero (i.e. leaf block) and the lastElement flag is not set. + + if( (m_pStack->uiLevel == 0) && m_bData && !bteLastElementFlag( pucEntry)) + { + *pbMoreToRemove = TRUE; + } + + // Find out if we are looking at the last entry in the block. + + if( m_pStack->uiCurOffset == (FLMUINT)(m_pStack->pBlkHdr->ui16NumKeys - 1)) + { + bLastEntry = TRUE; + } + + // Now we remove the entry... Will also remove any chained Data Only blocks + + if( RC_BAD( rc = remove( TRUE))) + { + goto Exit; + } + + // If the block is now empty, we will free the block. + + if( !m_pStack->pBlkHdr->ui16NumKeys) + { + FLMBOOL bIsRoot; + + // Test for root block. + + bIsRoot = isRootBlk( m_pStack->pBlkHdr); + + if( RC_BAD( rc = deleteEmptyBlock())) + { + goto Exit; + } + + // Need to remove the parent entry referencing the deleted block. + + if( !bIsRoot) + { + *peAction = ELM_REMOVE; + m_pStack++; + } + else + { + // If we ever get here, it means we have just deleted the root block. + // I have put in the possibility, but typically, deleting the Btree + // is done by calling btDeleteTree. + + *peAction = ELM_DONE; + } + } + else + { + if( ((((F_BLK_HDR *)m_pStack->pBlkHdr)->ui16BlkBytesAvail * 100) / + m_uiBlockSize) >= BT_LOW_WATER_MARK) + { + // We will need to check to see if we can merge two blocks into one to + // conserve space. + + if( RC_BAD( rc = mergeBlocks( bLastEntry, &bMergedWithPrev, + &bMergedWithNext, peAction))) + { + goto Exit; + } + } + + // If the entry that we just removed was the last entry in the block and + // we did not merge any blocks, we will need to prep for an update to the + // parent with a new key. + + if( bLastEntry && !bMergedWithPrev && !bMergedWithNext) + { + if( m_bCounts) + { + *puiCounts = countKeys( (FLMBYTE *)m_pStack->pBlkHdr); + } + + // Backup to the new "last" entry (remove() does not adjust the offset + // in the stack). + + flmAssert( m_pStack->uiCurOffset > 0); + + m_pStack->uiCurOffset--; + pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, + m_pStack->uiCurOffset); + + *puiKeyLen = getEntryKeyLength( pucEntry, + m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType, ppucKey); + + *puiChildBlkAddr = m_pStack->ui32BlkAddr; + *peAction = ELM_REPLACE; + m_pStack++; + } + else + { + // Are we tracking counts? + + if( !bMergedWithPrev && !bMergedWithNext) + { + if( m_bCounts) + { + if( RC_BAD( rc = updateCounts())) + { + goto Exit; + } + } + + *peAction = ELM_DONE; + } + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Method to replace an existing entry with a new one. +****************************************************************************/ +RCODE F_Btree::replaceEntry( + const FLMBYTE ** ppucKey, + FLMUINT * puiKeyLen, + const FLMBYTE * pucValue, + FLMUINT uiLen, + FLMUINT uiFlags, + FLMUINT * puiChildBlkAddr, + FLMUINT * puiCounts, + const FLMBYTE ** ppucRemainingValue, + FLMUINT * puiRemainingLen, + F_ELM_UPD_ACTION * peAction, + FLMBOOL bTruncate) +{ + RCODE rc = NE_FLM_OK; + const FLMBYTE * pucDataValue = pucValue; + FLMUINT uiDataLen = uiLen; + FLMUINT uiOADataLen = 0; + FLMBYTE * pucEntry = NULL; + FLMUINT32 ui32OrigDOAddr = 0; + const FLMBYTE * pucData = NULL; + + if( m_pStack->uiLevel == 0) + { + *ppucRemainingValue = NULL; + *puiRemainingLen = 0; + } + + if( *peAction == ELM_REPLACE_DO) + { + // Adjust the data entry sizes as the data passed in + // is the OA Data Length. + + uiOADataLen = uiLen; + uiDataLen = 4; + } + + if( m_pStack->uiLevel == 0 && m_bData) + { + if( m_bOrigInDOBlocks) + { + flmAssert( bTruncate); + + pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, + m_pStack->uiCurOffset); + + btGetEntryDataLength( pucEntry, &pucData, NULL, NULL); + ui32OrigDOAddr = bteGetBlkAddr( pucData); + } + } + + // We only have to worry about updating the upper levels of the Btree + // when we are doing a replacement at a non-leaf level or we are maintaining + // counts. Replacements at the leaf level do not require a change in the + // parent block. The only exception is when the old entry spanned to + // another block, but the new one did not. This results in removing the + // excess part of the old entry unless we are not truncating the element. + // Even then, we only update the parent if the excess entry was the only key + // in the block, i.e. the block became empty as a result of the removal. + // All of this would have been handled already by the time we return from + // this call. + + // When bTruncate is FALSE we do not trim back the entry so we don't worry + // about updating the parentage. + + if( RC_BAD( rc = replaceOldEntry( ppucKey, puiKeyLen, pucDataValue, + uiDataLen, uiFlags, uiOADataLen, puiChildBlkAddr, puiCounts, + ppucRemainingValue, puiRemainingLen, peAction, bTruncate))) + { + goto Exit; + } + + // Do we need to free the original DO blocks since they are not + // used in the new entry? + + if( m_bOrigInDOBlocks && !m_bDataOnlyBlock && m_pStack->uiLevel == 0) + { + if( RC_BAD( rc = removeDOBlocks( ui32OrigDOAddr))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Method to handle replacing a single entry in a block. + ASSUMPTION: The find method has already been called to locate the + insertion point, so the stack has already been setup. +****************************************************************************/ +RCODE F_Btree::replaceOldEntry( + const FLMBYTE ** ppucKey, + FLMUINT * puiKeyLen, + const FLMBYTE * pucValue, + FLMUINT uiLen, + FLMUINT uiFlags, + FLMUINT uiOADataLen, + FLMUINT * puiChildBlkAddr, + FLMUINT * puiCounts, + const FLMBYTE ** ppucRemainingValue, + FLMUINT * puiRemainingLen, + F_ELM_UPD_ACTION * peAction, + FLMBOOL bTruncate) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiOldEntrySize; + FLMBYTE * pucEntry = NULL; + FLMBYTE * pucData = NULL; + FLMUINT uiEntrySize; + FLMBOOL bLastEntry = FALSE; + FLMBOOL bLastElement = TRUE; + FLMBOOL bHaveRoom; + FLMBOOL bDefragBlk; + FLMUINT uiDataLen = 0; + FLMUINT uiOldOADataLen = 0; + FLMBOOL bRemoveOADataAllowance = FALSE; + FLMBYTE * pucTmpBlk = NULL; + void * pvPoolMark = m_pPool->poolMark(); + + uiOldEntrySize = actualEntrySize( + getEntrySize( (FLMBYTE *)m_pStack->pBlkHdr, + m_pStack->uiCurOffset, &pucEntry)); + + if( m_pStack->uiLevel == 0 && m_bData) + { + bLastElement = bteLastElementFlag( pucEntry); + + uiDataLen = btGetEntryDataLength( pucEntry, (const FLMBYTE **)&pucData, + &uiOldOADataLen, NULL); + + // Test to see if we need to worry about the bTruncate flag. + + if( uiDataLen == uiOldOADataLen) + { + if( uiLen > uiDataLen) + { + bTruncate = TRUE; + } + else if( uiLen <= uiDataLen && uiOADataLen == 0) + { + bRemoveOADataAllowance = TRUE; + } + } + else + { + if( uiLen > uiOldOADataLen) + { + bTruncate = TRUE; + } + } + } + + // bTruncate has no meaning if we have no data or we are not at the + // leaf level. + + if( m_pStack->uiLevel != 0 || !m_bData) + { + bTruncate = TRUE; + } + + // The calcNewEntrySize function will tack on 2 bytes for the offset. + // It also adds an extra 4 bytes for the OADataLen, even though it may + // not be needed. We will need to be aware of this here as it may affect + // our decision as to how we will replace the entry. + + if( RC_BAD( rc = calcNewEntrySize( *puiKeyLen, uiLen, &uiEntrySize, + &bHaveRoom, &bDefragBlk))) + { + goto Exit; + } + + if( bRemoveOADataAllowance) + { + uiEntrySize -= 4; + } + + // Since this is a replace operation, we don't need to know about the offset + // as that won't be a factor in what we are doing. 'actualEntrySize' will + // remove those two bytyes from the size. + + uiEntrySize = actualEntrySize( uiEntrySize); + if( uiEntrySize <= uiOldEntrySize) + { + if( !bTruncate) + { + flmAssert( uiLen <= uiDataLen); + f_memcpy( pucData, pucValue, uiLen); + + if( m_pStack->uiCurOffset == + (FLMUINT)(m_pStack->pBlkHdr->ui16NumKeys - 1)) + { + bLastEntry = TRUE; + } + } + else + { + if( !pucTmpBlk) + { + if( RC_BAD( rc = m_pPool->poolAlloc( m_uiBlockSize, (void **)&pucTmpBlk))) + { + goto Exit; + } + } + + // We can go ahead and replace this entry as it is. All checking + // has been made before getting to this point. + + if( RC_BAD( rc = buildAndStoreEntry( + ((F_BLK_HDR *)m_pStack->pBlock->getBlockPtr())->ui8BlkType, + uiFlags, *ppucKey, *puiKeyLen, pucValue, uiLen, uiOADataLen, + *puiChildBlkAddr, *puiCounts, pucTmpBlk, m_uiBlockSize, + &uiEntrySize))) + { + goto Exit; + } + + if( RC_BAD( rc = replace( pucTmpBlk, uiEntrySize, &bLastEntry))) + { + goto Exit; + } + } + + if( !bLastElement && bTruncate) + { + // The element that we replaced actually spans more than one entry. + // We will have to remove the remaining entries. + + if( RC_BAD( rc = removeRemainingEntries( *ppucKey, *puiKeyLen))) + { + goto Exit; + } + } + + if( (bLastEntry || m_bCounts) && !isRootBlk( m_pStack->pBlkHdr) && + (m_pStack->uiLevel != 0)) + { + // Are we in here because of the counts only? If so, then make + // sure we don't change the key in the parent. + + if( !bLastEntry) + { + if( RC_BAD( rc = updateCounts())) + { + goto Exit; + } + + *peAction = ELM_DONE; + } + else + { + // Return the key to the last entry in the block. + + pucEntry = BtLastEntry( (FLMBYTE *)m_pStack->pBlkHdr); + + *puiKeyLen = getEntryKeyLength( pucEntry, + m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType, ppucKey); + *puiChildBlkAddr = m_pStack->ui32BlkAddr; + + // Do we need counts for the next level? + + if( m_bCounts) + { + *puiCounts = countKeys( (FLMBYTE *)m_pStack->pBlkHdr); + } + + m_pStack++; + *peAction = ELM_REPLACE; + } + } + else + { + *peAction = ELM_DONE; + } + + goto Exit; + } + + // If we do not have a stack setup yet (which can happen if the replace + // is trying to shortcut to the previously known block address and offset), + // then at this point, we must build the stack, since it may be required + // to adjust the upper levels of the btree. + + if( !m_bStackSetup) + { + if( RC_BAD( rc = findEntry( *ppucKey, *puiKeyLen, FLM_EXACT))) + { + goto Exit; + } + } + + // The new entry will not fit into the original entry's space. + // If we remove the entry in the block, will there be enough room + // to put it in? + + if( bTruncate && + ((F_BLK_HDR *)m_pStack->pBlock->getBlockPtr())->ui16BlkBytesAvail + + uiOldEntrySize >= uiEntrySize) + { + // First remove the current entry. Do not delete any DO blocks chained + // to this entry. + + if( RC_BAD( rc = remove( FALSE))) + { + goto Exit; + } + + if( (m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail != + m_pStack->pBlkHdr->ui16HeapSize) && + ((uiEntrySize + 2) > m_pStack->pBlkHdr->ui16HeapSize)) + { + if( RC_BAD( rc = defragmentBlock( &m_pStack->pBlock))) + { + goto Exit; + } + } + + // Now insert the new entry. + + if( RC_BAD( rc = storeEntry( *ppucKey, *puiKeyLen, pucValue, uiLen, + uiFlags, uiOADataLen, *puiChildBlkAddr, *puiCounts, uiEntrySize, + &bLastEntry))) + { + goto Exit; + } + + // Check if the original element spanned more than one entry + + if( !bLastElement) + { + // The element that we replaced actually spans more than one entry. + // We will have to remove the remaining entries. + + if( RC_BAD( rc = removeRemainingEntries( *ppucKey, *puiKeyLen))) + { + goto Exit; + } + } + + if( (bLastEntry || m_bCounts) && !isRootBlk( m_pStack->pBlkHdr) && + (m_pStack->uiLevel != 0)) + { + // Are we in here because of the counts only? + + if( !bLastEntry) + { + if( RC_BAD( rc = updateCounts())) + { + goto Exit; + } + + *peAction = ELM_DONE; + } + else + { + // Set the key to the last entry in the block. + + pucEntry = BtLastEntry( (FLMBYTE *)m_pStack->pBlkHdr); + + *puiKeyLen = getEntryKeyLength( pucEntry, + m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType, ppucKey); + *puiChildBlkAddr = m_pStack->ui32BlkAddr; + + // Do we need counts for the next level? + + if( m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType == BT_NON_LEAF_COUNTS) + { + *puiCounts = countKeys( (FLMBYTE *)m_pStack->pBlkHdr); + } + + m_pStack++; + *peAction = ELM_REPLACE; + } + } + else + { + *peAction = ELM_DONE; + } + + goto Exit; + } + + // If the original element does not span multiple entries and we still don't + // have room for the replacement, then we will remove this entry and insert + // the replacement. When the insert happens, it will take care of moving + // things around or splitting the block as needed to get it in. If bTruncate + // is FALSE, and the new entry is larger than the original, we can ignore it. + + if( bLastElement) + { + if( RC_BAD( rc = replaceByInsert( ppucKey, puiKeyLen, + pucValue, uiLen, uiOADataLen, uiFlags, puiChildBlkAddr, + puiCounts, ppucRemainingValue, puiRemainingLen, + peAction))) + { + goto Exit; + } + + goto Exit; + } + + if( bTruncate) + { + if( RC_BAD( rc = replaceMultiples( ppucKey, puiKeyLen, pucValue, + uiLen, uiFlags, puiChildBlkAddr, puiCounts, ppucRemainingValue, + puiRemainingLen, peAction))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = replaceMultiNoTruncate( ppucKey, puiKeyLen, + pucValue, uiLen, uiFlags, puiChildBlkAddr, puiCounts, + ppucRemainingValue, puiRemainingLen, peAction))) + { + goto Exit; + } + } + +Exit: + + m_pPool->poolReset( pvPoolMark); + return( rc); +} + +/*************************************************************************** +Desc: This method is called whenever a replacement entry will not fit in + the block, even if we remove the existing entry. It ASSUMES that the + original element does not continue to another entry, either in the + same block or in another block. +****************************************************************************/ +RCODE F_Btree::replaceByInsert( + const FLMBYTE ** ppucKey, + FLMUINT * puiKeyLen, + const FLMBYTE * pucDataValue, + FLMUINT uiDataLen, + FLMUINT uiOADataLen, + FLMUINT uiFlags, + FLMUINT * puiChildBlkAddr, + FLMUINT * puiCounts, + const FLMBYTE ** ppucRemainingValue, + FLMUINT * puiRemainingLen, + F_ELM_UPD_ACTION * peAction) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiLen = uiDataLen; + + if( *peAction == ELM_REPLACE_DO) + { + uiLen = uiOADataLen; + *peAction = ELM_INSERT_DO; + } + else + { + *peAction = ELM_INSERT; + } + + // At this point, it is clear that this new entry is larger than the + // old entry. We will remove the old entry first. Then we can treat + // this whole operation as an insert rather than as a replace. + + if( RC_BAD( rc = remove( FALSE))) + { + goto Exit; + } + + if( RC_BAD( rc = insertEntry( ppucKey, puiKeyLen, pucDataValue, uiLen, + uiFlags, puiChildBlkAddr, puiCounts, ppucRemainingValue, puiRemainingLen, + peAction))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Method to replace an entry in a block and update the available + space. This method expects to receive a buffer with an entry already + prepared to be written to the block. +****************************************************************************/ +RCODE F_Btree::replace( + FLMBYTE * pucEntry, + FLMUINT uiEntrySize, + FLMBOOL * pbLastEntry) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucReplaceAt; + FLMUINT uiNumKeys; + FLMBYTE * pBlk; + FLMUINT uiOldEntrySize; + + *pbLastEntry = FALSE; + + // Log this block before making any changes to it. Since the + // pBlock could change, we must update the block header after the call. + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &m_pStack->pBlock))) + { + goto Exit; + } + + m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pBlock->getBlockPtr(); + pBlk = (FLMBYTE *)m_pStack->pBlkHdr; + m_pStack->pui16OffsetArray = BtOffsetArray( pBlk, 0); + + uiNumKeys = getBlkEntryCount( pBlk); + + uiOldEntrySize = actualEntrySize( getEntrySize( + pBlk, m_pStack->uiCurOffset)); + + flmAssert( uiOldEntrySize >= uiEntrySize); + + pucReplaceAt = BtEntry( pBlk, m_pStack->uiCurOffset); + + // Let's go ahead and copy the entry into the block now. + + f_memcpy( pucReplaceAt, pucEntry, uiEntrySize); + +#ifdef FLM_DEBUG + // Clean up the empty space (if any) + + if( uiOldEntrySize > uiEntrySize) + { + pucReplaceAt += uiEntrySize; + f_memset( pucReplaceAt, 0, uiOldEntrySize - uiEntrySize); + } +#endif + + // Update the available space. It may not have changed at all if the + // two entries are the same size. The Heap size will not have changed. + // This is because we write the entry into the same location as the + // original. Even though the new entry may be smaller, we start at + // the same location, possibly leaving a hole in the block. + + m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail += + (FLMUINT16)(uiOldEntrySize - uiEntrySize); + + + if( m_pStack->uiCurOffset == (FLMUINT)(m_pStack->pBlkHdr->ui16NumKeys - 1)) + { + *pbLastEntry = TRUE; + } + + // Preserve the block and offset index in case it is wanted on the way out. + + if( !m_pStack->uiLevel && bteFirstElementFlag( pucEntry)) + { + m_ui32PrimaryBlkAddr = m_pStack->ui32BlkAddr; + m_uiCurOffset = m_pStack->uiCurOffset; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Method to rebuild the stack so that it references the parentage of + the parameter pBlock. The assumption is that we will begin at + whatever level m_pStack is currently sitting at. Therefore, this + method can be called for any level in the Btree. +****************************************************************************/ +RCODE F_Btree::moveStackToPrev( + IF_Block * pBlock) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiBlkAddr; + F_BTREE_BLK_HDR * pBlkHdr; + F_BTSK * pStack = m_pStack; + IF_Block * pPrevBlock = NULL; + + if( pBlock) + { + if( pStack->pBlock) + { + // Make sure the block we passed in really is the previous + // block in the chain. + + if( ((F_BLK_HDR *)pBlock->getBlockPtr())->ui32BlkAddr != + ((F_BLK_HDR *)pStack->pBlock->getBlockPtr())->ui32PrevBlkInChain) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + // Cannot be the same block. + + if( pBlock == pStack->pBlock) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + // Release the current block. We don't need to fetch + // the new block because it was passed in to us. If + // we encounter this situation further up the tree, + // we will have to fetch the block as well. + + pStack->pBlock->Release(); + } + + pStack->pBlock = pBlock; + pBlkHdr = (F_BTREE_BLK_HDR *)pBlock->getBlockPtr(); + pStack->pBlkHdr = pBlkHdr; + pStack->ui32BlkAddr = ((F_BLK_HDR *)pBlkHdr)->ui32BlkAddr; + pStack->uiCurOffset = pBlkHdr->ui16NumKeys - 1; // Last entry + pStack->uiLevel = pBlkHdr->ui8BlkLevel; + pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); + + // Now walk up the stack until done. + + pStack++; + } + + for (;;) + { + // If we don't have this block in the stack, we must first get it. + + if( pStack->pBlock == NULL) + { + // Don't continue if we don't have this level in the stack. + + if( pStack->ui32BlkAddr == 0) + { + break; + } + + if( RC_BAD( rc = m_pBlockMgr->getBlock( pStack->ui32BlkAddr, + &pStack->pBlock))) + { + goto Exit; + } + + pStack->pBlkHdr = (F_BTREE_BLK_HDR *)pStack->pBlock->getBlockPtr(); + } + + // See if we need to go to the previous block. + + if( pStack->uiCurOffset == 0) + { + // If this is the root block and we are looking at the first + // entry in the block, then we have a problem. + + if( !isRootBlk( pStack->pBlkHdr)) + { + // When the stack is pointing to the first entry, this + // means that we want the target stack to point to the previous + // block in the chain. + + uiBlkAddr = pStack->pBlkHdr->stdBlkHdr.ui32PrevBlkInChain; + flmAssert( uiBlkAddr); + + // Fetch the new block + + if( RC_BAD( rc = m_pBlockMgr->getBlock( uiBlkAddr, &pPrevBlock))) + { + goto Exit; + } + + // Release the old block + + pStack->pBlock->Release(); + pBlkHdr = (F_BTREE_BLK_HDR *)pPrevBlock->getBlockPtr(); + pStack->pBlock = pPrevBlock; + pPrevBlock = NULL; + pStack->pBlkHdr = pBlkHdr; + pStack->ui32BlkAddr = ((F_BLK_HDR *)pBlkHdr)->ui32BlkAddr; + pStack->uiCurOffset = pBlkHdr->ui16NumKeys - 1; // Last Entry + pStack->uiLevel = pBlkHdr->ui8BlkLevel; + pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); + } + else + { + // We have no previous. + + rc = RC_SET( NE_FLM_BOF_HIT); + goto Exit; + } + } + else + { + pStack->uiCurOffset--; // Previous Entry + break; + } + + pStack++; + } + +Exit: + + if( pPrevBlock) + { + pPrevBlock->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Method to rebuild the stack so that it references the parentage of + the parameter pBlock. The assumption is that we will begin at + whatever level m_pStack is currently sitting at. Therefore, this + method can be called for any level in the Btree. +****************************************************************************/ +RCODE F_Btree::moveStackToNext( + IF_Block * pBlock, + FLMBOOL bReleaseCurrent) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiBlkAddr; + F_BTREE_BLK_HDR * pBlkHdr; + F_BTSK * pStack = m_pStack; + IF_Block * pNextBlock = NULL; + + if( pBlock) + { + if( pStack->pBlock) + { + // Make sure the block we passed in really is the next in chain. + + if( ((F_BLK_HDR *)pBlock->getBlockPtr())->ui32BlkAddr != + ((F_BLK_HDR *)pStack->pBlock->getBlockPtr())->ui32NextBlkInChain) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + // Cannot be the same block. + + if( pBlock == pStack->pBlock) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + // Release the current block. We don't need to fetch + // the new block because it was passed in to us. If + // we encounter this situation further up the tree, + // we will have to fetch the block as well. + + if( bReleaseCurrent) + { + pStack->pBlock->Release(); + } + } + + pStack->pBlock = pBlock; + pBlkHdr = (F_BTREE_BLK_HDR *)pBlock->getBlockPtr(); + pStack->pBlkHdr = pBlkHdr; + pStack->ui32BlkAddr = ((F_BLK_HDR *)pBlkHdr)->ui32BlkAddr; + pStack->uiCurOffset = 0; // First entry + pStack->uiLevel = pBlkHdr->ui8BlkLevel; + pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); + + // Now walk up the stack until done. + + pStack++; + } + + for (;;) + { + // If we don't currently have this block, let's get it. + + if( pStack->pBlock == NULL) + { + if( RC_BAD( rc = m_pBlockMgr->getBlock( pStack->ui32BlkAddr, + &pStack->pBlock))) + { + goto Exit; + } + + pStack->pBlkHdr = (F_BTREE_BLK_HDR *)pStack->pBlock->getBlockPtr(); + } + + // See if we need to go to the next block. + + if( pStack->uiCurOffset == (FLMUINT)(pStack->pBlkHdr->ui16NumKeys - 1)) + { + // If this is the root block and we are looking at the last entry in the + // block, then we have a problem. + + if( !isRootBlk( pStack->pBlkHdr)) + { + // When the stack is pointing to the last entry, this + // means that we want the target stack to point the next block in + // the chain. + + uiBlkAddr = pStack->pBlkHdr->stdBlkHdr.ui32NextBlkInChain; + flmAssert( uiBlkAddr); + + // Get the next block + + if( RC_BAD( rc = getNextBlock( &pStack->pBlock))) + { + goto Exit; + } + + pBlkHdr = (F_BTREE_BLK_HDR *)pStack->pBlock->getBlockPtr(); + pStack->pBlkHdr = pBlkHdr; + pStack->ui32BlkAddr = ((F_BLK_HDR *)pBlkHdr)->ui32BlkAddr; + pStack->uiCurOffset = 0; // First Entry + pStack->uiLevel = pBlkHdr->ui8BlkLevel; + pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); + } + else + { + // We should never have to attempt to get a previous block + // on the root. + + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + } + else + { + // Move to the next entry + + pStack->uiCurOffset++; + break; + } + + pStack++; + } + +Exit: + + if( pNextBlock) + { + pNextBlock->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Method to calculate the actual entry size of a new entry +****************************************************************************/ +RCODE F_Btree::calcNewEntrySize( + FLMUINT uiKeyLen, + FLMUINT uiDataLen, + FLMUINT * puiEntrySize, + FLMBOOL * pbHaveRoom, + FLMBOOL * pbDefragBlk) +{ + RCODE rc = NE_FLM_OK; + + // Calculate the entry size. + + switch( m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType) + { + case BT_LEAF: + { + // This block type is a leaf block, No Data + + *puiEntrySize = BTE_LEAF_OVHD + uiKeyLen; + break; + } + + case BT_LEAF_DATA: + { + // Leaf block with data + + *puiEntrySize = BTE_LEAF_DATA_OVHD + + (uiKeyLen > ONE_BYTE_SIZE ? 2 : 1) + + (uiDataLen > ONE_BYTE_SIZE ? 2 : 1) + + uiKeyLen + uiDataLen; + break; + } + + case BT_NON_LEAF: + { + *puiEntrySize = BTE_NON_LEAF_OVHD + uiKeyLen; + break; + } + + case BT_NON_LEAF_COUNTS: + { + *puiEntrySize = BTE_NON_LEAF_COUNTS_OVHD + uiKeyLen; + break; + } + + default: + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + *puiEntrySize = 0; + goto Exit; + } + } + + // See if we have room in the heap first. If not, maybe we can make + // room by defraging the block. + + if( *puiEntrySize <= m_pStack->pBlkHdr->ui16HeapSize) + { + *pbDefragBlk = FALSE; + *pbHaveRoom = TRUE; + } + else if( *puiEntrySize <= m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail) + { + // A defrag of the block is required to make room. We will only defrag + // if we can recover a minimum of 5% of the total block size. + + if( m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail >= m_uiDefragThreshold) + { + *pbHaveRoom = TRUE; + *pbDefragBlk = TRUE; + } + else + { + *pbHaveRoom = FALSE; + *pbDefragBlk = FALSE; + } + } + else + { + *pbHaveRoom = FALSE; + *pbDefragBlk = FALSE; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Function to save the replacement information that we could not store + on the current go round. The replace function will check for the + presence of this structure and deal with it later. +****************************************************************************/ +RCODE F_Btree::saveReplaceInfo( + const FLMBYTE * pucNewKey, + FLMUINT uiNewKeyLen) +{ + RCODE rc = NE_FLM_OK; + BTREE_REPLACE_STRUCT * pPrev; + F_BTSK * pStack = m_pStack; + const FLMBYTE * pucParentKey; + FLMBYTE * pucEntry; + + if( m_uiReplaceLevels + 1 >= BH_MAX_LEVELS) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + pPrev = m_pReplaceInfo; + m_pReplaceInfo = &m_pReplaceStruct[ m_uiReplaceLevels++]; + m_pReplaceInfo->pPrev = (void *)pPrev; + + // We should not be at the root level already! + + flmAssert( pStack->uiLevel != m_uiStackLevels - 1); + + m_pReplaceInfo->uiParentLevel = pStack->uiLevel+1; + m_pReplaceInfo->uiNewKeyLen = uiNewKeyLen; + m_pReplaceInfo->uiChildBlkAddr = pStack->ui32BlkAddr; + + if( m_bCounts) + { + m_pReplaceInfo->uiCounts = countKeys( (FLMBYTE *)pStack->pBlkHdr); + } + else + { + m_pReplaceInfo->uiCounts = 0; + } + + f_memcpy( &m_pReplaceInfo->pucNewKey[0], pucNewKey, uiNewKeyLen); + + pStack++; + pucEntry = BtEntry( (FLMBYTE *)pStack->pBlkHdr, pStack->uiCurOffset); + + m_pReplaceInfo->uiParentKeyLen = getEntryKeyLength( pucEntry, + pStack->pBlkHdr->stdBlkHdr.ui8BlkType, &pucParentKey); + + f_memcpy( &m_pReplaceInfo->pucParentKey[0], pucParentKey, + m_pReplaceInfo->uiParentKeyLen); + + m_pReplaceInfo->uiParentChildBlkAddr = bteGetBlkAddr( pucEntry); + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Method to restore the stack to a state where we can finish updating + the parent with the new key information. +****************************************************************************/ +RCODE F_Btree::restoreReplaceInfo( + const FLMBYTE ** ppucKey, + FLMUINT * puiKeyLen, + FLMUINT * puiChildBlkAddr, + FLMUINT * puiCounts) +{ + RCODE rc = NE_FLM_OK; + RCODE rcTmp = NE_FLM_OK; + FLMUINT uiLoop; + FLMBYTE * pucEntry; + FLMUINT uiKeyLen; + const FLMBYTE * pucKey; + FLMUINT uiSearchLevel = m_uiSearchLevel; + FLMUINT uiStackLevels = m_uiStackLevels; + + // We will need to redo our stack from the top down to + // make sure we are looking at the correct blocks. + + m_uiSearchLevel = m_uiStackLevels - m_pReplaceInfo->uiParentLevel - 1; + rcTmp = findEntry( m_pReplaceInfo->pucParentKey, + m_pReplaceInfo->uiParentKeyLen, FLM_EXACT); + + m_uiSearchLevel = uiSearchLevel; + + if ((rcTmp != NE_FLM_OK) && + (rcTmp != NE_FLM_NOT_FOUND) && + (rcTmp != NE_FLM_EOF_HIT)) + { + rc = RC_SET( rcTmp); + goto Exit; + } + + // Set the stack pointer to the parent level that we want to replace. + + m_pStack = &m_Stack[ m_pReplaceInfo->uiParentLevel]; + + // There is always the possibility that the key we are searching for + // has a duplicate key ahead of it, as a result of a continuation element. + // We really must replace the entry we were looking at when the information + // was stored, therefore, we will verify that we have the right entry. + + for( ;;) + { + pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); + + uiKeyLen = getEntryKeyLength( pucEntry, + m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType, &pucKey); + + if( uiKeyLen != m_pReplaceInfo->uiParentKeyLen) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + if( f_memcmp( &m_pReplaceInfo->pucParentKey[0], pucKey, uiKeyLen) == 0) + { + if( bteGetBlkAddr( pucEntry) != m_pReplaceInfo->uiParentChildBlkAddr) + { + // Try moving forward to the next entry ... + + if( RC_BAD( rc = moveStackToNext( NULL))) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + } + else + { + break; + } + } + else + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + } + + // Now return the other important stuff + + *puiChildBlkAddr = m_pReplaceInfo->uiChildBlkAddr; + *puiKeyLen = m_pReplaceInfo->uiNewKeyLen; + *puiCounts = m_pReplaceInfo->uiCounts; + + for( uiLoop = 0; uiLoop < m_uiStackLevels; uiLoop++) + { + m_Stack[ uiLoop].uiKeyLen = m_pReplaceInfo->uiNewKeyLen; + } + + m_uiStackLevels = uiStackLevels; + + // Point to the key + + *ppucKey = &m_pReplaceInfo->pucNewKey[ 0]; + + // Free the current ReplaceInfo Buffer + + m_pReplaceInfo = (BTREE_REPLACE_STRUCT *)m_pReplaceInfo->pPrev; + m_uiReplaceLevels--; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Method to set the key to be returned to the caller. +****************************************************************************/ +FINLINE RCODE F_Btree::setReturnKey( + FLMBYTE * pucEntry, + FLMUINT uiBlockType, + FLMBYTE * pucKey, + FLMUINT * puiKeyLen, + FLMUINT uiKeyBufSize) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiKeyLen; + const FLMBYTE * pucKeyRV; + + uiKeyLen = getEntryKeyLength( pucEntry, uiBlockType, &pucKeyRV); + + if( uiKeyLen == 0) + { + // We hit the LEM, hence the EOF error + + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + + if( uiKeyLen <= uiKeyBufSize) + { + f_memcpy( pucKey, pucKeyRV, uiKeyLen); + *puiKeyLen = uiKeyLen; + } + else + { + rc = RC_SET( NE_FLM_CONV_DEST_OVERFLOW); + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Method to return the data from either the BTREE block or + the DO block. It will update the tracking variables too. + This method assumes that the m_pBlock has already been setup for + the 1st go-round. +****************************************************************************/ +RCODE F_Btree::extractEntryData( + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMBYTE * pucBuffer, + FLMUINT uiBufSiz, + FLMUINT * puiDataLen) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucDestPtr = pucBuffer; + FLMUINT32 ui32BlkAddr = 0; + FLMBOOL bNewBlock; + FLMUINT uiDataLen = 0; + FLMBYTE * pucBlk = NULL; + + flmAssert( m_pBlock); + + if( puiDataLen) + { + *puiDataLen = 0; + } + +#ifdef FLM_DEBUG + if( pucBuffer) + { + f_memset( pucBuffer, 0, uiBufSiz); + } +#endif + + // Is there anything to read? + + if( m_uiOADataRemaining == 0) + { + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + + while( m_uiOADataRemaining && (uiDataLen < uiBufSiz)) + { + if( m_uiDataRemaining <= (uiBufSiz - uiDataLen)) + { + // Let's take what we have left in this block first. + + if( pucDestPtr) + { + f_memcpy( pucDestPtr, m_pucDataPtr, m_uiDataRemaining); + pucDestPtr += m_uiDataRemaining; + } + + uiDataLen += m_uiDataRemaining; + m_uiOADataRemaining -= m_uiDataRemaining; + m_uiDataRemaining = 0; + } + else + { + // Buffer is too small to hold everything in this block. + + if( pucDestPtr) + { + f_memcpy( pucDestPtr, m_pucDataPtr, uiBufSiz - uiDataLen); + pucDestPtr += (uiBufSiz - uiDataLen); + } + + m_pucDataPtr += (uiBufSiz - uiDataLen); + m_uiOADataRemaining -= (uiBufSiz - uiDataLen); + m_uiDataRemaining -= (uiBufSiz - uiDataLen); + uiDataLen += (uiBufSiz - uiDataLen); + } + + // If there is still more overall data remaining, we need to get the + // next DO block or standard block and setup to read it too. + // i.e. More to come, but nothing left in this block. + + if( (m_uiOADataRemaining > 0) && (m_uiDataRemaining == 0)) + { + if (!m_bDataOnlyBlock && + (m_uiCurOffset < + (FLMUINT)(((F_BTREE_BLK_HDR *)m_pBlock->getBlockPtr())->ui16NumKeys - 1))) + { + m_uiCurOffset++; + bNewBlock = FALSE; + } + else + { + // Get the next block address + + ui32BlkAddr = ((F_BLK_HDR *)m_pBlock->getBlockPtr())->ui32NextBlkInChain; + + // Release the current block before we get the next one. + + m_pBlock->Release(); + m_pBlock = NULL; + + if( ui32BlkAddr == 0) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + if( RC_BAD( rc = m_pBlockMgr->getBlock( ui32BlkAddr, &m_pBlock))) + { + goto Exit; + } + + bNewBlock = TRUE; + } + + // If this is a data only block, then we can get the local data size + // from the header. + + if( m_bDataOnlyBlock) + { + pucBlk = m_pBlock->getBlockPtr(); + + flmAssert( ((F_BLK_HDR *)pucBlk)->ui8BlkType == BT_DATA_ONLY); + + m_pucDataPtr = pucBlk + sizeofDOBlkHdr( (F_BLK_HDR *)pucBlk); + + m_uiDataRemaining = m_uiBlockSize - + sizeofDOBlkHdr( (F_BLK_HDR *)pucBlk) - + ((F_BLK_HDR *)pucBlk)->ui16BlkBytesAvail; + + m_uiDataLength = m_uiDataRemaining; + m_ui32CurBlkAddr = ui32BlkAddr; + } + else + { + F_BTREE_BLK_HDR * pBlkHdr; + FLMBYTE * pucEntry; + + // In a BTREE block, we MUST ensure that the first entry is a + // continuation of the previous entry in the previous block. + + pBlkHdr = (F_BTREE_BLK_HDR *)m_pBlock->getBlockPtr(); + + if( pBlkHdr->ui16NumKeys == 0) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + if( bNewBlock) + { + m_uiCurOffset = 0; + } + + // Point to the first entry ... + + pucEntry = BtEntry( (FLMBYTE *)pBlkHdr, m_uiCurOffset); + + if( !checkContinuedEntry( pucKey, uiKeyLen, NULL, pucEntry, + pBlkHdr->stdBlkHdr.ui8BlkType)) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + m_uiDataRemaining = btGetEntryDataLength( pucEntry, + &m_pucDataPtr, NULL, NULL); + + m_uiDataLength = m_uiDataRemaining; + + if( bNewBlock) + { + m_ui32CurBlkAddr = ui32BlkAddr; + } + } + + // Update the offset at the begining of the current entry. + + m_uiOffsetAtStart = m_uiOADataLength - m_uiOADataRemaining; + } + } + +Exit: + + if( puiDataLen) + { + *puiDataLen = uiDataLen; + } + + if( m_pBlock) + { + m_pBlock->Release(); + m_pBlock = NULL; + } + + return( rc); +} + +/*************************************************************************** +Desc: Method to prepare the Btree state for reading. Since several APIs do + the same thing, this has been put into a private method. +****************************************************************************/ +RCODE F_Btree::setupReadState( + F_BLK_HDR * pBlkHdr, + FLMBYTE * pucEntry) +{ + RCODE rc = NE_FLM_OK; + IF_Block * pBlock = NULL; + const FLMBYTE * pucData; + FLMBYTE * pucBlk = NULL; + + // Is there any data? Check the block type. + + if( pBlkHdr->ui8BlkType == BT_LEAF_DATA) + { + // How large is the value for this entry? + + m_uiDataLength = btGetEntryDataLength( pucEntry, &pucData, + &m_uiOADataLength, &m_bDataOnlyBlock); + + m_uiPrimaryDataLen = m_uiDataLength; + } + else + { + m_uiDataLength = 0; + m_uiOADataLength = 0; + m_bDataOnlyBlock = FALSE; + } + + // Represents the offset at the beginning entry in the first block. This + // will change as we move through the blocks. + + m_uiOffsetAtStart = 0; + + // Track the overall length progress + + m_uiOADataRemaining = m_uiOADataLength; + + // Track the local entry progress + + m_uiDataRemaining = m_uiDataLength; + + if( m_bDataOnlyBlock) + { + m_ui32DOBlkAddr = bteGetBlkAddr( pucData); + m_ui32CurBlkAddr = m_ui32DOBlkAddr; + + if( RC_BAD( rc = m_pBlockMgr->getBlock( m_ui32DOBlkAddr, &pBlock))) + { + goto Exit; + } + + // Local amount of data in this block + + pucBlk = pBlock->getBlockPtr(); + + m_uiDataRemaining = m_uiBlockSize - + sizeofDOBlkHdr( (F_BLK_HDR *)pucBlk) - + ((F_BLK_HDR *)pucBlk)->ui16BlkBytesAvail; + + // Keep the actual local data size for later. + + m_uiDataLength = m_uiDataRemaining; + + // Adjust for the key at the beginning of the first block. + + if( ((F_BLK_HDR *)pucBlk)->ui32PrevBlkInChain == 0) + { + FLMBYTE * pucPtr = pucBlk + sizeofDOBlkHdr((F_BLK_HDR *)pucBlk); + FLMUINT16 ui16KeyLen = FB2UW( pucPtr); + + m_uiDataLength -= (ui16KeyLen + 2); + m_uiDataRemaining -= (ui16KeyLen + 2); + } + + // Now release the DO Block. We will get it again when we need it. + + pBlock->Release(); + pBlock = NULL; + } + +Exit: + + if( pBlock) + { + pBlock->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Method to remove extra entries after a replace operation. +****************************************************************************/ +RCODE F_Btree::removeRemainingEntries( + const FLMBYTE * pucKey, + FLMUINT uiKeyLen) +{ + RCODE rc = NE_FLM_OK; + F_BTREE_BLK_HDR * pBlkHdr; + FLMBOOL bLastElement = FALSE; + FLMBYTE * pucEntry; + FLMBOOL bFirst = TRUE; + + // We should never get to this function when in the upper levels. + + flmAssert( m_pStack->uiLevel == 0); + + // If we do not have a stack setup yet (which can happen if the replace + // is trying to shortcut to the previously known block address and offset), + // then at this point, we must build the stack, since it may be required + // to adjust the upper levels of the btree. + + if( !m_bStackSetup) + { + if( RC_BAD( rc = findEntry( pucKey, uiKeyLen, FLM_EXACT))) + { + goto Exit; + } + } + + while( !bLastElement) + { + // Begin each iteration at the leaf level. + + m_pStack = &m_Stack[ 0]; + + // Advance the stack to the next entry. + + if (bFirst || + m_pStack->uiCurOffset >= (FLMUINT)(m_pStack->pBlkHdr->ui16NumKeys)) + { + if( RC_BAD( rc = moveStackToNext( NULL))) + { + goto Exit; + } + } + + bFirst = FALSE; + pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); + + if( !checkContinuedEntry( pucKey, uiKeyLen, &bLastElement, + pucEntry, getBlkType( (FLMBYTE *)m_pStack->pBlkHdr))) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + // Remove the entry from this block. + + if( RC_BAD( rc = remove( FALSE))) + { + goto Exit; + } + + pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pBlock->getBlockPtr(); + + // Is the block empty now? If it is, then we will want to remove this + // block and remove the entry in the parent that points to this block. + + if( pBlkHdr->ui16NumKeys == 0) + { + for (;;) + { + flmAssert( !isRootBlk( m_pStack->pBlkHdr)); + + // Remove this block, then update the parent. + + if( RC_BAD( rc = deleteEmptyBlock())) + { + goto Exit; + } + + // Now update the parent blocks + + m_pStack++; + + if( RC_BAD( rc = remove( FALSE))) + { + goto Exit; + } + + // Update the counts if keeping counts. + + if( m_bCounts && !isRootBlk(pBlkHdr)) + { + if( RC_BAD( rc = updateCounts())) + { + goto Exit; + } + } + + if( m_pStack->pBlkHdr->ui16NumKeys > 0) + { + break; + } + } + + // Rebuild the stack to the beginning after a delete block operation. + + if( RC_BAD( findEntry( pucKey, uiKeyLen, FLM_EXACT))) + { + goto Exit; + } + + bFirst = TRUE; + } + else + { + // Update the counts if keeping counts. + + if( m_bCounts) + { + if( RC_BAD( rc = updateCounts())) + { + goto Exit; + } + } + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Method to delete an empty block. The block that will be deleted is + the current block pointed to by m_pStack. +****************************************************************************/ +RCODE F_Btree::deleteEmptyBlock( void) +{ + RCODE rc = NE_FLM_OK; + FLMUINT32 ui32PrevBlkAddr; + FLMUINT32 ui32NextBlkAddr; + IF_Block * pBlock = NULL; + + // Get the previous block address so we can back everything up in the stack + + ui32PrevBlkAddr = m_pStack->pBlkHdr->stdBlkHdr.ui32PrevBlkInChain; + ui32NextBlkAddr = m_pStack->pBlkHdr->stdBlkHdr.ui32NextBlkInChain; + + // Free the block + + rc = m_pBlockMgr->freeBlock( &m_pStack->pBlock); + m_pStack->pBlkHdr = NULL; + + if( RC_BAD( rc)) + { + goto Exit; + } + + // Update the previous block. + + if( ui32PrevBlkAddr) + { + if( RC_BAD( rc = m_pBlockMgr->getBlock( ui32PrevBlkAddr, &pBlock))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &pBlock))) + { + goto Exit; + } + + ((F_BLK_HDR *)pBlock->getBlockPtr())->ui32NextBlkInChain = ui32NextBlkAddr; + + pBlock->Release(); + pBlock = NULL; + } + + // Update the next block + + if( ui32NextBlkAddr) + { + if( RC_BAD( rc = m_pBlockMgr->getBlock( ui32NextBlkAddr, &pBlock))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &pBlock))) + { + goto Exit; + } + + ((F_BLK_HDR *)pBlock->getBlockPtr())->ui32PrevBlkInChain = ui32PrevBlkAddr; + + pBlock->Release(); + pBlock = NULL; + } + +Exit: + + if( pBlock) + { + pBlock->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Method to remove (free) all data only blocks that are linked to the + data only block whose address is passed in (inclusive). +****************************************************************************/ +RCODE F_Btree::removeDOBlocks( + FLMUINT32 ui32BlkAddr) +{ + RCODE rc = NE_FLM_OK; + FLMUINT32 ui32NextBlkAddr; + IF_Block * pBlock = NULL; + + ui32NextBlkAddr = ui32BlkAddr; + + while( ui32NextBlkAddr) + { + if( RC_BAD( rc = m_pBlockMgr->getBlock( ui32NextBlkAddr, &pBlock))) + { + goto Exit; + } + + flmAssert( getBlkType( pBlock->getBlockPtr()) == BT_DATA_ONLY); + ui32NextBlkAddr = ((F_BLK_HDR *)pBlock->getBlockPtr())->ui32NextBlkInChain; + + if( RC_BAD( rc = m_pBlockMgr->freeBlock( &pBlock))) + { + goto Exit; + } + } + +Exit: + + if( pBlock) + { + pBlock->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Method used to replace entries where the original spans multiple + elements and we are NOT to truncate it. To do this, we will attempt + to fill each block until we have stored everything. +****************************************************************************/ +RCODE F_Btree::replaceMultiples( + const FLMBYTE ** ppucKey, + FLMUINT * puiKeyLen, + const FLMBYTE * pucDataValue, + FLMUINT uiLen, + FLMUINT, //uiFlags, + FLMUINT *, //puiChildBlkAddr, + FLMUINT *, //puiCounts, + const FLMBYTE ** ppucRemainingValue, + FLMUINT * puiRemainingLen, + F_ELM_UPD_ACTION * peAction) +{ + RCODE rc = NE_FLM_OK; + FLMBOOL bLastElement = FALSE; + FLMUINT uiRemainingData = uiLen; + const FLMBYTE * pucRemainingValue = pucDataValue; + FLMBYTE * pucEntry = NULL; + FLMBYTE * pucData; + FLMUINT uiDataLength; + FLMUINT uiOADataLength = uiLen; + FLMUINT uiOldOADataLength; + FLMUINT uiAmtCopied; + + // Must be at the leaf level! + + flmAssert( m_pStack->uiLevel == 0); + + while( uiRemainingData) + { + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &m_pStack->pBlock))) + { + goto Exit; + } + + m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pBlock->getBlockPtr(); + m_pStack->pui16OffsetArray = BtOffsetArray( + (FLMBYTE *)m_pStack->pBlkHdr, 0); + + // Get a pointer to the current entry + + pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); + + // Determine the data size for this entry + + uiDataLength = btGetEntryDataLength( pucEntry, (const FLMBYTE **)&pucData, + &uiOldOADataLength, NULL); + + // Now over-write as much of the data as we can + + if( uiRemainingData >= uiDataLength) + { + f_memcpy( pucData, pucRemainingValue, uiDataLength); + + uiAmtCopied = uiDataLength; + pucRemainingValue += uiDataLength; + uiRemainingData -= uiDataLength; + } + else + { + f_memcpy( pucData, pucRemainingValue, uiRemainingData); + uiAmtCopied = uiRemainingData; + pucRemainingValue += uiRemainingData; + uiRemainingData = 0; + } + + // Do we need to adjust the data length? + + if( uiDataLength > uiAmtCopied) + { + FLMBYTE * pucTmp = pucEntry; + + // Skip the flag + + pucTmp++; + + if( bteKeyLenFlag( pucEntry)) + { + pucTmp += 2; + } + else + { + pucTmp++; + } + + if( bteDataLenFlag( pucEntry)) + { + UW2FBA( uiAmtCopied, pucTmp); + pucTmp += 2; + } + else + { + *pucTmp = (FLMBYTE)uiAmtCopied; + pucTmp++; + } + + // We need to adjust the free space in the block too. + + m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail += + (FLMUINT16)(uiDataLength - uiAmtCopied); + + +#ifdef FLM_DEBUG + // Clear the unused portion of the block now. + + pucTmp = pucData + uiAmtCopied; + f_memset( pucTmp, 0, (uiDataLength - uiAmtCopied)); +#endif + } + + // Adjust the OA Data length if needed. We only need to worry about this + // on the first element. No others have it. + + if( bteFirstElementFlag( pucEntry) && uiOADataLength != uiOldOADataLength) + { + FLMBYTE * pucTmp = pucEntry; + + flmAssert( bteOADataLenFlag( pucEntry)); + + pucTmp++; + + if( bteKeyLenFlag( pucEntry)) + { + pucTmp += 2; + } + else + { + pucTmp++; + } + + if( bteDataLenFlag( pucEntry)) + { + pucTmp += 2; + } + else + { + pucTmp++; + } + + UD2FBA( uiOADataLength, pucTmp); + } + + // If we just updated the last member of this entry so break out. + + if( uiRemainingData == 0) + { + break; + } + + // Was this the last element for this entry? + + if( bteLastElementFlag(pucEntry)) + { + FLMBYTE * pucTmp = pucEntry; + + // Turn off the lastElement flag on this entry. + + *pucTmp &= ~BTE_FLAG_LAST_ELEMENT; + + // No more to replace, the rest is going to be new data. + + *ppucRemainingValue = pucRemainingValue; + *puiRemainingLen = uiRemainingData; + break; + } + + // Advance to the next entry, this block or the next... + // The function expects to find the block in m_pBlock, so + // let's put it there for now. + + if( RC_BAD( rc = moveStackToNext( NULL))) + { + goto Exit; + } + + pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, + m_pStack->uiCurOffset); + + // Make sure we are still looking at the same key etc. + + if( !checkContinuedEntry( *ppucKey, *puiKeyLen, &bLastElement, + pucEntry, getBlkType( (FLMBYTE *)m_pStack->pBlkHdr))) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + } + + // Are there any more entries to remove? + + if( !bteLastElementFlag( pucEntry) && !uiRemainingData) + { + *pucEntry |= BTE_FLAG_LAST_ELEMENT; + + if( RC_BAD( rc = removeRemainingEntries( *ppucKey, *puiKeyLen))) + { + goto Exit; + } + } + + *peAction = ELM_DONE; + +Exit: + + if( m_pBlock) + { + m_pBlock->Release(); + m_pBlock = NULL; + } + + return( rc); +} + +/*************************************************************************** +Desc: Method used to replace entries where the original spans multiple + elements and we are not to truncate it. To do this, we will attempt + to fill each block until we have stored everything. +****************************************************************************/ +RCODE F_Btree::replaceMultiNoTruncate( + const FLMBYTE ** ppucKey, + FLMUINT * puiKeyLen, + const FLMBYTE * pucDataValue, + FLMUINT uiLen, + FLMUINT, //uiFlags, + FLMUINT *, //puiChildBlkAddr, + FLMUINT *, //puiCounts, + const FLMBYTE ** ppucRemainingValue, + FLMUINT * puiRemainingLen, + F_ELM_UPD_ACTION * peAction) +{ + RCODE rc = NE_FLM_OK; + FLMBOOL bLastElement = FALSE; + FLMUINT uiRemainingData = uiLen; + const FLMBYTE * pucRemainingValue = pucDataValue; + FLMBYTE * pucEntry; + FLMBYTE * pucData; + FLMUINT uiDataLength; + + // Must be at the leaf level + + flmAssert( m_pStack->uiLevel == 0); + + while( uiRemainingData) + { + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &m_pStack->pBlock))) + { + goto Exit; + } + + m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pBlock->getBlockPtr(); + m_pStack->pui16OffsetArray = BtOffsetArray( + (FLMBYTE *)m_pStack->pBlkHdr, 0); + + // Get a pointer to the current entry + + pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); + + // Determine the data size for this entry + + uiDataLength = btGetEntryDataLength( pucEntry, + (const FLMBYTE **)&pucData, NULL, NULL); + + // Now over-write as much of the data as we can. + + if( uiRemainingData > uiDataLength) + { + f_memcpy( pucData, pucRemainingValue, uiDataLength); + pucRemainingValue += uiDataLength; + uiRemainingData -= uiDataLength; + } + else + { + f_memcpy( pucData, pucRemainingValue, uiRemainingData); + pucRemainingValue += uiRemainingData; + uiRemainingData = 0; + } + + // We just updated the last member of this entry so break out. + + if( uiRemainingData == 0) + { + break; + } + + // Was this the last element for this entry? + + if( bteLastElementFlag( pucEntry)) + { + // No more to replace, the rest is going to be new data. + + *ppucRemainingValue = pucRemainingValue; + *puiRemainingLen = uiRemainingData; + break; + } + + // Advance to the next entry, this block or the next... + // The function expects to find the block in m_pBlock, so + // let's put it there for now. + + if( RC_BAD( rc = moveStackToNext( NULL))) + { + goto Exit; + } + + pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, + m_pStack->uiCurOffset); + + // Make sure we are still looking at the same key etc. + + if( !checkContinuedEntry( *ppucKey, *puiKeyLen, &bLastElement, + pucEntry, getBlkType( (FLMBYTE *)m_pStack->pBlkHdr))) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + } + + *peAction = ELM_DONE; + +Exit: + + // Only release the m_pBlock if the use count is greater than 1. It is + // pointed to by the stack also. + + if( m_pBlock) + { + m_pBlock->Release(); + m_pBlock = NULL; + } + + return( rc); +} + +/*************************************************************************** +Desc: Private method to retrieve the next block in the chain relative to + the block that is passed in. The block that is passed in is always + released prior to getting the next block. +****************************************************************************/ +FINLINE RCODE F_Btree::getNextBlock( + IF_Block ** ppBlock) +{ + RCODE rc = NE_FLM_OK; + FLMUINT32 ui32BlkAddr; + + ui32BlkAddr = ((F_BLK_HDR *)(*ppBlock)->getBlockPtr())->ui32NextBlkInChain; + + (*ppBlock)->Release(); + *ppBlock = NULL; + + if( ui32BlkAddr == 0) + { + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + + if( RC_BAD( rc = m_pBlockMgr->getBlock( ui32BlkAddr, ppBlock))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Private method to retrieve the previous block in the chain relative to + the block that is passed in. The block that is passed in is always + released prior to getting the previous block. +****************************************************************************/ +FINLINE RCODE F_Btree::getPrevBlock( + IF_Block ** ppBlock) +{ + RCODE rc = NE_FLM_OK; + FLMUINT32 ui32BlkAddr; + + ui32BlkAddr = ((F_BLK_HDR *)(*ppBlock)->getBlockPtr())->ui32PrevBlkInChain; + + (*ppBlock)->Release(); + *ppBlock = NULL; + + if( ui32BlkAddr == 0) + { + rc = RC_SET( NE_FLM_BOF_HIT); + goto Exit; + } + + if( RC_BAD( rc = m_pBlockMgr->getBlock( ui32BlkAddr, ppBlock))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Private method to verify that the entry we are looking at in the stack + is a continuation entry. The key must match the key we pass in and + the entry must be marked as a continuation, i.e. not the first + element. +****************************************************************************/ +FLMBOOL F_Btree::checkContinuedEntry( + const FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMBOOL * pbLastElement, + FLMBYTE * pucEntry, + FLMUINT uiBlkType) +{ + FLMBOOL bOk = TRUE; + FLMUINT uiBlkKeyLen; + const FLMBYTE * pucBlkKey; + + if( pbLastElement) + { + *pbLastElement = bteLastElementFlag( pucEntry); + } + + uiBlkKeyLen = getEntryKeyLength( pucEntry, uiBlkType, &pucBlkKey); + + // Must be the same size key! + + if( uiKeyLen != uiBlkKeyLen) + { + bOk = FALSE; + goto Exit; + } + + // Must be identical! + + if( f_memcmp( pucKey, pucBlkKey, uiKeyLen) != 0) + { + bOk = FALSE; + goto Exit; + } + + // Must not be the first element! + + if( bteFirstElementFlag( pucEntry)) + { + bOk = FALSE; + goto Exit; + } + +Exit: + + return( bOk); +} + +/*************************************************************************** +Desc: Private method to assend the tree, updating the counts for a + particular block. This method allows us to update the counts quickly + without the need to continually loop, replacing existing keys with + new counts. +****************************************************************************/ +RCODE F_Btree::updateCounts( void) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiLevel; + + for( uiLevel = m_pStack->uiLevel; + uiLevel < m_uiStackLevels - 1; + uiLevel++) + { + if( RC_BAD( rc = updateParentCounts( m_Stack[ uiLevel].pBlock, + &m_Stack[ uiLevel + 1].pBlock, m_Stack[ uiLevel + 1].uiCurOffset))) + { + goto Exit; + } + + m_Stack[ uiLevel + 1].pBlkHdr = + (F_BTREE_BLK_HDR *)m_Stack[ uiLevel + 1].pBlock->getBlockPtr(); + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Private method to store part of an entry in a block. This method will + determine how much of the data can be stored in the block. The amount + that does not get stored will be returned in ppucRemainingValue and + puiRemainingLen. +****************************************************************************/ +RCODE F_Btree::storePartialEntry( + const FLMBYTE * pucKey, + FLMUINT uiKeyLen, + const FLMBYTE * pucValue, + FLMUINT uiLen, + FLMUINT uiFlags, + FLMUINT uiChildBlkAddr, + FLMUINT uiCounts, + const FLMBYTE ** ppucRemainingValue, + FLMUINT * puiRemainingLen, + FLMBOOL bNewBlock) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiNewDataLen; + FLMUINT uiOADataLen = 0; + FLMUINT uiEntrySize; + FLMBOOL bHaveRoom; + FLMBOOL bDefragBlk; + FLMBOOL bLastEntry; + + if( RC_BAD( rc = calcOptimalDataLength( uiKeyLen, + uiLen, m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail, + &uiNewDataLen))) + { + goto Exit; + } + + if( uiNewDataLen < uiLen) + { + // Turn off the last element flag. + + uiFlags &= ~BTE_FLAG_LAST_ELEMENT; + + if( uiFlags & BTE_FLAG_FIRST_ELEMENT) + { + // Store the overall data length from this point forward. + + uiOADataLen = uiLen; + } + } + + if( RC_BAD( rc = calcNewEntrySize( uiKeyLen, uiNewDataLen, &uiEntrySize, + &bHaveRoom, &bDefragBlk))) + { + goto Exit; + } + + // We will defragment the block first if the avail and heap + // are not the same size. + + if( m_pStack->pBlkHdr->ui16HeapSize != + m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail) + { + if( RC_BAD( rc = defragmentBlock( &m_pStack->pBlock))) + { + goto Exit; + } + } + + if( RC_BAD( rc = storeEntry( pucKey, uiKeyLen, pucValue, uiNewDataLen, + uiFlags, uiOADataLen, uiChildBlkAddr, uiCounts, + uiEntrySize, &bLastEntry))) + { + goto Exit; + } + + // If this block has a parent block, and the btree is maintaining counts + // we will want to update the counts on the parent block. + + if( !isRootBlk( m_pStack->pBlkHdr) && m_bCounts && !bNewBlock) + { + if( RC_BAD( rc = updateCounts())) + { + goto Exit; + } + } + + if( uiNewDataLen < uiLen) + { + // Save the portion of the data that was not written. + // It will be written later. + + *ppucRemainingValue = pucValue + uiNewDataLen; + *puiRemainingLen = uiLen - uiNewDataLen; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Private meethod for checking the down links in the btree to make sure + they are not corrupt. +****************************************************************************/ +RCODE F_Btree::checkDownLinks( void) +{ + RCODE rc = NE_FLM_OK; + IF_Block * pParentBlock = NULL; + F_BLK_HDR * pBlkHdr; + + if( RC_BAD( rc = m_pBlockMgr->getBlock( m_uiRootBlkAddr, &pParentBlock))) + { + goto Exit; + } + + pBlkHdr = ((F_BLK_HDR *)pParentBlock->getBlockPtr()); + + if( (pBlkHdr->ui8BlkType == BT_NON_LEAF) || + (pBlkHdr->ui8BlkType == BT_NON_LEAF_COUNTS)) + { + if( RC_BAD( rc = verifyChildLinks( pParentBlock))) + { + goto Exit; + } + } + +Exit: + + if( pParentBlock) + { + pParentBlock->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Private method (recursive) that checks the child links in the given + blocks to ensure they are correct. +****************************************************************************/ +RCODE F_Btree::verifyChildLinks( + IF_Block * pParentBlock) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiNumKeys; + IF_Block * pChildBlock = NULL; + F_BTREE_BLK_HDR * pParentBlkHdr; + F_BTREE_BLK_HDR * pChildBlkHdr; + FLMUINT uiCurOffset; + FLMBYTE * pucEntry; + FLMUINT32 ui32BlkAddr; + const FLMBYTE * pucParentKey; + FLMBYTE * pucChildEntry; + const FLMBYTE * pucChildKey; + FLMUINT uiParentKeyLen; + FLMUINT uiChildKeyLen; + + pParentBlkHdr = (F_BTREE_BLK_HDR *)pParentBlock->getBlockPtr(); + uiNumKeys = pParentBlkHdr->ui16NumKeys; + + for( uiCurOffset = 0; uiCurOffset < uiNumKeys; uiCurOffset++) + { + pucEntry = BtEntry( (FLMBYTE *)pParentBlkHdr, uiCurOffset); + + // Non-leaf nodes have children. + + ui32BlkAddr = bteGetBlkAddr( pucEntry); + flmAssert( ui32BlkAddr); + + if( RC_BAD( rc = m_pBlockMgr->getBlock( ui32BlkAddr, &pChildBlock))) + { + goto Exit; + } + + pChildBlkHdr = (F_BTREE_BLK_HDR *)pChildBlock->getBlockPtr(); + + // Get key from the parent entry and compare it to the + // last key in the child block. + + uiParentKeyLen = getEntryKeyLength( + pucEntry, pParentBlkHdr->stdBlkHdr.ui8BlkType, &pucParentKey); + + // Get the last entry in the child block. + + pucChildEntry = BtLastEntry( (FLMBYTE *)pChildBlkHdr); + + uiChildKeyLen = getEntryKeyLength( + pucChildEntry, pChildBlkHdr->stdBlkHdr.ui8BlkType, &pucChildKey); + + if( uiParentKeyLen != uiChildKeyLen) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + if( f_memcmp( pucParentKey, pucChildKey, uiParentKeyLen) != 0) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + if( (pChildBlkHdr->stdBlkHdr.ui8BlkType == BT_NON_LEAF) || + (pChildBlkHdr->stdBlkHdr.ui8BlkType == BT_NON_LEAF_COUNTS)) + { + if( RC_BAD( rc = verifyChildLinks( pChildBlock))) + { + goto Exit; + } + } + + pChildBlock->Release(); + pChildBlock = NULL; + } + +Exit: + + if( pChildBlock) + { + pChildBlock->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: This is a private method that computes the number of entries (keys) + and the number of blocks between two points in the Btree. +****************************************************************************/ +RCODE F_Btree::computeCounts( + F_BTSK * pFromStack, + F_BTSK * pUntilStack, + FLMUINT * puiBlockCount, + FLMUINT * puiKeyCount, + FLMBOOL * pbTotalsEstimated, + FLMUINT uiAvgBlkFullness) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiTotalKeys = 0; + FLMUINT uiTempKeyCount = 0; + FLMUINT uiEstKeyCount = 0; + FLMUINT uiTotalBlocksBetween = 0; + FLMUINT uiEstBlocksBetween = 0; + + uiTotalBlocksBetween = 0; + *pbTotalsEstimated = FALSE; + + // The stack that we are looking at does not hold the blocks + // we need. We first need to restore the blocks as needed. + + if( RC_BAD( rc = getCacheBlocks( pFromStack, pUntilStack))) + { + goto Exit; + } + + // Are the from and until positions in the same block? + + if( pFromStack->ui32BlkAddr == pUntilStack->ui32BlkAddr) + { + rc = blockCounts( pFromStack, pFromStack->uiCurOffset, + pUntilStack->uiCurOffset, &uiTotalKeys, NULL); + goto Exit; + } + + // Are we maintaining counts on this Btree? If so, we can just + // use the counts we have... The blocks count may still be estimated. + + if( m_bCounts) + { + return( getStoredCounts( pFromStack, pUntilStack, puiBlockCount, + puiKeyCount, pbTotalsEstimated, uiAvgBlkFullness)); + } + + // Since we are not keeping counts on this Btree, we will need to + // count them and possibly estimate them. + + // Gather the counts in the from and until leaf blocks. + + if( RC_BAD( rc = blockCounts( pFromStack, pFromStack->uiCurOffset, + pFromStack->pBlkHdr->ui16NumKeys - 1, &uiTotalKeys, NULL))) + { + goto Exit; + } + + if( RC_BAD( rc = blockCounts( pUntilStack, 0, + pUntilStack->uiCurOffset, &uiTempKeyCount, NULL))) + { + goto Exit; + } + + uiTotalKeys += uiTempKeyCount; + + // Do the obvious check to see if the blocks are neighbors. If they + // are, we are done. + + if( pFromStack->pBlkHdr->stdBlkHdr.ui32NextBlkInChain == + pUntilStack->ui32BlkAddr) + { + goto Exit; + } + + // Estimate the number of elements in the parent block. + + *pbTotalsEstimated = TRUE; + + uiEstKeyCount = getAvgKeyCount( pFromStack, pUntilStack, uiAvgBlkFullness); + uiEstBlocksBetween = 1; + + for (;;) + { + FLMUINT uiElementCount; + FLMUINT uiTempElementCount; + FLMUINT uiEstElementCount; + + // Go up a b-tree level and check out how far apart the elements are. + + pFromStack++; + pUntilStack++; + + if( RC_BAD( rc = getCacheBlocks( pFromStack, pUntilStack))) + { + goto Exit; + } + + // Share the same block? + + if( pFromStack->ui32BlkAddr == pUntilStack->ui32BlkAddr) + { + if( RC_BAD( rc = blockCounts( pFromStack, pFromStack->uiCurOffset, + pUntilStack->uiCurOffset, NULL, &uiElementCount))) + { + goto Exit; + } + + // Don't count the pFromStack or the pUntilStack current elements. + + uiElementCount -= 2; + + uiTotalBlocksBetween += uiEstBlocksBetween * + (uiElementCount > 0 ? uiElementCount : 1); + uiTotalKeys += uiEstKeyCount * + (uiElementCount > 0 ? uiElementCount : 1); + goto Exit; + } + + // Gather the counts in the from and until non-leaf blocks. + + if( RC_BAD( rc = blockCounts( pFromStack, pFromStack->uiCurOffset, + pFromStack->pBlkHdr->ui16NumKeys - 1, NULL, &uiElementCount))) + { + goto Exit; + } + + // Don't count the first element. + + uiElementCount--; + + if( RC_BAD( rc = blockCounts( pUntilStack, 0, + pUntilStack->uiCurOffset, NULL, &uiTempElementCount))) + { + goto Exit; + } + + uiElementCount += (uiTempElementCount - 1); + + uiTotalBlocksBetween += uiEstBlocksBetween * uiElementCount; + uiTotalKeys += uiEstKeyCount * uiElementCount; + + // Do the obvious check to see if the blocks are neighbors. + + if( (FLMUINT)pFromStack->pBlkHdr->stdBlkHdr.ui32NextBlkInChain == + pUntilStack->ui32BlkAddr) + { + goto Exit; + } + + // Recompute the estimated element count on every b-tree level + // because the compression is better the lower in the b-tree we go. + + uiEstElementCount = getAvgKeyCount( + pFromStack, pUntilStack, uiAvgBlkFullness); + + // Adjust the estimated key/ref count to be the counts from a complete + // (not partial) block starting at this level going to the leaf. + + uiEstKeyCount *= uiEstElementCount; + uiEstBlocksBetween *= uiEstElementCount; + } + +Exit: + + if( puiKeyCount) + { + *puiKeyCount = uiTotalKeys; + } + + if( puiBlockCount) + { + *puiBlockCount = uiTotalBlocksBetween; + } + + return( rc); +} + +/*************************************************************************** +Desc: Private method to count the number of unique keys between two points. + The count returned is inclusive of the first and last offsets. +****************************************************************************/ +RCODE F_Btree::blockCounts( + F_BTSK * pStack, + FLMUINT uiFirstOffset, + FLMUINT uiLastOffset, + FLMUINT * puiKeyCount, + FLMUINT * puiElementCount) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiKeyCount; + FLMUINT uiElementCount; + FLMBYTE * pucBlk; + FLMBYTE * pucEntry; + + // Debug checks. + + flmAssert( uiFirstOffset <= uiLastOffset); + flmAssert( uiLastOffset <= (FLMUINT)(pStack->pBlkHdr->ui16NumKeys - 1)); + + uiKeyCount = uiElementCount = 0; + pucBlk = (FLMBYTE *)pStack->pBlkHdr; + + // Loop gathering the statistics. + + while( uiFirstOffset <= uiLastOffset) + { + uiElementCount++; + + if( puiKeyCount) + { + pucEntry = BtEntry( pucBlk, uiFirstOffset); + + // We only have to worry about first key elements when we are at the + // leaf level and we are keeping data at that level. + + if( pStack->uiLevel == 0 && m_bData) + { + if( bteFirstElementFlag( pucEntry)) + { + uiKeyCount++; + } + } + else + { + uiKeyCount++; + } + } + + // Next element. + + if( uiFirstOffset == (FLMUINT)(pStack->pBlkHdr->ui16NumKeys - 1)) + { + break; + } + else + { + uiFirstOffset++; + } + } + + if( puiKeyCount) + { + *puiKeyCount = uiKeyCount; + } + + if( puiElementCount) + { + *puiElementCount = uiElementCount; + } + + return( rc); +} + +/*************************************************************************** +Desc: Similar to computeCounts, except we use the stored counts. +****************************************************************************/ +RCODE F_Btree::getStoredCounts( + F_BTSK * pFromStack, + F_BTSK * pUntilStack, + FLMUINT * puiBlockCount, + FLMUINT * puiKeyCount, + FLMBOOL * pbTotalsEstimated, + FLMUINT uiAvgBlkFullness) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiOmittedKeys; + FLMUINT uiTotalKeys; + FLMUINT uiEstBlocksBetween; + FLMUINT uiTotalBlocksBetween; + + *pbTotalsEstimated = FALSE; + *puiBlockCount = 0; + uiTotalBlocksBetween = 0; + + // Are these blocks adjacent? + + if( pFromStack->pBlkHdr->stdBlkHdr.ui32NextBlkInChain == + pUntilStack->ui32BlkAddr) + { + *puiKeyCount = (pFromStack->pBlkHdr->ui16NumKeys - + pFromStack->uiCurOffset) + pUntilStack->uiCurOffset + 1; + goto Exit; + } + + *pbTotalsEstimated = TRUE; + + // How many keys are excluded in the From and Until blocks? + + uiOmittedKeys = countRangeOfKeys( + pFromStack, 0, pFromStack->uiCurOffset) - 1; + + uiOmittedKeys += countRangeOfKeys( + pUntilStack, pUntilStack->uiCurOffset, + pUntilStack->pBlkHdr->ui16NumKeys - 1) - 1; + + uiTotalKeys = 0; + uiEstBlocksBetween = 1; + + for( ;;) + { + FLMUINT uiElementCount; + FLMUINT uiTempElementCount; + FLMUINT uiEstElementCount; + + // Go up a b-tree level and check out how far apart the elements are. + + pFromStack++; + pUntilStack++; + + if( RC_BAD( rc = getCacheBlocks( pFromStack, pUntilStack))) + { + goto Exit; + } + + // Share the same block? We can get the actual key count now. + + if( pFromStack->ui32BlkAddr == pUntilStack->ui32BlkAddr) + { + + if( RC_BAD( rc = blockCounts( pFromStack, pFromStack->uiCurOffset, + pUntilStack->uiCurOffset, NULL, &uiElementCount))) + { + goto Exit; + } + + // Don't count the pFromStack current element. + + uiElementCount -= 2; + uiTotalBlocksBetween += uiEstBlocksBetween * + (uiElementCount > 0 ? uiElementCount : 1); + + // Add one to the lasty offset to include the last entry in the count. + + uiTotalKeys = countRangeOfKeys( + pFromStack, pFromStack->uiCurOffset, pUntilStack->uiCurOffset); + + *puiKeyCount = uiTotalKeys - uiOmittedKeys; + *puiBlockCount = uiTotalBlocksBetween; + goto Exit; + } + + // How many to exclude from the From & Until blocks. + + if( pFromStack->uiCurOffset) + { + uiOmittedKeys += countRangeOfKeys( + pFromStack, 0, pFromStack->uiCurOffset - 1); + } + + uiOmittedKeys += countRangeOfKeys( + pUntilStack, pUntilStack->uiCurOffset + 1, + pUntilStack->pBlkHdr->ui16NumKeys - 1); + + // Gather the counts in the from and until non-leaf blocks. + + if( RC_BAD( rc = blockCounts( pFromStack, pFromStack->uiCurOffset, + pFromStack->pBlkHdr->ui16NumKeys - 1, NULL, &uiElementCount))) + { + goto Exit; + } + + // Don't count the first element. + + uiElementCount--; + + if( RC_BAD( rc = blockCounts( pUntilStack, 0, + pUntilStack->uiCurOffset, NULL, &uiTempElementCount))) + { + goto Exit; + } + + uiElementCount += (uiTempElementCount - 1); + uiTotalBlocksBetween += uiEstBlocksBetween * uiElementCount; + + // We are not going to check if these blocks are neighbors here because + // we want to find the common parent. That will tell us what the actual + // counts are at the leaf level. + + // Recompute the estimated element count on every b-tree level + // because the compression is better the lower in the b-tree we go. + + uiEstElementCount = getAvgKeyCount( + pFromStack, pUntilStack, uiAvgBlkFullness); + uiEstBlocksBetween *= uiEstElementCount; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Retrieve the blocks identified in the two stack entries. Used in + computing counts (btComputeCounts etc.) +****************************************************************************/ +RCODE F_Btree::getCacheBlocks( + F_BTSK * pStack1, + F_BTSK * pStack2) +{ + RCODE rc = NE_FLM_OK; + + // If these blocks are at the root level, we must ensure that we retrieve + // the root block. The root block can potentially change address, so + // we wil reset it here to be sure. + + if( pStack1->uiLevel == m_uiRootLevel) + { + pStack1->ui32BlkAddr = (FLMUINT32)m_uiRootBlkAddr; + } + + if( pStack2->uiLevel == m_uiRootLevel) + { + pStack2->ui32BlkAddr = (FLMUINT32)m_uiRootBlkAddr; + } + + if( RC_BAD( m_pBlockMgr->getBlock( pStack1->ui32BlkAddr, &pStack1->pBlock))) + { + goto Exit; + } + + pStack1->pBlkHdr = (F_BTREE_BLK_HDR *)pStack1->pBlock->getBlockPtr(); + + if( RC_BAD( m_pBlockMgr->getBlock( pStack2->ui32BlkAddr, &pStack2->pBlock))) + { + goto Exit; + } + + pStack2->pBlkHdr = (F_BTREE_BLK_HDR *)pStack2->pBlock->getBlockPtr(); + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Method to tally the counts in a block between (inclusive) the + uiFromOffset & uiUntilOffset parameters. +****************************************************************************/ +FLMUINT F_Btree::countRangeOfKeys( + F_BTSK * pFromStack, + FLMUINT uiFromOffset, + FLMUINT uiUntilOffset) +{ + FLMUINT uiCount = 0; + FLMBYTE * pucBlk; + FLMUINT uiLoop = uiFromOffset; + FLMBYTE * pucEntry; + FLMUINT uiBlkType; + + pucBlk = (FLMBYTE *)pFromStack->pBlkHdr; + uiBlkType = getBlkType( pucBlk); + + if( uiBlkType == BT_NON_LEAF_COUNTS) + { + while( uiLoop < uiUntilOffset) + { + pucEntry = BtEntry( pucBlk, uiLoop); + pucEntry += 4; + uiCount += FB2UD( pucEntry); + uiLoop++; + } + } + else + { + uiCount = uiUntilOffset; + } + + return( uiCount); +} + +/*************************************************************************** +Desc: Method to estimate the averge number of keys, based on the anticipated + average block usage (passed in) and the actual block usage. +****************************************************************************/ +FINLINE FLMUINT F_Btree::getAvgKeyCount( + F_BTSK * pFromStack, + F_BTSK * pUntilStack, + FLMUINT uiAvgBlkFullness) +{ + FLMUINT uiFromUsed; + FLMUINT uiUntilUsed; + FLMUINT uiTotalUsed; + FLMUINT uiFromKeys; + FLMUINT uiUntilKeys; + FLMUINT uiTotalKeys; + + uiFromUsed = m_uiBlockSize - + ((F_BLK_HDR *)pFromStack->pBlkHdr)->ui16BlkBytesAvail; + + uiUntilUsed = m_uiBlockSize - + ((F_BLK_HDR *)pUntilStack->pBlkHdr)->ui16BlkBytesAvail; + + uiTotalUsed = uiFromUsed + uiUntilUsed; + + uiFromKeys = pFromStack->pBlkHdr->ui16NumKeys; + uiUntilKeys = pUntilStack->pBlkHdr->ui16NumKeys; + uiTotalKeys = uiFromKeys + uiUntilKeys; + + return( (uiAvgBlkFullness * uiTotalKeys) / uiTotalUsed); +} + +/*************************************************************************** +Desc: Method to test if two blocks can be merged together to make a single + block. This is done only after a remove operation and is intended to + try to consolidate space as much as possible. If we can consolidate + two blocks, we will do it, then update the tree. +****************************************************************************/ +RCODE F_Btree::mergeBlocks( + FLMBOOL bLastEntry, + FLMBOOL * pbMergedWithPrev, + FLMBOOL * pbMergedWithNext, + F_ELM_UPD_ACTION * peAction) +{ + RCODE rc = NE_FLM_OK; + FLMUINT32 ui32PrevBlkAddr; + IF_Block * pPrevBlock = NULL; + FLMUINT32 ui32NextBlkAddr; + IF_Block * pNextBlock = NULL; + F_BLK_HDR * pBlkHdr; + F_BLK_HDR * pPrevBlkHdr; + F_BLK_HDR * pNextBlkHdr; + + *pbMergedWithPrev = FALSE; + *pbMergedWithNext = FALSE; + + // Our first check is to see if we can merge the current block with its + // previous block. + + pBlkHdr = ((F_BLK_HDR *)m_pStack->pBlock->getBlockPtr()); + ui32PrevBlkAddr = pBlkHdr->ui32PrevBlkInChain; + if( ui32PrevBlkAddr) + { + // Get the block. + + if( RC_BAD( rc = m_pBlockMgr->getBlock( ui32PrevBlkAddr, &pPrevBlock))) + { + goto Exit; + } + + pPrevBlkHdr = ((F_BLK_HDR *)pPrevBlock->getBlockPtr()); + + // Is there room to merge? + + if( (FLMUINT)(pPrevBlkHdr->ui16BlkBytesAvail + pBlkHdr->ui16BlkBytesAvail) >= + (FLMUINT)(m_uiBlockSize - sizeofBTreeBlkHdr( (F_BTREE_BLK_HDR *)pBlkHdr))) + { + // Looks like we can merge these two. We will move the content + // of the previous block into this one. + + if( RC_BAD( rc = merge( &pPrevBlock, &m_pStack->pBlock))) + { + goto Exit; + } + + // Save the changed block header address + + m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pBlock->getBlockPtr(); + pBlkHdr = (F_BLK_HDR *)m_pStack->pBlkHdr; + + // Update the counts for the current block before releasing it. + + if( m_bCounts) + { + if( RC_BAD( rc = updateCounts())) + { + goto Exit; + } + } + + if( bLastEntry) + { + // Need to save the replace information for the last entry in + // the block before we move to the previous block. This will + // allow us to do the replace later. + + FLMBYTE * pucEntry; + const FLMBYTE * pucKey; + FLMUINT uiKeyLen; + + pucEntry = BtEntry( + (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->pBlkHdr->ui16NumKeys - 1); + + uiKeyLen = getEntryKeyLength( + pucEntry, getBlkType( (FLMBYTE *)m_pStack->pBlkHdr), &pucKey); + + if( RC_BAD( rc = saveReplaceInfo( pucKey, uiKeyLen))) + { + goto Exit; + } + } + + // Move the stack to the previous entry + + if( RC_BAD( rc = moveStackToPrev( pPrevBlock))) + { + goto Exit; + } + pPrevBlock = NULL; + + flmAssert( m_pStack->pBlkHdr->ui16NumKeys == 0); + + // Free the empty block. + + if( RC_BAD( rc = deleteEmptyBlock())) + { + goto Exit; + } + + // Now we want to remove the parent entry for the block that was + // freed. + + m_pStack++; + *peAction = ELM_REMOVE; + *pbMergedWithPrev = TRUE; + + goto Exit; + } + else + { + // No room here so release the block. + + pPrevBlock->Release(); + pPrevBlock = NULL; + } + } + + // Can we merge with the next block? + + pBlkHdr = (F_BLK_HDR *)m_pStack->pBlock->getBlockPtr(); + ui32NextBlkAddr = pBlkHdr->ui32NextBlkInChain; + if( ui32NextBlkAddr) + { + // Get the block. + + if( RC_BAD( rc = m_pBlockMgr->getBlock( ui32NextBlkAddr, &pNextBlock))) + { + goto Exit; + } + + pNextBlkHdr = (F_BLK_HDR *)pNextBlock->getBlockPtr(); + + // Is there room to merge? + + if( (FLMUINT)(pNextBlkHdr->ui16BlkBytesAvail + pBlkHdr->ui16BlkBytesAvail) >= + (FLMUINT)(m_uiBlockSize - sizeofBTreeBlkHdr( (F_BTREE_BLK_HDR *)pBlkHdr))) + { + // Looks like we can merge these two. + + if( RC_BAD( rc = merge( &m_pStack->pBlock, &pNextBlock))) + { + goto Exit; + } + + // Save the changed block header address. + + m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pBlock->getBlockPtr(); + + // Update the counts for the current block and the next block. + + if( m_bCounts) + { + pPrevBlock = m_pStack->pBlock; + + // Need to move the stack to the next entry. Don't let the current + // block get released because we still need it. + + if( RC_BAD( rc = moveStackToNext( pNextBlock, FALSE))) + { + goto Exit; + } + pNextBlock = NULL; + + if( RC_BAD( rc = updateCounts())) + { + goto Exit; + } + + // Move back to the original stack again. It's okay to release the + // now current block. + + if( RC_BAD( rc = moveStackToPrev( pPrevBlock))) + { + goto Exit; + } + + pPrevBlock = NULL; + } + + flmAssert( m_pStack->pBlkHdr->ui16NumKeys == 0); + + // Free the empty block. + + if( RC_BAD( rc = deleteEmptyBlock())) + { + goto Exit; + } + + // Now we want to remove the parent entry for the block that was freed. + + m_pStack++; + *peAction = ELM_REMOVE; + *pbMergedWithNext = TRUE; + goto Exit; + } + else + { + // No room here so release the block. + + pNextBlock->Release(); + pNextBlock = NULL; + } + } + +Exit: + + if( pPrevBlock) + { + pPrevBlock->Release(); + } + + if( pNextBlock) + { + pNextBlock->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Method to move the contents of ppFromBlock into ppToBlock. + Note that all merges are a move to next operation. +****************************************************************************/ +RCODE F_Btree::merge( + IF_Block ** ppFromBlock, + IF_Block ** ppToBlock) +{ + RCODE rc = NE_FLM_OK; + F_BTSK tempStack; + F_BTSK * pStack = NULL; + IF_Block * pBlock; + F_BTREE_BLK_HDR * pBlkHdr; + + // May need to defragment the blocks first. + + pBlkHdr = (F_BTREE_BLK_HDR *)(*ppToBlock)->getBlockPtr(); + if( pBlkHdr->stdBlkHdr.ui16BlkBytesAvail != pBlkHdr->ui16HeapSize) + { + if( RC_BAD( rc = defragmentBlock( ppToBlock))) + { + goto Exit; + } + } + + // Make a temporary stack entry so we can "fool" the moveToNext + // function into moving the entries for us. + + pBlock = *ppFromBlock; + tempStack.pBlkHdr = (F_BTREE_BLK_HDR *)pBlock->getBlockPtr(); + tempStack.ui32BlkAddr = ((F_BLK_HDR *)tempStack.pBlkHdr)->ui32BlkAddr; + tempStack.pBlock = pBlock; + tempStack.uiCurOffset = 0; + tempStack.uiLevel = m_pStack->uiLevel; + tempStack.pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlock->getBlockPtr(), 0); + + // Save the current m_pStack. + + pStack = m_pStack; + m_pStack = &tempStack; + + // Now do the move + + if( RC_BAD( rc = moveToNext( tempStack.pBlkHdr->ui16NumKeys - 1, + 0, ppToBlock))) + { + goto Exit; + } + + // Return the changed block structure + + *ppFromBlock = tempStack.pBlock; + +Exit: + + // Must always restore the stack. + + m_pStack = pStack; + return( rc); +} + +/*************************************************************************** +Desc: Method to test if the src and dst entries can be combined into one + entry. +****************************************************************************/ +RCODE F_Btree::combineEntries( + F_BTREE_BLK_HDR * pSrcBlkHdr, + FLMUINT uiSrcOffset, + F_BTREE_BLK_HDR * pDstBlkHdr, + FLMUINT uiDstOffset, + FLMBOOL * pbEntriesCombined, + FLMUINT * puiEntrySize, + FLMBYTE * pucTmpBlk) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucSrcEntry; + FLMBYTE * pucDstEntry; + FLMUINT uiSrcKeyLen; + FLMUINT uiDstKeyLen; + const FLMBYTE * pucSrcKey; + const FLMBYTE * pucDstKey; + FLMUINT uiFlags = 0; + FLMBYTE * pucTmp; + FLMUINT uiSrcOADataLen; + FLMUINT uiDstOADataLen; + const FLMBYTE * pucSrcData; + const FLMBYTE * pucDstData; + FLMUINT uiSrcDataLen; + FLMUINT uiDstDataLen; + FLMUINT uiEntrySize; + + *pbEntriesCombined = FALSE; + *puiEntrySize = 0; + + if( pDstBlkHdr->ui16NumKeys == 0) + { + goto Exit; + } + + if( pSrcBlkHdr->ui16NumKeys == 0) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + if( getBlkType( (FLMBYTE *)pSrcBlkHdr) != BT_LEAF_DATA) + { + goto Exit; + } + + pucSrcEntry = BtEntry( (FLMBYTE *)pSrcBlkHdr, uiSrcOffset); + pucDstEntry = BtEntry( (FLMBYTE *)pDstBlkHdr, uiDstOffset); + + // Do we have the same key? + + uiSrcKeyLen = getEntryKeyLength( pucSrcEntry, BT_LEAF_DATA, &pucSrcKey); + uiDstKeyLen = getEntryKeyLength( pucDstEntry, BT_LEAF_DATA, &pucDstKey); + + if( uiSrcKeyLen != uiDstKeyLen) + { + // Not the same key. + + goto Exit; + } + + if( f_memcmp( pucSrcKey, pucDstKey, uiSrcKeyLen) != 0) + { + // Not the same key. + + goto Exit; + } + + // They match, so we can combine them. + + pucTmp = &pucTmpBlk[ 1]; // Key length position + uiFlags = (pucDstEntry[0] & (BTE_FLAG_FIRST_ELEMENT | BTE_FLAG_LAST_ELEMENT)) | + (pucSrcEntry[0] & (BTE_FLAG_FIRST_ELEMENT | BTE_FLAG_LAST_ELEMENT)); + uiEntrySize = 1; + + if( uiSrcKeyLen > ONE_BYTE_SIZE) + { + uiFlags |= BTE_FLAG_KEY_LEN; + UW2FBA( uiSrcKeyLen, pucTmp); + pucTmp += 2; + uiEntrySize += 2; + } + else + { + *pucTmp = (FLMBYTE)uiSrcKeyLen; + pucTmp++; + uiEntrySize++; + } + + uiSrcDataLen = btGetEntryDataLength( + pucSrcEntry, &pucSrcData, &uiSrcOADataLen, NULL); + + uiDstDataLen = btGetEntryDataLength( + pucDstEntry, &pucDstData, &uiDstOADataLen, NULL); + + if( (uiSrcDataLen + uiDstDataLen) > ONE_BYTE_SIZE) + { + uiFlags |= BTE_FLAG_DATA_LEN; + UW2FBA( (uiSrcDataLen + uiDstDataLen), pucTmp); + pucTmp += 2; + uiEntrySize += 2; + } + else + { + *pucTmp = (FLMBYTE)(uiSrcDataLen + uiDstDataLen); + pucTmp++; + uiEntrySize++; + } + + // Verify the OA Data length + + if( (*pucSrcEntry & BTE_FLAG_OA_DATA_LEN) && + (uiSrcOADataLen > (uiSrcDataLen + uiDstDataLen))) + { + uiFlags |= BTE_FLAG_OA_DATA_LEN; + UD2FBA( uiSrcOADataLen, pucTmp); + pucTmp += 4; + uiEntrySize += 4; + } + else if( (*pucDstEntry & BTE_FLAG_OA_DATA_LEN) && + (uiDstOADataLen > (uiSrcDataLen + uiDstDataLen))) + { + uiFlags |= BTE_FLAG_OA_DATA_LEN; + UD2FBA( uiDstOADataLen, pucTmp); + pucTmp += 4; + uiEntrySize += 4; + } + + f_memcpy( pucTmp, pucSrcKey, uiSrcKeyLen); + pucTmp += uiSrcKeyLen; + uiEntrySize += uiSrcKeyLen; + + // Need to put the entry together in the right order. If the Src block is + // before the Dst block, then we will put down the Src data first. + + if( pSrcBlkHdr->stdBlkHdr.ui32NextBlkInChain == + pDstBlkHdr->stdBlkHdr.ui32BlkAddr) + { + f_memcpy( pucTmp, pucSrcData, uiSrcDataLen); + pucTmp += uiSrcDataLen; + uiEntrySize += uiSrcDataLen; + + f_memcpy( pucTmp, pucDstData, uiDstDataLen); + uiEntrySize += uiDstDataLen; + } + else + { + f_memcpy( pucTmp, pucDstData, uiDstDataLen); + uiEntrySize += uiDstDataLen; + pucTmp += uiDstDataLen; + + f_memcpy( pucTmp, pucSrcData, uiSrcDataLen); + uiEntrySize += uiSrcDataLen; + } + + pucTmpBlk[ 0] = (FLMBYTE)uiFlags; + *puiEntrySize = uiEntrySize; + *pbEntriesCombined = TRUE; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Method to move a block from one location to another. +****************************************************************************/ +RCODE F_Btree::btMoveBlock( + FLMUINT32 ui32FromBlkAddr, + FLMUINT32 ui32ToBlkAddr) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiType; + + if( !m_bOpened || m_bSetupForRead || m_bSetupForReplace || + (m_bSetupForWrite)) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_BAD_STATE); + goto Exit; + } + + flmAssert( m_uiSearchLevel >= BH_MAX_LEVELS); + + // Get the From block and retrieve the last key in the block. Make note + // of the level of the block. We will need this to make sure we get the + // right block. + + if( RC_BAD( rc = m_pBlockMgr->getBlock( ui32FromBlkAddr, &m_pBlock))) + { + goto Exit; + } + + // Find out if this is a Btree block or a DO block. + + uiType = getBlkType( (FLMBYTE *)m_pBlock->getBlockPtr()); + + if( uiType == BT_FREE) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + if( uiType == BT_DATA_ONLY) + { + if( RC_BAD( rc = moveDOBlock( ui32FromBlkAddr, ui32ToBlkAddr))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = moveBtreeBlock( ui32FromBlkAddr, ui32ToBlkAddr))) + { + goto Exit; + } + } + +Exit: + + if( m_pBlock) + { + m_pBlock->Release(); + m_pBlock = NULL; + } + + return( rc); +} + +/*************************************************************************** +Desc: Move a Btree block from one address to another, updating its parent. +****************************************************************************/ +RCODE F_Btree::moveBtreeBlock( + FLMUINT32 ui32FromBlkAddr, + FLMUINT32 ui32ToBlkAddr) +{ + RCODE rc = NE_FLM_OK; + F_BTREE_BLK_HDR * pBlkHdr = NULL; + F_BTREE_BLK_HDR * pNewBlkHdr = NULL; + FLMBYTE * pucEntry; + const FLMBYTE * pucKeyRV = NULL; + FLMBYTE * pucKey = NULL; + FLMUINT uiBlkLevel; + FLMBYTE * pucSrc; + FLMBYTE * pucDest; + IF_Block * pBlock = NULL; + FLMUINT uiKeyLen; + + // m_pBlock has already been retrieved. + + flmAssert( m_pBlock); + + pBlkHdr = (F_BTREE_BLK_HDR *)m_pBlock->getBlockPtr(); + uiBlkLevel = pBlkHdr->ui8BlkLevel; + + pucEntry = BtLastEntry( (FLMBYTE *)pBlkHdr); + + uiKeyLen = getEntryKeyLength( pucEntry, getBlkType((FLMBYTE *)pBlkHdr), + &pucKeyRV); + + if( RC_BAD( rc = f_calloc( uiKeyLen, &pucKey))) + { + goto Exit; + } + + f_memcpy( pucKey, pucKeyRV, uiKeyLen); + + // Release the block and search for the key. + + m_pBlock->Release(); + m_pBlock = NULL; + + if( RC_BAD( rc = findEntry( pucKey, uiKeyLen, FLM_EXACT))) + { + // We must find it! + + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + + // Verify that we found the right block. + + m_pStack = &m_Stack[ uiBlkLevel]; + + if( ui32FromBlkAddr != m_pStack->ui32BlkAddr) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &m_pStack->pBlock))) + { + goto Exit; + } + + m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pBlock->getBlockPtr(); + m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)m_pStack->pBlkHdr, 0); + pBlkHdr = m_pStack->pBlkHdr; + + // Get the new block and verify that it is a free block. + + if( RC_BAD( rc = m_pBlockMgr->getBlock( ui32ToBlkAddr, &m_pBlock))) + { + goto Exit; + } + + if( getBlkType( m_pBlock->getBlockPtr()) != BT_FREE) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + // Update the header of the new block to point to the prev and next + // blocks etc ... + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &m_pBlock))) + { + goto Exit; + } + + pNewBlkHdr = (F_BTREE_BLK_HDR *)m_pBlock->getBlockPtr(); + + pNewBlkHdr->stdBlkHdr.ui32PrevBlkInChain = + pBlkHdr->stdBlkHdr.ui32PrevBlkInChain; + pNewBlkHdr->stdBlkHdr.ui32NextBlkInChain = + pBlkHdr->stdBlkHdr.ui32NextBlkInChain; + pNewBlkHdr->stdBlkHdr.ui16BlkBytesAvail = + pBlkHdr->stdBlkHdr.ui16BlkBytesAvail; + pNewBlkHdr->stdBlkHdr.ui8BlkType = pBlkHdr->stdBlkHdr.ui8BlkType; + pNewBlkHdr->stdBlkHdr.ui8BlkFlags = pBlkHdr->stdBlkHdr.ui8BlkFlags; + + pNewBlkHdr->ui16BtreeId = pBlkHdr->ui16BtreeId; + pNewBlkHdr->ui16NumKeys = pBlkHdr->ui16NumKeys; + pNewBlkHdr->ui8BlkLevel = pBlkHdr->ui8BlkLevel; + pNewBlkHdr->ui8BTreeFlags = pBlkHdr->ui8BTreeFlags; + pNewBlkHdr->ui16HeapSize = pBlkHdr->ui16HeapSize; + + // Get the previous and next blocks and set their next and prev addresses. + + if( pBlkHdr->stdBlkHdr.ui32PrevBlkInChain) + { + F_BLK_HDR * pTmpHdr; + + if( RC_BAD( rc = m_pBlockMgr->getBlock( + pBlkHdr->stdBlkHdr.ui32PrevBlkInChain, &pBlock))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &pBlock))) + { + goto Exit; + } + + pTmpHdr = (F_BLK_HDR *)pBlock->getBlockPtr(); + + flmAssert( pTmpHdr->ui32NextBlkInChain == ui32FromBlkAddr); + pTmpHdr->ui32NextBlkInChain = ui32ToBlkAddr; + + pBlock->Release(); + pBlock = NULL; + } + + if( pBlkHdr->stdBlkHdr.ui32NextBlkInChain) + { + F_BLK_HDR * pTmpHdr; + + if( RC_BAD( rc = m_pBlockMgr->getBlock( + pBlkHdr->stdBlkHdr.ui32NextBlkInChain, &pBlock))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &pBlock))) + { + goto Exit; + } + + pTmpHdr = (F_BLK_HDR *)pBlock->getBlockPtr(); + + flmAssert( pTmpHdr->ui32PrevBlkInChain == ui32FromBlkAddr); + pTmpHdr->ui32PrevBlkInChain = ui32ToBlkAddr; + + pBlock->Release(); + pBlock = NULL; + } + + // Copy the content of the old block into the new block. + + pucSrc = (FLMBYTE *)pBlkHdr + sizeofBTreeBlkHdr( pBlkHdr); + pucDest = (FLMBYTE *)pNewBlkHdr + sizeofBTreeBlkHdr( pNewBlkHdr); + + f_memcpy( pucDest, pucSrc, m_uiBlockSize - sizeofBTreeBlkHdr( pBlkHdr)); + + if( isRootBlk( pBlkHdr)) + { + m_uiRootBlkAddr = ui32ToBlkAddr; + goto Exit; + } + + // Move up one level to the parent entry. + + m_pStack++; + flmAssert( m_pStack->pBlock); + + // Log that we are making a change to the block. + + if( RC_BAD( rc = m_pBlockMgr->prepareForUpdate( &m_pStack->pBlock))) + { + goto Exit; + } + + m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pBlock->getBlockPtr(); + + // Update the parent block with a new address for the new block. + + pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); + UD2FBA( ui32ToBlkAddr, pucEntry); + +Exit: + + if( m_pBlock) + { + m_pBlock->Release(); + m_pBlock = NULL; + } + + if( pBlock) + { + pBlock->Release(); + } + + f_free( &pucKey); + releaseBlocks( TRUE); + return( rc); +} + +/*************************************************************************** +Desc: Move a DO block from one address to another, updating its reference + btree entry. +****************************************************************************/ +RCODE F_Btree::moveDOBlock( + FLMUINT32 ui32FromBlkAddr, + FLMUINT32 ui32ToBlkAddr) +{ + RCODE rc = NE_FLM_OK; + F_BLK_HDR * pBlkHdr = NULL; + F_BLK_HDR * pNewBlkHdr = NULL; + FLMBYTE * pucEntry; + FLMBYTE * pucKey = NULL; + FLMBYTE * pucSrc; + FLMBYTE * pucDest; + IF_Block * pBlock = NULL; + IF_Block * pPrevBlock = NULL; + IF_Block * pNextBlock = NULL; + FLMUINT uiKeyLen; + FLMUINT uiOADataLen; + const FLMBYTE * pucData; + FLMUINT32 ui32DOBlkAddr; + FLMUINT uiDataLen; + FLMBYTE ucDataBuffer[ sizeof(FLMUINT32)]; + FLMUINT uiBlkHdrSize; + + // m_pBlock has already been retrieved. + + flmAssert( m_pBlock); + + // Log that we are changing this block. + + if( RC_BAD( m_pBlockMgr->prepareForUpdate( &m_pBlock))) + { + goto Exit; + } + + pBlkHdr = (F_BLK_HDR *)m_pBlock->getBlockPtr(); + + // Get the new block and verify that it is a free block. + + if( RC_BAD( m_pBlockMgr->getBlock( ui32ToBlkAddr, &pBlock))) + { + goto Exit; + } + + if( getBlkType( pBlock->getBlockPtr()) != BT_FREE) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + // Update the header of the new block to point to the prev and next + // blocks etc.. + + if( RC_BAD( m_pBlockMgr->prepareForUpdate( &pBlock))) + { + goto Exit; + } + + pNewBlkHdr = (F_BLK_HDR *)pBlock->getBlockPtr(); + pNewBlkHdr->ui32PrevBlkInChain = pBlkHdr->ui32PrevBlkInChain; + pNewBlkHdr->ui32NextBlkInChain = pBlkHdr->ui32NextBlkInChain; + pNewBlkHdr->ui16BlkBytesAvail = pBlkHdr->ui16BlkBytesAvail; + pNewBlkHdr->ui8BlkType = pBlkHdr->ui8BlkType; + pNewBlkHdr->ui8BlkFlags = pBlkHdr->ui8BlkFlags; + + // Get the previous and next blocks and set their next and prev addresses. + + if( pBlkHdr->ui32PrevBlkInChain) + { + F_BLK_HDR * pTmpHdr; + + if( RC_BAD( rc = m_pBlockMgr->getBlock( + pBlkHdr->ui32PrevBlkInChain, &pPrevBlock))) + { + goto Exit; + } + + if( RC_BAD( m_pBlockMgr->prepareForUpdate( &pPrevBlock))) + { + goto Exit; + } + + pTmpHdr = (F_BLK_HDR *)pPrevBlock->getBlockPtr(); + + flmAssert( pTmpHdr->ui32NextBlkInChain == ui32FromBlkAddr); + pTmpHdr->ui32NextBlkInChain = ui32ToBlkAddr; + pPrevBlock->Release(); + pPrevBlock = NULL; + } + + if( pBlkHdr->ui32NextBlkInChain) + { + F_BLK_HDR * pTmpHdr; + + if( RC_BAD( rc = m_pBlockMgr->getBlock( + pBlkHdr->ui32NextBlkInChain, &pNextBlock))) + { + goto Exit; + } + + if( RC_BAD( m_pBlockMgr->prepareForUpdate( &pNextBlock))) + { + goto Exit; + } + + pTmpHdr = (F_BLK_HDR *)pNextBlock->getBlockPtr(); + + flmAssert( pTmpHdr->ui32PrevBlkInChain == ui32FromBlkAddr); + pTmpHdr->ui32PrevBlkInChain = ui32ToBlkAddr; + pNextBlock->Release(); + pNextBlock = NULL; + } + + // Copy the content of the old block into the new block. + + uiBlkHdrSize = sizeofDOBlkHdr((F_BLK_HDR *)pBlkHdr); + pucSrc = (FLMBYTE *)pBlkHdr + uiBlkHdrSize; + pucDest = (FLMBYTE *)pNewBlkHdr + uiBlkHdrSize; + f_memcpy( pucDest, pucSrc, m_uiBlockSize - uiBlkHdrSize); + + // Do we need to update the reference btree entry. + + if( pBlkHdr->ui32PrevBlkInChain == 0) + { + // Get the key from the beginning of the block. + + uiKeyLen = FB2UW( pucDest); + pucKey = pucDest + sizeof( FLMUINT16); + + if( RC_BAD( rc = findEntry( pucKey, uiKeyLen, FLM_EXACT))) + { + // We must find it! + + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + + // Verify that we found the right block. + + pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); + + if( !bteDataBlockFlag( pucEntry)) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + uiDataLen = btGetEntryDataLength( pucEntry, &pucData, + &uiOADataLen, NULL); + + ui32DOBlkAddr = bteGetBlkAddr( pucData); + + if( ui32DOBlkAddr != ui32FromBlkAddr) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + if( uiDataLen != sizeof( ucDataBuffer)) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + // Make the data entry with the new block address + + UD2FBA( ui32ToBlkAddr, ucDataBuffer); + + if( RC_BAD( rc = updateEntry( + pucKey, uiKeyLen, ucDataBuffer, uiOADataLen, ELM_REPLACE_DO))) + { + goto Exit; + } + } + +Exit: + + if( m_pBlock) + { + m_pBlock->Release(); + m_pBlock = NULL; + } + + if( pBlock) + { + pBlock->Release(); + } + + if( pPrevBlock) + { + pPrevBlock->Release(); + } + + if( pNextBlock) + { + pNextBlock->Release(); + } + + releaseBlocks( TRUE); + return( rc); +} + +/*************************************************************************** +Desc: Method to move the read point in an entry to a particular position + within the entry. This method will move to a previous or a later + position. +****************************************************************************/ +RCODE F_Btree::btSetReadPosition( + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMUINT uiPosition) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucEntry; + F_BLK_HDR * pBlkHdr = NULL; + FLMUINT32 ui32BlkAddr; + FLMBOOL bLastElement = FALSE; + + if( !m_bOpened || !m_bSetupForRead) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_BAD_STATE); + goto Exit; + } + + // We cannot position to a point beyond the end of the current entry. + + if( uiPosition >= m_uiOADataLength) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + // The easiest case to handle is when we want to position within the + // current entry. We should not have to worry about the data only blocks + // because the m_uiDataLength and m_uiDataRemaining are being set correctly + // in setupReadState (via btLocateEntry, btNextEntry, btPrevEntry, + // btFirstEntry and btLastEntry) which is always called before this method is + // called. + + if( (uiPosition < (m_uiOffsetAtStart + m_uiDataLength)) && + (uiPosition >= m_uiOffsetAtStart)) + { + m_uiDataRemaining = m_uiDataLength - (uiPosition - m_uiOffsetAtStart); + m_uiOADataRemaining = m_uiOADataLength - uiPosition; + goto Exit; + } + + // Get the current block. It is either a DO or a Btree block. + + if( m_pBlock == NULL) + { + if( RC_BAD( rc = m_pBlockMgr->getBlock( m_ui32CurBlkAddr, &m_pBlock))) + { + goto Exit; + } + } + + // The next case is when the new position is in a *previous* entry, possibly + // a previous block. + + while( uiPosition < m_uiOffsetAtStart) + { + pBlkHdr = (F_BLK_HDR *)m_pBlock->getBlockPtr(); + + // Are we dealing with DataOnly blocks? + + if( m_bDataOnlyBlock) + { + ui32BlkAddr = pBlkHdr->ui32PrevBlkInChain; + flmAssert( ui32BlkAddr); + + m_pBlock->Release(); + m_pBlock = NULL; + + if( RC_BAD( rc = m_pBlockMgr->getBlock( ui32BlkAddr, &m_pBlock))) + { + goto Exit; + } + + m_ui32CurBlkAddr = ui32BlkAddr; + pBlkHdr = (F_BLK_HDR *)m_pBlock->getBlockPtr(); + + m_uiDataLength = m_uiBlockSize - pBlkHdr->ui16BlkBytesAvail - + sizeofDOBlkHdr((F_BLK_HDR *)pBlkHdr); + + if( !pBlkHdr->ui32PrevBlkInChain) + { + FLMBYTE * pucPtr = (FLMBYTE *)pBlkHdr + sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr); + FLMUINT16 ui16KeyLen = FB2UW( pucPtr); + + // We need to adjust for the key in the first block. + + m_uiDataLength -= ui16KeyLen; + } + + // Decrement by the size of the current data + + m_uiOffsetAtStart -= m_uiDataLength; + } + else + { + // Backup to the previous element. This may or may not get + // another block + + if( RC_BAD( rc = backupToPrevElement( FALSE))) + { + goto Exit; + } + + pucEntry = BtEntry( m_pBlock->getBlockPtr(), m_uiCurOffset); + + // Make sure we are still looking at the same key etc. + + if( !checkContinuedEntry( + pucKey, uiKeyLen, &bLastElement, pucEntry, + getBlkType( m_pBlock->getBlockPtr()))) + { + // Should always match at this point! + + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + m_uiDataLength = btGetEntryDataLength( pucEntry, NULL, NULL, NULL); + m_uiOffsetAtStart -= m_uiDataLength; + } + } + + // Did we find the block? + + if( (uiPosition < (m_uiOffsetAtStart + m_uiDataLength)) && + (uiPosition >= m_uiOffsetAtStart)) + { + m_uiDataRemaining = m_uiDataLength - (uiPosition - m_uiOffsetAtStart); + m_uiOADataRemaining = m_uiOADataLength - uiPosition; + goto Exit; + } + + // Finally, we realize that the new position is beyond the current entry. + + while( uiPosition >= (m_uiOffsetAtStart + m_uiDataLength)) + { + flmAssert( m_uiDataLength + m_uiOffsetAtStart <= m_uiOADataLength); + + // Get the next entry. + + pBlkHdr = (F_BLK_HDR *)m_pBlock->getBlockPtr(); + + // Are we dealing with DataOnly blocks? + + if( m_bDataOnlyBlock) + { + ui32BlkAddr = pBlkHdr->ui32NextBlkInChain; + flmAssert( ui32BlkAddr); + + m_pBlock->Release(); + m_pBlock = NULL; + + if( RC_BAD( rc = m_pBlockMgr->getBlock( ui32BlkAddr, &m_pBlock))) + { + goto Exit; + } + + m_ui32CurBlkAddr = ui32BlkAddr; + pBlkHdr = (F_BLK_HDR *)m_pBlock->getBlockPtr(); + + // Increment by the size of the previous data. Note that in this + // case, we do not have to be concerned about the key in the first + // DO block since we will never move forward to it. + + m_uiOffsetAtStart += m_uiDataLength; + m_uiDataLength = m_uiBlockSize - pBlkHdr->ui16BlkBytesAvail - + sizeofDOBlkHdr((F_BLK_HDR *)pBlkHdr); + } + else + { + // Advance to the next element. This may or may not get another block. + // Be sure we do not advance the stack since we do not have one. + + if( RC_BAD( rc = advanceToNextElement( FALSE))) + { + goto Exit; + } + + pucEntry = BtEntry( m_pBlock->getBlockPtr(), m_uiCurOffset); + + // Make sure we are still looking at the same key etc. + + if( !checkContinuedEntry( + pucKey, uiKeyLen, &bLastElement, pucEntry, + getBlkType( m_pBlock->getBlockPtr()))) + { + // Should always match at this point! + + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + // Get the data length of the current entry. + + m_uiOffsetAtStart += m_uiDataLength; + m_uiDataLength = btGetEntryDataLength( pucEntry, NULL, NULL, NULL); + } + } + + // Did we find the block? If we still don't find it, then we + // have a big problem. + + if( (uiPosition >= (m_uiOffsetAtStart + m_uiDataLength)) || + (uiPosition < m_uiOffsetAtStart)) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + m_uiDataRemaining = m_uiDataLength - (uiPosition - m_uiOffsetAtStart); + m_uiOADataRemaining = m_uiOADataLength - uiPosition; + +Exit: + + if( m_pBlock) + { + m_pBlock->Release(); + m_pBlock = NULL; + } + + releaseBlocks( FALSE); + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Btree::btGetReadPosition( + FLMUINT * puiPosition) +{ + RCODE rc = NE_FLM_OK; + + if( !m_bOpened || !m_bSetupForRead) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_BAD_STATE); + goto Exit; + } + + flmAssert( puiPosition); + *puiPosition = m_uiOffsetAtStart + (m_uiDataLength - m_uiDataRemaining); + +Exit: + + if( m_pBlock) + { + m_pBlock->Release(); + m_pBlock = NULL; + } + + releaseBlocks( FALSE); + return( rc); +} + +/*************************************************************************** +Desc: Performs a consistancy check on the BTree + NOTE: Must be performed inside of a read transaction! +****************************************************************************/ +RCODE F_Btree::btCheck( + BTREE_ERR_STRUCT * pErrStruct) +{ + RCODE rc = NE_FLM_OK; + FLMUINT32 ui32NextBlkAddr = 0; + FLMUINT32 ui32NextLevelBlkAddr = 0; + FLMUINT32 ui32ChildBlkAddr = 0; + FLMUINT32 ui32DOBlkAddr = 0; + FLMUINT uiNumKeys; + const FLMBYTE * pucPrevKey; + FLMUINT uiPrevKeySize; + const FLMBYTE * pucCurKey; + FLMUINT uiCurKeySize; + IF_Block * pCurrentBlk = NULL; + IF_Block * pPrevBlock = NULL; + FLMBYTE * pBlk = NULL; + FLMBYTE * pucEntry = NULL; + FLMBYTE * pucPrevEntry = NULL; + IF_Block * pChildBlk = NULL; + FLMUINT16 * puiOffsetArray; + BTREE_ERR_STRUCT localErrStruct; + FLMINT iCmpResult; + FLMUINT uiOADataLength = 0; + + // Initial setup... + + ui32NextLevelBlkAddr = (FLMUINT32)m_uiRootBlkAddr; + f_memset( &localErrStruct, 0, sizeof( localErrStruct)); + localErrStruct.uiBlockSize = m_uiBlockSize; + + // While there's a next level.... + + while( ui32NextLevelBlkAddr) + { + localErrStruct.uiLevels++; + ui32NextBlkAddr = ui32NextLevelBlkAddr; + + // Update uiNextLevelBlkAddr + + if( RC_BAD( rc = m_pBlockMgr->getBlock( ui32NextBlkAddr, &pCurrentBlk))) + { + localErrStruct.type = SCA_GET_BLOCK_FAILED; + f_sprintf( localErrStruct.szMsg, + "Failed to get block at %X", ui32NextBlkAddr); + goto Exit; + } + + pBlk = pCurrentBlk->getBlockPtr(); + puiOffsetArray = BtOffsetArray( pBlk, 0); + + if( (getBlkType( pBlk) == BT_LEAF) || (getBlkType( pBlk) == BT_LEAF_DATA)) + { + ui32NextLevelBlkAddr = 0; + } + else + { + pucEntry = BtEntry( pBlk, 0); + + // The child block address is the first part of the entry + + ui32NextLevelBlkAddr = bteGetBlkAddr( pucEntry); + } + + if( pPrevBlock) + { + pPrevBlock->Release(); + pPrevBlock = NULL; + } + + // While there's another block on this level... + + while( ui32NextBlkAddr) + { + // This loop assumes that pCurrentBlk and pBlk are already initialized. + + localErrStruct.uiBlocksChecked++; + localErrStruct.uiAvgFreeSpace = + (localErrStruct.uiAvgFreeSpace * (localErrStruct.uiBlocksChecked - 1) / + localErrStruct.uiBlocksChecked) + + (getBlkAvailSpace(pBlk) / localErrStruct.uiBlocksChecked); + localErrStruct.ui64FreeSpace += getBlkAvailSpace(pBlk); + + localErrStruct.LevelStats[ localErrStruct.uiLevels - 1].uiBlkCnt++; + localErrStruct.LevelStats[ localErrStruct.uiLevels - 1].uiBytesUsed += + (m_uiBlockSize - getBlkAvailSpace(pBlk)); + + uiNumKeys = ((F_BTREE_BLK_HDR *)pBlk)->ui16NumKeys; + + // Verify that the keys are in order... + // Make sure that we check the keys between blocks as well. + + if( pPrevBlock) + { + pucEntry = BtLastEntry( pPrevBlock->getBlockPtr()); + uiPrevKeySize = getEntryKeyLength( pucEntry, getBlkType( + pPrevBlock->getBlockPtr()), &pucPrevKey); + } + else + { + pucEntry = BtEntry( pBlk, 0); + uiPrevKeySize = getEntryKeyLength( pucEntry, getBlkType( pBlk), + &pucPrevKey); + if( getBlkType(pBlk) == BT_LEAF_DATA) + { + if( bteFirstElementFlag( pucEntry)) + { + localErrStruct.LevelStats[ + localErrStruct.uiLevels - 1].uiFirstKeyCnt++; + } + } + else + { + // Everything else is a first key. + + localErrStruct.LevelStats[ + localErrStruct.uiLevels - 1].uiFirstKeyCnt++; + } + } + + for( FLMUINT uiLoop = (pPrevBlock ? 0: 1); + uiLoop < uiNumKeys; uiLoop++) + { + pucPrevEntry = pucEntry; + pucEntry = BtEntry( pBlk, uiLoop); + + if( getBlkType(pBlk) == BT_LEAF_DATA) + { + if( bteFirstElementFlag( pucEntry)) + { + localErrStruct.LevelStats[ + localErrStruct.uiLevels - 1].uiFirstKeyCnt++; + } + } + else + { + // Everything else is a first key. + + localErrStruct.LevelStats[ + localErrStruct.uiLevels - 1].uiFirstKeyCnt++; + } + + uiCurKeySize = getEntryKeyLength( pucEntry, + getBlkType( pBlk), &pucCurKey); + + // The last key in the last block of each level is an infinity marker + // It must have a 0 keylength and if it's a leaf node, a 0 datalength. + + if( (uiLoop == uiNumKeys - 1) && + (((F_BLK_HDR *)pBlk)->ui32NextBlkInChain == 0)) + { + // If the key size is not 0, or we're a leaf block, and the + // data size is not 0 ... + + if( (uiCurKeySize != 0) || + (((getBlkType( pBlk) == BT_LEAF_DATA)) && + (btGetEntryDataLength( pucEntry, NULL, NULL, NULL) > 0))) + { + localErrStruct.type = INFINITY_MARKER; + localErrStruct.uiBlkAddr = ((F_BLK_HDR *)pBlk)->ui32BlkAddr; + f_sprintf( localErrStruct.szMsg, "Invalid Infinity Marker %ul", uiLoop); + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + } + else + { + // Do a comparison of the previous and current keys ... + + if( RC_BAD( rc = compareKeys( pucPrevKey, uiPrevKeySize, + pucCurKey, uiCurKeySize, &iCmpResult))) + { + goto Exit; + } + + if( iCmpResult > 0) + { + localErrStruct.type = KEY_ORDER; + localErrStruct.uiBlkAddr = ((F_BLK_HDR *)pBlk)->ui32BlkAddr; + f_sprintf( localErrStruct.szMsg, "Key Number %ul", uiLoop); + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + if( getBlkType(pBlk) == BT_LEAF_DATA) + { + if( iCmpResult < 0) + { + flmAssert( *pucEntry & BTE_FLAG_FIRST_ELEMENT); + } + else if( iCmpResult == 0) + { + flmAssert( (*pucEntry & BTE_FLAG_FIRST_ELEMENT) == 0); + flmAssert( (*pucPrevEntry & BTE_FLAG_LAST_ELEMENT) == 0); + } + } + } + + pucPrevKey = pucCurKey; + uiPrevKeySize = uiCurKeySize; + } + + localErrStruct.uiNumKeys += uiNumKeys; + localErrStruct.LevelStats[ + localErrStruct.uiLevels - 1].uiKeyCnt += uiNumKeys; + + // If this is a leaf block, check for any pointers to data-only + // blocks. Verify the blocks... + + if( getBlkType( pBlk) == BT_LEAF || + getBlkType( pBlk) == BT_LEAF_DATA) + { + if( getBlkType( pBlk) == BT_LEAF_DATA) + { + for( FLMUINT uiLoop = 0; uiLoop < uiNumKeys; uiLoop++) + { + pucEntry = BtEntry( pBlk, uiLoop); + + if( bteDataBlockFlag( pucEntry)) + { + FLMBYTE ucDOBlkAddr[ 4]; + + if( RC_BAD( rc = btGetEntryData( pucEntry, + &ucDOBlkAddr[ 0], 4, NULL))) + { + RC_UNEXPECTED_ASSERT( rc); + localErrStruct.type = CATASTROPHIC_FAILURE; + localErrStruct.uiBlkAddr = ((F_BLK_HDR *)pBlk)->ui32BlkAddr; + f_sprintf( localErrStruct.szMsg, + "getEntryData couldn't get the DO blk addr."); + goto Exit; + } + + ui32DOBlkAddr = bteGetBlkAddr( (FLMBYTE *)&ucDOBlkAddr[ 0]); + + // Verify that there is an OverallDataLength field + + if( bteOADataLenFlag( pucEntry) == 0) + { + localErrStruct.type = MISSING_OVERALL_DATA_LENGTH; + localErrStruct.uiBlkAddr = ((F_BLK_HDR *)pBlk)->ui32BlkAddr; + f_sprintf( localErrStruct.szMsg, + "OverallDataLength field is missing"); + } + else + { + if( bteKeyLenFlag( pucEntry)) + { + uiOADataLength = FB2UD( pucEntry + 4); + } + else + { + uiOADataLength = FB2UD( pucEntry + 3); + } + } + + if( RC_BAD( rc = verifyDOBlkChain( ui32DOBlkAddr, + uiOADataLength , &localErrStruct))) + { + goto Exit; + } + } + } + } + } + else + { + // This is a non-leaf block, verify that blocks exist for all + // the child block addresses + + // NOTE: Also need to somehow verify that no two elements have the + // same child block address... + + for( FLMUINT uiLoop = 0; uiLoop < uiNumKeys; uiLoop++) + { + pucEntry = BtEntry( pBlk, uiLoop); + ui32ChildBlkAddr = bteGetBlkAddr( pucEntry); + if( RC_BAD( rc = m_pBlockMgr->getBlock( + ui32ChildBlkAddr, &pChildBlk))) + { + localErrStruct.type = SCA_GET_BLOCK_FAILED; + f_sprintf( localErrStruct.szMsg, "Failed to get block at %X", + ui32ChildBlkAddr); + goto Exit; + } + + pChildBlk->Release(); + pChildBlk = NULL; + } + } + + // Release the current block and get the next one + + ui32NextBlkAddr = ((F_BLK_HDR *)pBlk)->ui32NextBlkInChain; + + if( pPrevBlock) + { + pPrevBlock->Release(); + pPrevBlock = NULL; + } + + pPrevBlock = pCurrentBlk; + pCurrentBlk = NULL; + + if( ui32NextBlkAddr) + { + if( RC_BAD( rc = m_pBlockMgr->getBlock( + ui32NextBlkAddr, &pCurrentBlk))) + { + localErrStruct.type = SCA_GET_BLOCK_FAILED; + f_sprintf( localErrStruct.szMsg, + "Failed to get block at %X", ui32ChildBlkAddr); + goto Exit; + } + + pBlk = pCurrentBlk->getBlockPtr(); + } + } + } + + if( m_bCounts) + { + if( RC_BAD( rc = verifyCounts( &localErrStruct))) + { + goto Exit; + } + } + +Exit: + + if( pPrevBlock) + { + pPrevBlock->Release(); + } + + if( pCurrentBlk) + { + pCurrentBlk->Release(); + } + + f_memcpy( pErrStruct, &localErrStruct, sizeof( localErrStruct)); + return( rc); +} + +/*************************************************************************** +Desc: Performs an integrity check on a chain of data-only blocks. Should + only be called from btCheck(). Note that unlike btCheck(), + errStruct CANNOT be NULL here. +****************************************************************************/ +RCODE F_Btree::verifyDOBlkChain( + FLMUINT uiDOAddr, // Address of first block in chain + FLMUINT uiDataLength, // The length of the entire entry + BTREE_ERR_STRUCT * errStruct) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiRunningLength = 0; // A running total of the DataLength fields + // for all of the blocks in this chain + IF_Block * pCurrentBlk = NULL; + FLMUINT32 ui32NextAddr = (FLMUINT32)uiDOAddr; + FLMBYTE * pBlk; + FLMUINT uiDataSize; + + while( ui32NextAddr) + { + errStruct->LevelStats[ errStruct->uiLevels - 1].uiDOBlkCnt++; + + // Get the next block + + if( RC_BAD( m_pBlockMgr->getBlock( ui32NextAddr, &pCurrentBlk))) + { + errStruct->type = SCA_GET_BLOCK_FAILED; + f_sprintf( errStruct->szMsg, "Failed to get block at %X", uiDOAddr); + goto Exit; + } + + pBlk = pCurrentBlk->getBlockPtr(); + + // Verify that it's really a DO Block + + if( getBlkType( pBlk) != BT_DATA_ONLY) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + errStruct->type = NOT_DATA_ONLY_BLOCK; + goto Exit; + } + + // Update counts info in errStruct + + errStruct->LevelStats[ errStruct->uiLevels - 1].uiDOBytesUsed += + m_uiBlockSize - ((F_BLK_HDR *)pBlk)->ui16BlkBytesAvail; + + // Update the data length running total + + uiDataSize = m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)pBlk) - + ((F_BLK_HDR *)pBlk)->ui16BlkBytesAvail; + + if( ((F_BLK_HDR *)pBlk)->ui32PrevBlkInChain == 0) + { + FLMBYTE * pucPtr = pBlk + sizeofDOBlkHdr( (F_BLK_HDR *)pBlk); + FLMUINT16 ui16KeyLen = FB2UW( pucPtr); + + uiDataSize -= (ui16KeyLen + 2); + } + + uiRunningLength += uiDataSize; + + // Update ui32nextAddr + + ui32NextAddr = ((F_BLK_HDR *)pBlk)->ui32NextBlkInChain; + + // Release it when we no longer need it. + + pCurrentBlk->Release(); + pCurrentBlk = NULL; + } + + // Check the calculated overall length vs. uiDataLength + + if( uiRunningLength != uiDataLength) + { + errStruct->type = BAD_DO_BLOCK_LENGTHS; + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + +Exit: + + if( pCurrentBlk) + { + pCurrentBlk->Release(); + } + + if( rc == NE_FLM_BTREE_ERROR) + { + f_sprintf( errStruct->szMsg, "Corrupt DO chain starting at %X", uiDOAddr); + } + + return( NE_FLM_OK); +} + +/*************************************************************************** +Desc: Method to check the counts in a database with counts. +****************************************************************************/ +RCODE F_Btree::verifyCounts( + BTREE_ERR_STRUCT * pErrStruct) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiNextLevelBlkAddr; + FLMUINT uiNextBlkAddr; + FLMUINT uiChildBlkAddr; + IF_Block * pCurrentBlk = NULL; + IF_Block * pChildBlk = NULL; + FLMBYTE * pucEntry; + FLMUINT uiNumKeys; + FLMUINT uiEntryNum; + FLMUINT uiParentCounts; + FLMUINT uiChildCounts; + FLMBYTE * pBlk; + FLMBOOL bDone = FALSE; + + flmAssert( m_bCounts); + + // Repeat at each level, starting at the root. + + uiNextLevelBlkAddr = m_uiRootBlkAddr; + + while( uiNextLevelBlkAddr) + { + if( RC_BAD( rc = m_pBlockMgr->getBlock( + uiNextLevelBlkAddr, &pCurrentBlk))) + { + goto Exit; + } + + pBlk = pCurrentBlk->getBlockPtr(); + + if( ((F_BLK_HDR *)pBlk)->ui8BlkType != BT_NON_LEAF_COUNTS) + { + pCurrentBlk->Release(); + pCurrentBlk = NULL; + break; + } + + pucEntry = BtEntry( pBlk, 0); + uiNextLevelBlkAddr = bteGetBlkAddr( pucEntry); + + // For every entry in the block, and for every block on this level, + // check that the counts match the actual counts in the corresponding + // child block. + + bDone = FALSE; + while( !bDone) + { + pBlk = pCurrentBlk->getBlockPtr(); + uiNumKeys = ((F_BTREE_BLK_HDR *)pBlk)->ui16NumKeys; + + // Now check every entry in this block. + + for( uiEntryNum = 0; uiEntryNum < uiNumKeys; uiEntryNum++) + { + F_BLK_HDR * pChildBlkHdr; + + pucEntry = BtEntry( pBlk, uiEntryNum); + uiChildBlkAddr = bteGetBlkAddr( pucEntry); + + pucEntry += 4; + uiParentCounts = FB2UD( pucEntry); + + if( RC_BAD( rc = m_pBlockMgr->getBlock( + uiChildBlkAddr, &pChildBlk))) + { + goto Exit; + } + + pChildBlkHdr = ((F_BLK_HDR *)pChildBlk->getBlockPtr()); + uiChildCounts = countKeys( (FLMBYTE *)pChildBlkHdr); + + if( uiChildCounts != uiParentCounts) + { + pErrStruct->type = BAD_COUNTS; + pErrStruct->uiBlkAddr = pChildBlkHdr->ui32BlkAddr; + + f_sprintf( + pErrStruct->szMsg, + "Counts do not match. Expected %d, got %d", + uiParentCounts, uiChildCounts); + rc = RC_SET_AND_ASSERT( NE_FLM_BTREE_ERROR); + goto Exit; + } + + pChildBlk->Release(); + pChildBlk = NULL; + } + + // Now get the next block at this level. + + uiNextBlkAddr = ((F_BLK_HDR *)pBlk)->ui32NextBlkInChain; + pCurrentBlk->Release(); + pCurrentBlk = NULL; + + if( uiNextBlkAddr == 0) + { + bDone = TRUE; + } + else + { + if( RC_BAD( rc = m_pBlockMgr->getBlock( + uiNextBlkAddr, &pCurrentBlk))) + { + goto Exit; + } + } + } + } + +Exit: + + if( pCurrentBlk) + { + pCurrentBlk->Release(); + } + + if( pChildBlk) + { + pChildBlk->Release(); + } + + return( rc); +} diff --git a/ftk/src/ftkdir.cpp b/ftk/src/ftkdir.cpp new file mode 100644 index 0000000..e8014da --- /dev/null +++ b/ftk/src/ftkdir.cpp @@ -0,0 +1,921 @@ +//------------------------------------------------------------------------------ +// Desc: Class for doing file directory operations. +// +// Tabs: 3 +// +// Copyright (c) 1998-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: ftkdir.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +#define ERR_NO_FILES_FOUND 0xFF +#define ERR_INVALID_PATH 0x9C + +#if defined( FLM_WIN) + + FSTATIC FLMBOOL f_fileMeetsFindCriteria( + F_IO_FIND_DATA * pFindData); + +#elif defined( FLM_UNIX) || defined( FLM_NLM) + + FSTATIC int Find1( + char * FindTemplate, + F_IO_FIND_DATA * DirInfo); + + + FSTATIC int Find2( + F_IO_FIND_DATA * DirStuff); + + FSTATIC FLMBYTE ReturnAttributes( + mode_t FileMode, + char * pszFileName); + + FSTATIC int RetrieveFileStat( + char * FilePath, + struct stat * StatusRec); + +#else + + #error Platform not supported + +#endif + +/**************************************************************************** +Desc: Constructor +****************************************************************************/ +F_DirHdl::F_DirHdl() +{ + m_rc = NE_FLM_OK; + m_bFirstTime = TRUE; + m_bFindOpen = FALSE; + m_uiAttrib = 0; + m_szPattern[ 0] = '\0'; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +const char * FLMAPI F_DirHdl::currentItemName( void) +{ + const char * pszName = NULL; + + if( RC_OK( m_rc)) + { + pszName = m_szFileName; + } + + return( pszName); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE void FLMAPI F_DirHdl::currentItemPath( + char * pszPath) +{ + if( RC_OK( m_rc)) + { + f_strcpy( pszPath, m_szDirectoryPath); + gv_pFileSystem->pathAppend( pszPath, m_szFileName); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL FLMAPI F_DirHdl::currentItemIsDir( void) +{ + return( ((m_uiAttrib & F_IO_FA_DIRECTORY) + ? TRUE + : FALSE)); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT64 FLMAPI F_DirHdl::currentItemSize( void) +{ + FLMUINT64 ui64Size = 0; + + if( RC_OK( m_rc)) + { +#if defined( FLM_WIN) + ui64Size = (((FLMUINT64)m_FindData.findBuffer.nFileSizeHigh) << 32) + + m_FindData.findBuffer.nFileSizeLow; +#elif defined( FLM_UNIX) || defined ( FLM_NLM) + ui64Size = m_FindData.FileStat.st_size; +#endif + } + return( ui64Size); +} + +/**************************************************************************** +Desc: Get the next item in a directory +****************************************************************************/ +RCODE FLMAPI F_DirHdl::next( void) +{ + char szFoundPath[ F_PATH_MAX_SIZE]; + char szDummyPath[ F_PATH_MAX_SIZE]; + FLMUINT uiSearchAttributes; + FLMUINT uiFoundAttrib; + + if( RC_BAD( m_rc)) + { + goto Exit; + } + + uiSearchAttributes = + F_IO_FA_NORMAL | F_IO_FA_RDONLY | F_IO_FA_ARCHIVE | F_IO_FA_DIRECTORY; + + for( ;;) + { + if ( m_bFirstTime ) + { + m_bFirstTime = FALSE; + + if( RC_BAD( m_rc = f_fileFindFirst( m_szDirectoryPath, uiSearchAttributes, + &m_FindData, szFoundPath, &uiFoundAttrib))) + { + goto Exit; + } + + m_bFindOpen = TRUE; + m_uiAttrib = uiFoundAttrib; + } + else + { + if( RC_BAD( m_rc = f_fileFindNext( &m_FindData, + szFoundPath, &uiFoundAttrib))) + { + goto Exit; + } + + m_uiAttrib = uiFoundAttrib; + } + + if( RC_BAD( m_rc = gv_pFileSystem->pathReduce( szFoundPath, + szDummyPath, m_szFileName))) + { + goto Exit; + } + + if( gv_pFileSystem->doesFileMatch( m_szFileName, m_szPattern)) + { + break; + } + } + +Exit: + + return( m_rc); +} + +/**************************************************************************** +Desc: Open a directory +****************************************************************************/ +RCODE F_DirHdl::openDir( + const char * pszDirName, + const char * pszPattern) +{ + RCODE rc = NE_FLM_OK; + + m_rc = NE_FLM_OK; + m_bFirstTime = TRUE; + m_bFindOpen = FALSE; + m_uiAttrib = 0; + + f_strcpy( m_szDirectoryPath, pszDirName); + + if( pszPattern) + { + if( f_strlen( pszPattern) >= sizeof( m_szPattern)) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + f_strcpy( m_szPattern, pszPattern); + } + +Exit: + + return( rc); + +} + +/**************************************************************************** +Desc: Create a directory (and parent directories if necessary). +****************************************************************************/ +RCODE F_DirHdl::createDir( + const char * pszDirPath) +{ + char * pszParentDir = NULL; + RCODE rc = NE_FLM_OK; + + if( RC_BAD( rc = f_alloc( F_PATH_MAX_SIZE, &pszParentDir))) + { + goto Exit; + } + + // Discover the parent directory of the given one + + if( RC_BAD( rc = gv_pFileSystem->pathReduce( pszDirPath, + pszParentDir, NULL))) + { + goto Exit; + } + + // If pathReduce couldn't reduce the path at all, then an + // invalid path was supplied. + + if( f_strcmp( pszDirPath, pszParentDir) == 0) + { + rc = RC_SET( NE_FLM_IO_INVALID_FILENAME); + goto Exit; + } + + // If a parent directory was found, and it doesn't already exist, create it + + if( *pszParentDir) + { + // If the "parent" is actually a regular file we need to return an error + + if( RC_OK( gv_pFileSystem->doesFileExist( pszParentDir))) + { + if( !gv_pFileSystem->isDir( pszParentDir)) + { + rc = RC_SET( NE_FLM_IO_ACCESS_DENIED); + goto Exit; + } + } + else if( RC_BAD( rc = createDir( pszParentDir))) + { + goto Exit; + } + } + +#if defined( FLM_WIN) + + if( !CreateDirectory((LPTSTR)pszDirPath, NULL)) + { + rc = MapPlatformError( GetLastError(), NE_FLM_CREATING_FILE); + } + +#elif defined( FLM_UNIX) || defined( FLM_NLM) + + if( mkdir( (char *)pszDirPath, 0777) == -1) + { + rc = MapPlatformError( errno, NE_FLM_CREATING_FILE); + } + +#endif + +Exit: + + if( pszParentDir) + { + f_free( &pszParentDir); + } + + return( rc); +} + +/**************************************************************************** +Desc: Remove a directory +Notes: The directory must be empty. +****************************************************************************/ +RCODE F_DirHdl::removeDir( + const char * pszDirName) +{ +#if defined( FLM_WIN) + + if( !RemoveDirectory((LPTSTR)pszDirName)) + { + return( MapPlatformError( GetLastError(), NE_FLM_IO_DELETING_FILE)); + } + + return( NE_FLM_OK); + +#elif defined( FLM_UNIX) || defined( FLM_NLM) + + if( rmdir( (char *)pszDirName) == -1) + { + return( MapPlatformError( errno, NE_FLM_IO_DELETING_FILE)); + } + + return( NE_FLM_OK); + +#endif +} + +/**************************************************************************** +Desc: Find the first file that matches the supplied criteria +****************************************************************************/ +RCODE f_fileFindFirst( + char * pszSearchPath, + FLMUINT uiSearchAttrib, + F_IO_FIND_DATA * pFindData, + char * pszFoundPath, + FLMUINT * puiFoundAttrib) +{ +#ifdef FLM_WIN + char szTmpPath[ F_PATH_MAX_SIZE]; + char * pszWildCard = "*.*"; + RCODE rc = NE_FLM_OK; + + f_memset( pFindData, 0, sizeof( F_IO_FIND_DATA)); + pFindData->findHandle = INVALID_HANDLE_VALUE; + pFindData->uiSearchAttrib = uiSearchAttrib; + + if( !pszSearchPath) + { + rc = RC_SET( NE_FLM_IO_PATH_NOT_FOUND); + goto Exit; + } + + f_strcpy( pFindData->szSearchPath, pszSearchPath); + + if( uiSearchAttrib & F_IO_FA_NORMAL ) + { + uiSearchAttrib |= F_IO_FA_ARCHIVE; + } + + f_strcpy( szTmpPath, pszSearchPath); + + if( RC_BAD( rc = gv_pFileSystem->pathAppend( szTmpPath, pszWildCard))) + { + goto Exit; + } + + if( (pFindData->findHandle = FindFirstFile( (LPTSTR)szTmpPath, + &(pFindData->findBuffer))) == INVALID_HANDLE_VALUE) + { + rc = MapPlatformError( GetLastError(), NE_FLM_OPENING_FILE); + goto Exit; + } + + // Loop until a file with correct attributes is found + + for( ;;) + { + if( f_fileMeetsFindCriteria( pFindData)) + { + break; + } + + if( FindNextFile( pFindData->findHandle, + &(pFindData->findBuffer)) == FALSE) + { + rc = MapPlatformError( GetLastError(), NE_FLM_READING_FILE); + goto Exit; + } + } + + // Append the file name to the path name + + f_strcpy( pszFoundPath, pFindData->szSearchPath); + + if( RC_BAD( rc = gv_pFileSystem->pathAppend( pszFoundPath, + (char *)pFindData->findBuffer.cFileName))) + { + goto Exit; + } + + // Return the found file attribute + + *puiFoundAttrib = pFindData->findBuffer.dwFileAttributes; + +Exit: + + if( RC_BAD( rc) && pFindData && + pFindData->findHandle != INVALID_HANDLE_VALUE) + { + f_fileFindClose( pFindData); + } + + return( rc); + +#else + + char szTmpPath[ F_PATH_MAX_SIZE]; + FSTATIC char pszWildCard[] = {'*',0}; + int iRetVal; + RCODE rc = NE_FLM_OK; + + if( !pszSearchPath) + { + rc = RC_SET( NE_FLM_IO_PATH_NOT_FOUND); + goto Exit; + } + + f_strcpy( szTmpPath, pszSearchPath); + if( RC_BAD( rc = gv_pFileSystem->pathAppend( szTmpPath, pszWildCard))) + { + goto Exit; + } + + f_memset( pFindData, 0, sizeof( F_IO_FIND_DATA)); + if( uiSearchAttrib & F_IO_FA_DIRECTORY) + { + pFindData->mode_flag |= S_IFDIR; + } + + if( uiSearchAttrib & F_IO_FA_RDONLY) + { + pFindData->mode_flag |= S_IREAD; + } + + iRetVal = Find1( (char*)szTmpPath, pFindData); + + if( iRetVal != 0) + { + // If there were no more files found then return no more files + // instead of mapping to error path not found or io error. + // To return no more files ret_val is ENOENT (set in Find2) + // and errno is not set + + if( iRetVal == ENOENT && errno == 0) + { + rc = RC_SET( NE_FLM_IO_NO_MORE_FILES); + } + else + { + rc = MapPlatformError( errno, NE_FLM_READING_FILE); + } + + goto Exit; + } + + // filter out ".." (PARENT) and "." (CURRENT) directories + + if( uiSearchAttrib & F_IO_FA_DIRECTORY ) + { + while( (f_strcmp( (FLMBYTE *)pFindData->name, (FLMBYTE *)"..") == 0) || + (f_strcmp( (FLMBYTE *)pFindData->name, (FLMBYTE *)".") == 0)) + { + if( (iRetVal = Find2( pFindData)) != 0) + { + // If there were no more files found then return no more files + // instead of mapping to error path not found or io error. + // To return no more files ret_val is ENOENT (set in Find2) + // and errno is not set + + if( iRetVal == ENOENT && errno == 0) + { + rc = RC_SET( NE_FLM_IO_NO_MORE_FILES); + } + else + { + rc = MapPlatformError( errno, NE_FLM_READING_FILE); + } + + goto Exit; + } + } + } + + // Append the file name to the path name + + f_strcpy( pszFoundPath, pszSearchPath); + + if( RC_BAD( rc = gv_pFileSystem->pathAppend( pszFoundPath, + (char *)pFindData->name))) + { + goto Exit; + } + + *puiFoundAttrib = (FLMUINT)ReturnAttributes( + pFindData->FileStat.st_mode, pszFoundPath); + + // Save the search path in the NE_FLM_IO_FIND_DATA struct + // for a find next call + + f_strcpy( pFindData->search_path, pszSearchPath); + +Exit: + + return( rc); +#endif +} + +/**************************************************************************** +Desc: Find the next file that matches the supplied criteria +****************************************************************************/ +RCODE f_fileFindNext( + F_IO_FIND_DATA * pFindData, + char * pszFoundPath, + FLMUINT * puiFoundAttrib) +{ + RCODE rc = NE_FLM_OK; + +#ifdef FLM_WIN + + if( FindNextFile( pFindData->findHandle, + &(pFindData->findBuffer)) == FALSE) + { + rc = MapPlatformError( GetLastError(), NE_FLM_READING_FILE); + goto Exit; + } + + // Loop until a file with correct attributes is found + + for( ;;) + { + if( f_fileMeetsFindCriteria( pFindData)) + { + break; + } + + if( FindNextFile( pFindData->findHandle, + &(pFindData->findBuffer)) == FALSE) + { + rc = MapPlatformError( GetLastError(), NE_FLM_READING_FILE); + goto Exit; + } + } + + // Append the file name to the path name + + f_strcpy( pszFoundPath, pFindData->szSearchPath); + + if( RC_BAD( rc = gv_pFileSystem->pathAppend( pszFoundPath, + (char *)pFindData->findBuffer.cFileName))) + { + goto Exit; + } + + // Return the found file attribute + + *puiFoundAttrib = pFindData->findBuffer.dwFileAttributes; + +#elif defined( FLM_UNIX) || defined( FLM_NLM) + int iRetVal; + + if( (iRetVal = Find2( pFindData)) != 0) + { + // If there were no more files found then return no more files + // instead of mapping to error path not found or io error. + // To return no more files ret_val is ENOENT (set in Find2) + // and errno is not set + + if( iRetVal == ENOENT && errno == 0) + { + return( RC_SET( NE_FLM_IO_NO_MORE_FILES)); + } + + return( MapPlatformError( errno, NE_FLM_READING_FILE)); + } + + // Append the file name to the path name + + f_strcpy( pszFoundPath, pFindData->search_path); + + if( RC_BAD( rc = gv_pFileSystem->pathAppend( pszFoundPath, + (char *)pFindData->name))) + { + goto Exit; + } + + *puiFoundAttrib = (FLMUINT)ReturnAttributes( + pFindData->FileStat.st_mode, pszFoundPath); +#else + rc = RC_SET_AND_ASSERT( NE_FLM_NOT_IMPLEMENTED); + goto Exit; +#endif + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Releases any memory allocated to an F_IO_FIND_DATA structure +****************************************************************************/ +void f_fileFindClose( + F_IO_FIND_DATA * pFindData) +{ +#ifdef FLM_WIN + + // Don't call it on an already closed or invalid handle. + + if( pFindData->findHandle != INVALID_HANDLE_VALUE) + { + FindClose( pFindData->findHandle ); + pFindData->findHandle = INVALID_HANDLE_VALUE; + } +#elif defined( FLM_UNIX) || defined ( FLM_NLM) + if( pFindData->globbuf.gl_pathv) + { + pFindData->globbuf.gl_offs = 0; + globfree( &pFindData->globbuf); + pFindData->globbuf.gl_pathv = 0; + } +#endif +} + +/**************************************************************************** +Desc: Find the next file that matches the supplied criteria +****************************************************************************/ +#ifdef FLM_WIN +FSTATIC FLMBOOL f_fileMeetsFindCriteria( + F_IO_FIND_DATA * pFindData) +{ + // Fail ".." (PARENT) and "." (CURRENT) directories. Then, + // if the file found possesses any of the search attributes, it's + // a match. + + if( !((f_strcmp( pFindData->findBuffer.cFileName, "..") == 0) || + (f_strcmp( pFindData->findBuffer.cFileName, ".") == 0) || + (!(pFindData->uiSearchAttrib & F_IO_FA_DIRECTORY) && + (pFindData->findBuffer.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)))) + { + if( (pFindData->findBuffer.dwFileAttributes & + pFindData->uiSearchAttrib) || + ((pFindData->uiSearchAttrib & F_IO_FA_NORMAL) && + (pFindData->findBuffer.dwFileAttributes == 0))) + { + return( TRUE); + } + } + + return( FALSE); +} +#endif + +/**************************************************************************** +Desc: Search for file names matching FindTemplate (UNIX) +****************************************************************************/ +#if defined( FLM_UNIX) || defined( FLM_NLM) + +FSTATIC int Find1( + char * FindTemplate, + F_IO_FIND_DATA * DirInfo) +{ + char MaskNam[ F_PATH_MAX_SIZE]; + char *PathSeparator; + FLMINT uiFindLen; + FLMINT uiLen; +#ifdef FLM_NLM + char szPosixNam[ F_PATH_MAX_SIZE]; + FLMINT uiCount; +#endif + + // If supplied template is illegal, return immediately + + if( (FindTemplate == (char*)NULL) || !( uiFindLen = f_strlen( FindTemplate))) + { + return( EINVAL); + } + + // Now separate the template into a PATH and a template MASK + // If no separating slash character found, use current directory + // as path! + + f_strcpy( DirInfo->full_path, FindTemplate); + +#ifdef FLM_NLM + if( (( PathSeparator = strrchr( DirInfo->full_path, '/')) == NULL) && + ( PathSeparator = strrchr( DirInfo->full_path, '\\')) == NULL) +#else + if( (PathSeparator = strrchr( DirInfo->full_path, '/')) == NULL) +#endif + { + (void) getcwd( DirInfo->full_path, F_PATH_MAX_SIZE); + uiLen = f_strlen( DirInfo->full_path ); + DirInfo->full_path[uiLen] = '/'; + DirInfo->full_path[uiLen+1] = '\0'; + (void) f_strcat( DirInfo->full_path, FindTemplate ); + PathSeparator = strrchr( DirInfo->full_path, '/'); + } + + // Copy the template MASK, and null terminate the PATH + + f_strcpy( MaskNam, PathSeparator + 1); + + if( ! f_strlen(MaskNam)) + { + (void) f_strcpy( MaskNam, "*"); + } + + *PathSeparator = '\0'; + + // Use ROOT directory if PATH is empty + + if( ! f_strlen(DirInfo->full_path)) + { + (void) f_strcpy( DirInfo->full_path, "/"); + } + + f_strcpy( DirInfo->dirpath, DirInfo->full_path ); + + // Open the specified directory. Return immediately + // if error detected! + + errno = 0; + DirInfo->globbuf.gl_pathv = 0; + +#ifdef FLM_NLM + // glob does not seem to be able to handle a non-posix path + // on NetWare. + for( uiCount = 0; uiCount <= uiFindLen; uiCount++) + { + if( FindTemplate[ uiCount] == '\\') + { + szPosixNam[ uiCount] = '/'; + } + else + { + szPosixNam[ uiCount] = FindTemplate[ uiCount]; + } + } + if( glob( szPosixNam, GLOB_NOSORT, 0, &DirInfo->globbuf) != 0 && + !DirInfo->globbuf.gl_pathc) +#else + if( glob( FindTemplate, GLOB_NOSORT, 0, &DirInfo->globbuf) != 0 && + !DirInfo->globbuf.gl_pathc) +#endif + { + globfree(&DirInfo->globbuf); + DirInfo->globbuf.gl_pathv = 0; + return ENOENT; + } + + // Call Find2 to get the 1st matching file + + return( Find2(DirInfo) ); +} +#endif + + +/**************************************************************************** +Desc: Search for file names matching FindTemplate (UNIX) +****************************************************************************/ +#if defined( FLM_UNIX) || defined( FLM_NLM) +FSTATIC int Find2( + F_IO_FIND_DATA * DirStuff) +{ + int stat; + glob_t * pglob = &DirStuff->globbuf; + char * pszTmp; + char * pszLastSlash; + + errno = 0; + + for( ;;) + { + if( pglob->gl_offs == pglob->gl_pathc) + { + pglob->gl_offs = 0; + globfree(pglob); + pglob->gl_pathv = 0; + return ENOENT; + } + + // Get status of file + + f_strcpy(DirStuff->full_path, pglob->gl_pathv[pglob->gl_offs++]); + if( (stat = RetrieveFileStat( DirStuff->full_path, + &DirStuff->FileStat)) != 0 ) + { + // If file name just read from directory is NO + // longer there (deleted by another process) + // then just advance to the next file in + // directory! + + if( stat == ENOENT) + { + continue; + } + else + { + break; + } + } + + // If we don't want directories, and current entry + // is a directory, then skip it! + + if( (! S_ISDIR(DirStuff->mode_flag)) && + S_ISDIR(DirStuff->FileStat.st_mode)) + { + continue; + } + + // If we only want regular files and file is NOT + // regular, then skip it! This means there is no + // way to retrieve named pipes, sockets, or links! + + if ( (DirStuff->mode_flag == F_IO_FA_NORMAL) && + (! S_ISREG(DirStuff->FileStat.st_mode)) ) + { + continue; + } + + pszTmp = &DirStuff->full_path[ 0]; + pszLastSlash = NULL; + while( *pszTmp) + { + if( *pszTmp == '/') + { + pszLastSlash = pszTmp; + } + pszTmp++; + } + + if( pszLastSlash) + { + f_strcpy( DirStuff->name, &pszLastSlash[ 1]); + } + else + { + f_strcpy( DirStuff->name, DirStuff->full_path); + } + stat = 0; + break; + } + + return( stat); +} +#endif +/**************************************************************************** +Desc: Return file's attributes (UNIX) +****************************************************************************/ +#if defined( FLM_UNIX) || defined( FLM_NLM) +FSTATIC FLMBYTE ReturnAttributes( + mode_t FileMode, + char * fileName) +{ + FLMBYTE IOmode = 0; + + // Return the found file attribute + + if( S_ISDIR( FileMode ) ) + { + IOmode |= F_IO_FA_DIRECTORY; + } + else + { + if( access( (char *)fileName, (int)(R_OK | W_OK)) == 0) + { + IOmode |= F_IO_FA_NORMAL; + } + else if( access( (char *)fileName, (int)R_OK ) == 0) + { + IOmode |= F_IO_FA_RDONLY; + } + } + + return( IOmode); +} +#endif + +/**************************************************************************** +Desc: Return file's attributes (UNIX) || (NetWare) +****************************************************************************/ +#if defined( FLM_UNIX) || defined( FLM_NLM) +FSTATIC int RetrieveFileStat( + char * FilePath, + struct stat * StatusRec) +{ + // Get status of last file read from directory, using the standard + // UNIX stat call + + errno = 0; + if( stat( FilePath, StatusRec ) == -1) + { + if( errno == ENOENT || errno == ELOOP) + { + // Get status of symbolic link rather than referenced file! + + errno = 0; + if( lstat( FilePath, StatusRec ) == -1) + { + return( errno); + } + } + else + { + return( errno); + } + } + + return( 0); +} +#endif diff --git a/ftk/src/ftkdom.cpp b/ftk/src/ftkdom.cpp new file mode 100644 index 0000000..070fec7 --- /dev/null +++ b/ftk/src/ftkdom.cpp @@ -0,0 +1,27 @@ +//------------------------------------------------------------------------------ +// Desc: DOM node implementation +// +// Tabs: 3 +// +// Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: fdom.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + diff --git a/ftk/src/ftkerror.cpp b/ftk/src/ftkerror.cpp new file mode 100644 index 0000000..800ab54 --- /dev/null +++ b/ftk/src/ftkerror.cpp @@ -0,0 +1,112 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains error routines that are used throughout FLAIM. +// +// Tabs: 3 +// +// Copyright (c) 1997-2000, 2002-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: flerror.cpp 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +/**************************************************************************** +Desc: The primary purpose of this function is to provide a way to easily + trap errors when they occur. Just put a breakpoint in this function + to catch them. +****************************************************************************/ +#ifdef FLM_DEBUG +RCODE f_makeErr( + RCODE rc, + const char *, // pszFile, + int, // iLine, + FLMBOOL bAssert) +{ + if( rc == NE_FLM_OK) + { + return( NE_FLM_OK); + } + + // Switch on warning type return codes + + if( rc <= NE_FLM_NOT_FOUND) + { + switch(rc) + { + case NE_FLM_BOF_HIT: + break; + case NE_FLM_EOF_HIT: + break; + case NE_FLM_END: + break; + case NE_FLM_EXISTS: + break; + case NE_FLM_NOT_FOUND: + break; + } + + goto Exit; + } + + // Switch on errors + + switch( rc) + { + case NE_FLM_IO_BAD_FILE_HANDLE: + break; + case NE_FLM_MEM: + break; + case NE_FLM_SYNTAX: + break; + case NE_FLM_NOT_IMPLEMENTED: + break; + case NE_FLM_CONV_DEST_OVERFLOW: + break; + case NE_FLM_FAILURE: + break; + case NE_FLM_ILLEGAL_OP: + break; + default: + rc = rc; + break; + } + +Exit: + +#if defined( FLM_DEBUG) + if( bAssert) + { + flmAssert( 0); + } +#else + F_UNREFERENCED_PARM( bAssert); +#endif + + return( rc); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_WATCOM_NLM) +int gv_ftkerrorDummy(void) +{ + return( 0); +} +#endif diff --git a/ftk/src/ftkfsys.cpp b/ftk/src/ftkfsys.cpp new file mode 100644 index 0000000..7ec0da6 --- /dev/null +++ b/ftk/src/ftkfsys.cpp @@ -0,0 +1,1497 @@ +//------------------------------------------------------------------------------ +// Desc: Contains the methods for the F_FileSystem class. +// +// Tabs: 3 +// +// Copyright (c) 1998-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: ffilesys.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +/**************************************************************************** +Desc: Create a file, return a file handle to created file. +****************************************************************************/ +RCODE FLMAPI F_FileSystem::createFile( + const char * pszFileName, + FLMUINT uiIoFlags, + IF_FileHdl ** ppFileHdl) +{ + RCODE rc = NE_FLM_OK; + F_FileHdl * pFileHdl = NULL; + + if ((pFileHdl = f_new F_FileHdl) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if (RC_BAD( rc = pFileHdl->create( pszFileName, uiIoFlags))) + { + pFileHdl->Release(); + pFileHdl = NULL; + goto Exit; + } + +Exit: + + *ppFileHdl = (IF_FileHdl *)pFileHdl; + return( rc); +} + +/**************************************************************************** +Desc: Create a block-oriented file, return a file handle to created file. +****************************************************************************/ +RCODE FLMAPI F_FileSystem::createBlockFile( + const char * pszFileName, + FLMUINT uiIoFlags, + FLMUINT uiBlockSize, + IF_FileHdl ** ppFileHdl) +{ + RCODE rc = NE_FLM_OK; + F_FileHdl * pFileHdl = NULL; + + if ((pFileHdl = f_new F_FileHdl) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + pFileHdl->setBlockSize( uiBlockSize); + + if (RC_BAD( rc = pFileHdl->create( pszFileName, uiIoFlags))) + { + pFileHdl->Release(); + pFileHdl = NULL; + goto Exit; + } + +Exit: + + *ppFileHdl = (IF_FileHdl *)pFileHdl; + return( rc); +} + +/**************************************************************************** +Desc: Create a unique file, return a file handle to created file. +****************************************************************************/ +RCODE FLMAPI F_FileSystem::createUniqueFile( + const char * pszDirName, + const char * pszFileExtension, + FLMUINT uiIoFlags, + IF_FileHdl ** ppFileHdl) +{ + RCODE rc; + F_FileHdl * pFileHdl = NULL; + + if ((pFileHdl = f_new F_FileHdl) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + if( RC_BAD( rc = pFileHdl->createUnique( pszDirName, + pszFileExtension, uiIoFlags))) + { + pFileHdl->Release(); + pFileHdl = NULL; + goto Exit; + } + +Exit: + + *ppFileHdl = (IF_FileHdl *)pFileHdl; + return( rc); +} + +/**************************************************************************** +Desc: Open a file, return a file handle to opened file. +****************************************************************************/ +RCODE FLMAPI F_FileSystem::openFile( + const char * pszFileName, + FLMUINT uiIoFlags, + IF_FileHdl ** ppFileHdl) +{ + RCODE rc = NE_FLM_OK; + F_FileHdl * pFileHdl = NULL; + + if ((pFileHdl = f_new F_FileHdl) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + if (RC_BAD( rc = pFileHdl->open( pszFileName, uiIoFlags))) + { + pFileHdl->Release(); + pFileHdl = NULL; + goto Exit; + } + +Exit: + + *ppFileHdl = (IF_FileHdl *)pFileHdl; + return( rc); +} + +/**************************************************************************** +Desc: Open a block-oriented file, return a file handle to opened file. +****************************************************************************/ +RCODE FLMAPI F_FileSystem::openBlockFile( + const char * pszFileName, + FLMUINT uiIoFlags, + FLMUINT uiBlockSize, + IF_FileHdl ** ppFileHdl) +{ + RCODE rc = NE_FLM_OK; + F_FileHdl * pFileHdl = NULL; + + if ((pFileHdl = f_new F_FileHdl) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + pFileHdl->setBlockSize( uiBlockSize); + + if (RC_BAD( rc = pFileHdl->open( pszFileName, uiIoFlags))) + { + pFileHdl->Release(); + pFileHdl = NULL; + goto Exit; + } + +Exit: + + *ppFileHdl = (IF_FileHdl *)pFileHdl; + return( rc); +} + +/**************************************************************************** +Desc: Open a directory, return a file handle to opened directory. +****************************************************************************/ +RCODE FLMAPI F_FileSystem::openDir( + const char * pszDirName, + const char * pszPattern, + IF_DirHdl ** ppDirHdl) +{ + RCODE rc = NE_FLM_OK; + F_DirHdl * pDirHdl = NULL; + + if ((pDirHdl = f_new F_DirHdl) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + if (RC_BAD( rc = pDirHdl->openDir( pszDirName, pszPattern))) + { + pDirHdl->Release(); + pDirHdl = NULL; + } + +Exit: + + *ppDirHdl = (IF_DirHdl *)pDirHdl; + return( rc); +} + +/**************************************************************************** +Desc: Create a directory. +****************************************************************************/ +RCODE FLMAPI F_FileSystem::createDir( + const char * pszDirName) +{ + RCODE rc = NE_FLM_OK; + F_DirHdl * pDirHdl = NULL; + + if ((pDirHdl = f_new F_DirHdl) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + rc = pDirHdl->createDir( pszDirName); + +Exit: + + if (pDirHdl) + { + pDirHdl->Release(); + } + return( rc); +} + +/**************************************************************************** +Desc: Remove a directory +****************************************************************************/ +RCODE FLMAPI F_FileSystem::removeDir( + const char * pszDirName, + FLMBOOL bClear) +{ + RCODE rc = NE_FLM_OK; + IF_DirHdl * pDirHdl = NULL; + char szFilePath[ F_PATH_MAX_SIZE]; + + if( bClear) + { + if( RC_BAD( rc = openDir( pszDirName, (char *)"*", &pDirHdl))) + { + goto Exit; + } + + for ( rc = pDirHdl->next(); RC_OK( rc) ; rc = pDirHdl->next()) + { + pDirHdl->currentItemPath( szFilePath); + if( !pDirHdl->currentItemIsDir()) + { + if( RC_BAD( rc = deleteFile( szFilePath))) + { + if( rc == NE_FLM_IO_PATH_NOT_FOUND || + rc == NE_FLM_IO_INVALID_FILENAME) + { + rc = NE_FLM_OK; + } + else + { + goto Exit; + } + } + } + else + { + if( RC_BAD( rc = removeDir( szFilePath, bClear))) + { + if( rc == NE_FLM_IO_PATH_NOT_FOUND || + rc == NE_FLM_IO_INVALID_FILENAME) + { + rc = NE_FLM_OK; + } + else + { + goto Exit; + } + } + } + } + + // Need to release the directory handle so the + // directory will be closed when we try to delete it + // below. + + pDirHdl->Release(); + pDirHdl = NULL; + } + + if( RC_BAD( rc = removeDir( pszDirName))) + { + goto Exit; + } + +Exit: + + if (pDirHdl) + { + pDirHdl->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Determine if a file or directory exists. +****************************************************************************/ +RCODE FLMAPI F_FileSystem::doesFileExist( + const char * pszPath) +{ +#if defined( FLM_NLM) + + return( flmNetWareTestIfFileExists( pszPath)); + +#elif defined( FLM_WIN) + + DWORD dwFileAttr = GetFileAttributes( (LPTSTR)pszPath); + + if( dwFileAttr == (DWORD)-1) + return RC_SET( NE_FLM_IO_PATH_NOT_FOUND); + + return NE_FLM_OK; + +#else + + if( access( pszPath, F_OK) == -1) + { + return( MapErrnoToFlaimErr( errno, NE_FLM_CHECKING_FILE_EXISTENCE)); + } + + return( NE_FLM_OK); + +#endif +} + +/**************************************************************************** +Desc: Get the time stamp of the last modification to this file. +Notes: + puiTimeStamp is assumed to point to a DWORD. + +NLM Notes: + We could call MapPathToDirectoryNumber and GetDirectoryEntry directly. + This works, providing that the high byte of the directory entry (returned + by MapPathToDirectoryNumber) is masked off. Otherwise, GetDirectoryEntry + will generate an abend. + We have opted to call a higher level function, GetEntryFromPathStringBase, + which calls the lower level functions for us. +****************************************************************************/ +RCODE FLMAPI F_FileSystem::getFileTimeStamp( + const char * pszPath, + FLMUINT * puiTimeStamp) +{ +#if defined( FLM_NLM) + + return( flmNetWareGetFileTimeStamp( pszPath, puiTimeStamp)); + +#elif defined( FLM_WIN) + + WIN32_FIND_DATA find_data; + FILETIME ftLocalFileTime; + SYSTEMTIME stLastFileWriteTime; + HANDLE hSearch = INVALID_HANDLE_VALUE; + RCODE rc = NE_FLM_OK; + F_TMSTAMP tmstamp; + + hSearch = FindFirstFile( (LPTSTR)pszPath, &find_data); + if( hSearch == INVALID_HANDLE_VALUE) + { + rc = MapPlatformError( GetLastError(), NE_FLM_OPENING_FILE); + switch( rc) + { + case NE_FLM_IO_NO_MORE_FILES: + rc = RC_SET( NE_FLM_IO_PATH_NOT_FOUND); + goto Exit; + default: + goto Exit; + } /* End switch. */ + } + + // Convert it to a local time, so we can adjust based on our own + // GroupWise time zone. + + if( FileTimeToLocalFileTime( &(find_data.ftLastWriteTime), + &ftLocalFileTime) == FALSE) + { + rc = MapPlatformError( GetLastError(), NE_FLM_OPENING_FILE); + goto Exit; + } + + // Convert the local time to a system time so we can map it into + // a GroupWise Date\Time structure + + if( FileTimeToSystemTime( &ftLocalFileTime, + &stLastFileWriteTime) == FALSE) + { + rc = MapPlatformError( GetLastError(), NE_FLM_OPENING_FILE); + goto Exit; + } + + /* Fill the Time Stamp structure */ + f_memset( &tmstamp, 0, sizeof( F_TMSTAMP)); + tmstamp.hour = (FLMBYTE)stLastFileWriteTime.wHour; + tmstamp.minute = (FLMBYTE)stLastFileWriteTime.wMinute; + tmstamp.second = (FLMBYTE)stLastFileWriteTime.wSecond; + tmstamp.hundredth = (FLMBYTE)stLastFileWriteTime.wMilliseconds; + tmstamp.year = (FLMUINT16)stLastFileWriteTime.wYear; + tmstamp.month = (FLMBYTE)(stLastFileWriteTime.wMonth - 1); + tmstamp.day = (FLMBYTE)stLastFileWriteTime.wDay; + + /* Convert and return the file time stamp as seconds since January 1, 1970 */ + f_timeDateToSeconds( &tmstamp, puiTimeStamp); + +Exit: + + if( hSearch != INVALID_HANDLE_VALUE) + { + FindClose( hSearch); + } + + if( RC_OK(rc)) + { + *puiTimeStamp = f_localTimeToUTC( *puiTimeStamp); + } + + return( rc); + +#else + + struct stat filestatus; + + if( stat( pszPath, &filestatus) == -1) + { + return( MapErrnoToFlaimErr( errno, NE_FLM_GETTING_FILE_INFO)); + } + + *puiTimeStamp = (FLMUINT)filestatus.st_mtime; // st_mtime is UTC + return NE_FLM_OK; + +#endif +} + +/**************************************************************************** +Desc: Determine if a path is a directory. +****************************************************************************/ +FLMBOOL FLMAPI F_FileSystem::isDir( + const char * pszDirName) +{ +#if defined( FLM_WIN) + + DWORD FileAttr = GetFileAttributes( (LPTSTR)pszDirName); + + if( FileAttr == 0xFFFFFFFF) + { + return( FALSE); + } + + return (FileAttr & FILE_ATTRIBUTE_DIRECTORY) ? TRUE : FALSE; + +#else + + struct stat filestatus; + + if( stat( (char *)pszDirName, &filestatus) == -1) + { + return FALSE; + } + + return ( S_ISDIR( filestatus.st_mode)) ? TRUE : FALSE; +#endif +} + +/**************************************************************************** +Desc: Delete a file or directory +****************************************************************************/ +RCODE FLMAPI F_FileSystem::deleteFile( + const char * pszFileName) +{ +#if defined( FLM_NLM) + + return( flmNetWareDeleteFile( pszFileName)); + +#elif defined( FLM_WIN) + + if( DeleteFile( (LPTSTR)pszFileName) == FALSE) + { + return( MapPlatformError( GetLastError(), NE_FLM_IO_DELETING_FILE)); + } + + return( NE_FLM_OK); + +#else + + struct stat FileStat; + + if( stat( (char *)pszFileName, &FileStat) == -1) + { + return( MapErrnoToFlaimErr( errno, NE_FLM_GETTING_FILE_INFO)); + } + + // Ensure that the path does NOT designate a directory for deletion + + if( S_ISDIR(FileStat.st_mode)) + { + return( RC_SET( NE_FLM_IO_ACCESS_DENIED)); + } + + // Delete the file + + if( unlink( (char *)pszFileName) == -1) + { + return( MapErrnoToFlaimErr( errno, NE_FLM_IO_DELETING_FILE)); + } + + return( NE_FLM_OK); + +#endif +} + +/**************************************************************************** +Desc: Copy a file. +****************************************************************************/ +RCODE FLMAPI F_FileSystem::copyFile( + const char * pszSrcFileName, // Name of source file to be copied. + const char * pszDestFileName, // Name of destination file. + FLMBOOL bOverwrite, // Overwrite destination file? + FLMUINT64 * pui64BytesCopied) // Returns number of bytes copied. +{ + RCODE rc = NE_FLM_OK; + IF_FileHdl * pSrcFileHdl = NULL; + IF_FileHdl * pDestFileHdl = NULL; + FLMBOOL bCreatedDest = FALSE; + FLMUINT64 ui64SrcSize; + + // See if the destination file exists. If it does, see if it is + // OK to overwrite it. If so, delete it. + + if (doesFileExist( pszDestFileName) == NE_FLM_OK) + { + if (!bOverwrite) + { + rc = RC_SET( NE_FLM_IO_ACCESS_DENIED); + goto Exit; + } + if (RC_BAD( rc = deleteFile( pszDestFileName))) + { + goto Exit; + } + } + + // Open the source file. + + if( RC_BAD( rc = openFile( pszSrcFileName, + FLM_IO_RDONLY | FLM_IO_SH_DENYNONE, &pSrcFileHdl))) + { + goto Exit; + } + + if (RC_BAD( rc = pSrcFileHdl->size( &ui64SrcSize))) + { + goto Exit; + } + + // Create the destination file. + + if( RC_BAD( rc = createFile( pszDestFileName, + FLM_IO_RDWR | FLM_IO_SH_DENYNONE, &pDestFileHdl))) + { + goto Exit; + } + bCreatedDest = TRUE; + + // Do the copy. + + if( RC_BAD( rc = f_copyPartial( pSrcFileHdl, 0, ui64SrcSize, + pDestFileHdl, 0, pui64BytesCopied))) + { + goto Exit; + } + +Exit: + + if( pSrcFileHdl) + { + pSrcFileHdl->close(); + pSrcFileHdl->Release(); + } + + if( pDestFileHdl) + { + pDestFileHdl->close(); + pDestFileHdl->Release(); + } + + if( RC_BAD( rc)) + { + if( bCreatedDest) + { + (void)deleteFile( pszDestFileName); + } + + *pui64BytesCopied = 0; + } + + return( rc); +} + +/**************************************************************************** +Desc: Rename a file. +****************************************************************************/ +RCODE FLMAPI F_FileSystem::renameFile( + const char * pszFileName, + const char * pszNewFileName) +{ +#if defined( FLM_NLM) + + return( flmNetWareRenameFile( pszFileName, pszNewFileName)); + +#elif defined( FLM_WIN) + + DWORD error; + RCODE rc = NE_FLM_OK; + FLMUINT64 ui64BytesCopied; + + // Try to move the file by doing a rename first, otherwise copy the file + + if( (MoveFile( (LPTSTR)pszFileName, (LPTSTR)pszNewFileName)) != TRUE) + { + error = GetLastError(); + switch( error) + { + case ERROR_NOT_SAME_DEVICE: + case ERROR_NO_MORE_FILES: + case NO_ERROR: + if( copyFile( pszFileName, pszNewFileName, TRUE, &ui64BytesCopied)) + { + rc = RC_SET( NE_FLM_IO_COPY_ERR); + } + else + { + rc = F_FileSystem::deleteFile( pszFileName); + } + break; + default: + rc = MapPlatformError( error, NE_FLM_RENAMING_FILE); + break; + } + } + + return( rc); + +#else + + RCODE rc; + FLMBOOL bSrcIsDir; + FLMUINT64 ui64BytesCopied; + + if( RC_BAD( rc = unix_TargetIsDir( (char*)pszFileName, &bSrcIsDir))) + { + return( rc); + } + + errno = 0; + + if( RC_BAD( unix_RenameSafe( pszFileName, pszNewFileName))) + { + switch( errno) + { + case EXDEV: + { + if( bSrcIsDir) + { + return( RC_SET( NE_FLM_IO_PATH_CREATE_FAILURE)); + } + else + { + if( copyFile( pszFileName, pszNewFileName, TRUE, &ui64BytesCopied)) + { + return( RC_SET( NE_FLM_IO_COPY_ERR)); + } + + F_FileSystem::deleteFile( pszFileName); + return( NE_FLM_OK); + } + } + + default: + { + if( errno == ENOENT) + { + return( RC_SET( NE_FLM_IO_RENAME_FAILURE)); + } + else + { + return( MapErrnoToFlaimErr( errno, NE_FLM_RENAMING_FILE)); + } + } + } + } + + return( NE_FLM_OK); +#endif +} + +/**************************************************************************** +Desc: Get the sector size (not supported on all platforms). +****************************************************************************/ +RCODE FLMAPI F_FileSystem::getSectorSize( + const char * pszFileName, + FLMUINT * puiSectorSize) +{ +#ifdef FLM_NLM + + F_UNREFERENCED_PARM( pszFileName); + *puiSectorSize = NETWARE_SECTOR_SIZE; + return( NE_FLM_OK); + +#elif defined( FLM_WIN) + + RCODE rc = NE_FLM_OK; + DWORD udSectorsPerCluster; + DWORD udBytesPerSector; + DWORD udNumberOfFreeClusters; + DWORD udTotalNumberOfClusters; + char szVolume [256]; + char * pszVolume; + FLMUINT uiLen; + + if (!pszFileName) + { + pszVolume = NULL; + } + else + { + pathParse( pszFileName, NULL, szVolume, NULL, NULL); + if (!szVolume [0]) + { + pszVolume = NULL; + } + else + { + uiLen = f_strlen( szVolume); + if (szVolume [uiLen - 1] == ':') + { + szVolume [uiLen] = '\\'; + szVolume [uiLen + 1] = 0; + } + pszVolume = &szVolume [0]; + } + } + + if (!GetDiskFreeSpace( (LPCTSTR)pszVolume, &udSectorsPerCluster, + &udBytesPerSector, &udNumberOfFreeClusters, + &udTotalNumberOfClusters)) + { + rc = MapPlatformError( GetLastError(), NE_FLM_INITIALIZING_IO_SYSTEM); + *puiSectorSize = 0; + goto Exit; + } + *puiSectorSize = (FLMUINT)udBytesPerSector; + +Exit: + + return( rc); + +#else + F_UNREFERENCED_PARM( pszFileName); + *puiSectorSize = (FLMUINT)sysconf( _SC_PAGESIZE); + return( NE_FLM_OK); +#endif +} + +/**************************************************************************** +Desc: Set the Read-Only Attribute (not supported on all platforms). +****************************************************************************/ +RCODE F_FileSystem::setReadOnly( + const char * pszFileName, + FLMBOOL bReadOnly) +{ + RCODE rc = NE_FLM_OK; + +#if defined( FLM_UNIX) + struct stat filestatus; + + if( stat( (char *)pszFileName, &filestatus)) + { + rc = RC_SET_AND_ASSERT( NE_FLM_FAILURE); + goto Exit; + } + + if ( bReadOnly) + { + filestatus.st_mode &= ~S_IWUSR; + } + else + { + filestatus.st_mode |= S_IWUSR; + } + + if ( chmod( (char *)pszFileName, filestatus.st_mode)) + { + rc = RC_SET( NE_FLM_FAILURE); + goto Exit; + } + +#elif defined( FLM_WIN) + + DWORD dwAttr; + + dwAttr = GetFileAttributes( (LPTSTR)pszFileName); + if( dwAttr == (DWORD)-1) + { + rc = RC_SET( NE_FLM_IO_PATH_NOT_FOUND); + goto Exit; + } + + if ( bReadOnly) + { + dwAttr |= F_IO_FA_RDONLY; + } + else + { + dwAttr &= ~F_IO_FA_RDONLY; + } + + if( !SetFileAttributes( (LPTSTR)pszFileName, dwAttr)) + { + rc = RC_SET_AND_ASSERT( NE_FLM_FAILURE); + goto Exit; + } +#elif defined( FLM_NLM) + + if ( RC_BAD( rc = flmNetWareSetReadOnly( pszFileName, bReadOnly))) + { + goto Exit; + } +#else + rc = RC_SET_AND_ASSERT( NE_FLM_NOT_IMPLEMENTED); +#endif + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: stat tpath to see if it is a directory +****************************************************************************/ +#if defined( FLM_UNIX) +RCODE F_FileSystem::unix_TargetIsDir( + const char * tpath, + FLMBOOL * isdir) +{ + struct stat sbuf; + RCODE rc = NE_FLM_OK; + + *isdir = 0; + if( stat(tpath, &sbuf) < 0) + { + rc = MapErrnoToFlaimErr( errno, NE_FLM_IO_ACCESS_DENIED); + } + else if( (sbuf.st_mode & S_IFMT) == S_IFDIR) + { + *isdir = 1; + } + + return( rc); +} +#endif + +/**************************************************************************** +Desc: Rename an existing file (typically an "X" locked file to an + unlocked file) using a safe (non-race) method. To ensure that + an existing file is not being overwritten by a rename operation, + we will first create a new file with the desired name (using the + CREAT and EXCL options, (ensuring a unique file name)). Then, + the source file will be renamed to new name. +****************************************************************************/ +#if defined( FLM_UNIX) +RCODE F_FileSystem::unix_RenameSafe( + const char * pszSrcFile, + const char * pszDestFile) +{ + RCODE rc = NE_FLM_OK; + struct stat temp_stat_buf; + + errno = 0; + if( stat( pszDestFile, &temp_stat_buf) != -1) + { + // If we were able to stat it, then the file obviously exists... + + rc = RC_SET( NE_FLM_IO_RENAME_FAILURE); + goto Exit; + } + else + { + if (errno != ENOENT) + { + // ENOENT means the file didn't exist, which is what we were + // hoping for. + + rc = MapErrnoToFlaimErr( errno, NE_FLM_IO_RENAME_FAILURE); + goto Exit; + } + } + + errno = 0; + if( rename( pszSrcFile, pszDestFile) != 0) + { + rc = MapErrnoToFlaimErr( errno, NE_FLM_IO_RENAME_FAILURE); + } + +Exit: + + return( rc); +} +#endif + +/**************************************************************************** +Desc: Initializes variables +****************************************************************************/ +F_FileHdlMgr::F_FileHdlMgr() +{ + m_hMutex = F_MUTEX_NULL; + + m_uiOpenThreshold = 0xFFFF; + FLM_SECS_TO_TIMER_UNITS( 30 * 60, m_uiMaxAvailTime); + m_bIsSetup = FALSE; + + m_uiFileIdCounter = 0; + + m_pFirstAvail = NULL; + m_pLastAvail = NULL; + m_uiNumAvail = 0; + + m_pFirstUsed = NULL; + m_pLastUsed = NULL; + m_uiNumUsed = 0; + +} + +/**************************************************************************** +Desc: Setup the File handle manager. +****************************************************************************/ +RCODE F_FileHdlMgr::setupFileHdlMgr( + FLMUINT uiOpenThreshold, + FLMUINT uiMaxAvailTime) +{ + RCODE rc = NE_FLM_OK; + + if (RC_BAD( rc = f_mutexCreate( &m_hMutex))) + { + goto Exit; + } + + m_uiOpenThreshold = uiOpenThreshold; + m_uiMaxAvailTime = uiMaxAvailTime; + m_bIsSetup = TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Return the next available file handle that matches the uiFileId. +****************************************************************************/ +void F_FileHdlMgr::findAvail( + FLMUINT uiFileId, + FLMBOOL bReadOnlyFlag, + F_FileHdl ** ppFileHdl) +{ + F_FileHdl * pFileHdl; + + lockMutex( FALSE); + pFileHdl = m_pFirstAvail; + while (pFileHdl) + { + if (pFileHdl->m_uiFileId == uiFileId && + pFileHdl->m_bOpenedReadOnly == bReadOnlyFlag) + { + + // Move this file handle out of the available list into + // the used list. + + // NOTE: To prevent this file handle from being freed this code + // performs an AddRef while its being relinked. This reference + // will be kept for the caller. + + pFileHdl->AddRef(); + + removeFromList( TRUE, + pFileHdl, &m_pFirstAvail, &m_pLastAvail, &m_uiNumAvail); + + insertInList( TRUE, pFileHdl, FALSE, + &m_pFirstUsed, &m_pLastUsed, &m_uiNumUsed); + + break; + } + + pFileHdl = pFileHdl->m_pNext; + } + + unlockMutex( FALSE); + *ppFileHdl = pFileHdl; +} + +/**************************************************************************** +Desc: Make the specified F_FileHdl available for someone else to use. +****************************************************************************/ +void F_FileHdlMgr::makeAvailAndRelease( + FLMBOOL bMutexAlreadyLocked, + F_FileHdl * pFileHdl) +{ + pFileHdl->m_uiAvailTime = (FLMUINT)FLM_GET_TIMER(); + + lockMutex( bMutexAlreadyLocked); + + pFileHdl->AddRef(); + + removeFromList( TRUE, + pFileHdl, &m_pFirstUsed, &m_pLastUsed, &m_uiNumUsed); + + insertInList( TRUE, pFileHdl, TRUE, + &m_pFirstAvail, &m_pLastAvail, &m_uiNumAvail); + + pFileHdl->Release(); + pFileHdl->Release(); + + unlockMutex( bMutexAlreadyLocked); +} + +/**************************************************************************** +Desc: Remove (close&free) all FileHdl's that have the specified FileId. + Remove from the avail and used lists. +****************************************************************************/ +void F_FileHdlMgr::removeFileHdls( + FLMUINT uiFileId) +{ + F_FileHdl * pFileHdl; + F_FileHdl * pNextFileHdl; + + lockMutex( FALSE); + + // Free all matching file handles in the available list. + + pFileHdl = m_pFirstAvail; + while (pFileHdl) + { + pNextFileHdl = pFileHdl->m_pNext; + if (pFileHdl->m_uiFileId == uiFileId) + { + removeFromList( TRUE, + pFileHdl, &m_pFirstAvail, &m_pLastAvail, &m_uiNumAvail); + } + + pFileHdl = pNextFileHdl; + } + + // Free all matching file handles in the used list. + + pFileHdl = m_pFirstUsed; + while (pFileHdl) + { + pNextFileHdl = pFileHdl->m_pNext; + if (pFileHdl->m_uiFileId == uiFileId) + { + removeFromList( TRUE, + pFileHdl, &m_pFirstUsed, &m_pLastUsed, &m_uiNumUsed); + } + + pFileHdl = pNextFileHdl; + } + + unlockMutex( FALSE); +} + +/**************************************************************************** +Desc: Remove all handles from the avail list. +****************************************************************************/ +void F_FileHdlMgr::freeAvailList( + FLMBOOL bMutexAlreadyLocked) +{ + F_FileHdl * pFileHdl; + F_FileHdl * pNextFileHdl; + + lockMutex( bMutexAlreadyLocked); + pFileHdl = m_pFirstAvail; + while (pFileHdl) + { + pFileHdl->m_bInList = FALSE; + pNextFileHdl = pFileHdl->m_pNext; + pFileHdl->Release(); + pFileHdl = pNextFileHdl; + } + + m_pFirstAvail = NULL; + m_pLastAvail = NULL; + m_uiNumAvail = 0; + + unlockMutex( bMutexAlreadyLocked); +} + +/**************************************************************************** +Desc: Remove all handles from the used list. +****************************************************************************/ +void F_FileHdlMgr::freeUsedList( + FLMBOOL bMutexAlreadyLocked) +{ + F_FileHdl * pFileHdl; + F_FileHdl * pNextFileHdl; + + lockMutex( bMutexAlreadyLocked); + + pFileHdl = m_pFirstUsed; + while (pFileHdl) + { + pFileHdl->m_bInList = FALSE; + pNextFileHdl = pFileHdl->m_pNext; + pFileHdl->Release(); + pFileHdl = pNextFileHdl; + } + + m_pFirstUsed = NULL; + m_pLastUsed = NULL; + m_uiNumUsed = 0; + + unlockMutex( bMutexAlreadyLocked); +} + +/**************************************************************************** +Desc: Insert a handle into either the avail or used list. +****************************************************************************/ +void F_FileHdlMgr::insertInList( + FLMBOOL bMutexAlreadyLocked, + F_FileHdl * pFileHdl, + FLMBOOL bInsertAtEnd, + F_FileHdl ** ppFirst, + F_FileHdl ** ppLast, + FLMUINT * puiCount) +{ + lockMutex( bMutexAlreadyLocked); + + flmAssert( !pFileHdl->m_bInList); + + if (bInsertAtEnd) + { + pFileHdl->m_pNext = NULL; + if ((pFileHdl->m_pPrev = *ppLast) != NULL) + { + pFileHdl->m_pPrev->m_pNext = pFileHdl; + } + else + { + *ppFirst = pFileHdl; + } + + *ppLast = pFileHdl; + } + else + { + pFileHdl->m_pPrev = NULL; + if ((pFileHdl->m_pNext = *ppFirst) != NULL) + { + pFileHdl->m_pNext->m_pPrev = pFileHdl; + } + else + { + *ppLast = pFileHdl; + } + + *ppFirst = pFileHdl; + } + + (*puiCount)++; + pFileHdl->m_bInList = TRUE; + pFileHdl->AddRef(); + + unlockMutex( bMutexAlreadyLocked); +} + +/**************************************************************************** +Desc: Remove a handle into either the avail or used list. +****************************************************************************/ +void F_FileHdlMgr::removeFromList( + FLMBOOL bMutexAlreadyLocked, + F_FileHdl * pFileHdl, + F_FileHdl ** ppFirst, + F_FileHdl ** ppLast, + FLMUINT * puiCount) +{ + lockMutex( bMutexAlreadyLocked); + + flmAssert( pFileHdl->m_bInList); + if (pFileHdl->m_pNext) + { + pFileHdl->m_pNext->m_pPrev = pFileHdl->m_pPrev; + } + else + { + *ppLast = pFileHdl->m_pPrev; + } + + if (pFileHdl->m_pPrev) + { + pFileHdl->m_pPrev->m_pNext = pFileHdl->m_pNext; + } + else + { + *ppFirst = pFileHdl->m_pNext; + } + + flmAssert( *puiCount); + + (*puiCount)--; + pFileHdl->m_bInList = FALSE; + pFileHdl->Release(); + + unlockMutex( bMutexAlreadyLocked); +} + +/**************************************************************************** +Desc: Check items in the avail list and if over a certain age then + remove them from the avail list. This will cause file handles + that have been opened for a long time to be closed. Also added + code to reduce the total number of file handles if it is more + than the open threshold. +****************************************************************************/ +void F_FileHdlMgr::checkAgedFileHdls( + FLMUINT uiMinTimeOpened) +{ + FLMUINT uiTime; + FLMUINT uiMaxAvailTicks; + + uiTime = (FLMUINT)FLM_GET_TIMER(); + + FLM_SECS_TO_TIMER_UNITS( uiMinTimeOpened, uiMaxAvailTicks); + + lockMutex( FALSE); + + // Loop while the open count is greater than the open threshold. + + while (m_uiNumAvail && (m_uiNumAvail + m_uiNumUsed > m_uiOpenThreshold)) + { + // Release until the threshold is down. + + releaseOneAvail( TRUE); + } + + // Reduce all items older than the specified time. + + while (m_pFirstAvail) + { + // All file handles are in order of oldest first. + // m_uiMaxAvailTime may be a zero value. + + if( FLM_ELAPSED_TIME( uiTime, + m_pFirstAvail->m_uiAvailTime) < uiMaxAvailTicks) + { + // All files are newer so we are done. + + break; + } + + removeFromList( TRUE, + m_pFirstAvail, &m_pFirstAvail, &m_pLastAvail, &m_uiNumAvail); + } + + unlockMutex( FALSE); +} + +/**************************************************************************** +Desc: Do a partial copy from one file into another file. +****************************************************************************/ +RCODE FLMAPI f_copyPartial( + IF_FileHdl * pSrcFileHdl, // Source file handle. + FLMUINT64 ui64SrcOffset, // Offset to start copying from. + FLMUINT64 ui64SrcSize, // Bytes to copy + IF_FileHdl * pDestFileHdl, // Destination file handle + FLMUINT64 ui64DestOffset, // Destination start offset. + FLMUINT64 * pui64BytesCopiedRV) // Returns number of bytes copied +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucBuffer = NULL; + FLMUINT uiAllocSize = 65536; + FLMUINT uiBytesToRead; + FLMUINT64 ui64CopySize; + FLMUINT64 ui64FileOffset; + FLMUINT uiBytesRead; + FLMUINT uiBytesWritten; + + ui64CopySize = ui64SrcSize; + *pui64BytesCopiedRV = 0; + + // Set the buffer size for use during the file copy + + if( ui64CopySize < uiAllocSize) + { + uiAllocSize = (FLMUINT)ui64CopySize; + } + + // Allocate a buffer + + if( RC_BAD( rc = f_alloc( uiAllocSize, &pucBuffer))) + { + goto Exit; + } + + // Position the file pointers + + if( RC_BAD( rc = pSrcFileHdl->seek( ui64SrcOffset, FLM_IO_SEEK_SET, + &ui64FileOffset))) + { + goto Exit; + } + + if( RC_BAD( rc = pDestFileHdl->seek( ui64DestOffset, FLM_IO_SEEK_SET, + &ui64FileOffset))) + { + goto Exit; + } + + // Begin copying the data + + while( ui64CopySize) + { + if( ui64CopySize > uiAllocSize) + { + uiBytesToRead = uiAllocSize; + } + else + { + uiBytesToRead = (FLMUINT)ui64CopySize; + } + + rc = pSrcFileHdl->read( FLM_IO_CURRENT_POS, uiBytesToRead, + pucBuffer, &uiBytesRead); + + if (rc == NE_FLM_IO_END_OF_FILE) + { + rc = NE_FLM_OK; + } + + if (RC_BAD( rc)) + { + rc = RC_SET( NE_FLM_IO_COPY_ERR); + goto Exit; + } + + uiBytesWritten = 0; + if( RC_BAD( rc = pDestFileHdl->write( FLM_IO_CURRENT_POS, uiBytesRead, + pucBuffer, &uiBytesWritten))) + { + if (rc == NE_FLM_IO_DISK_FULL) + { + *pui64BytesCopiedRV += uiBytesWritten; + } + else + { + rc = RC_SET( NE_FLM_IO_COPY_ERR); + } + + goto Exit; + } + + *pui64BytesCopiedRV += uiBytesWritten; + + if( uiBytesRead < uiBytesToRead) + { + rc = RC_SET( NE_FLM_IO_END_OF_FILE); + goto Exit; + } + + ui64CopySize -= uiBytesRead; + } + +Exit: + + if (pucBuffer) + { + (void)f_free( &pucBuffer); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FLMAPI f_filecpy( + const char * pszSourceFile, + const char * pszData) +{ + RCODE rc = NE_FLM_OK; + IF_FileHdl * pFileHdl = NULL; + F_FileSystem fileSystem; + FLMUINT uiBytesWritten = 0; + + if( RC_OK( rc = fileSystem.doesFileExist( pszSourceFile))) + { + if( RC_BAD( rc = fileSystem.deleteFile( pszSourceFile))) + { + goto Exit; + } + } + + if( RC_BAD( rc = fileSystem.createFile( pszSourceFile, FLM_IO_RDWR, + &pFileHdl))) + { + goto Exit; + } + + if( RC_BAD( rc = pFileHdl->write( 0, f_strlen( pszData), (void *)pszData, + &uiBytesWritten))) + { + goto Exit; + } + +Exit: + + if( pFileHdl) + { + pFileHdl->close(); + pFileHdl->Release(); + pFileHdl = NULL; + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FLMAPI f_filecat( + const char * pszSourceFile, + const char * pszData) +{ + RCODE rc = NE_FLM_OK; + IF_FileHdl * pFileHdl = NULL; + F_FileSystem fileSystem; + FLMUINT64 ui64FileSize = 0; + FLMUINT uiBytesWritten = 0; + + if (RC_BAD( rc = fileSystem.doesFileExist( pszSourceFile))) + { + if( rc == NE_FLM_IO_PATH_NOT_FOUND) + { + if( RC_BAD( rc = fileSystem.createFile( pszSourceFile, FLM_IO_RDWR, + &pFileHdl))) + { + goto Exit; + } + } + else + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = fileSystem.openFile( pszSourceFile, + FLM_IO_RDWR, &pFileHdl))) + { + goto Exit; + } + } + + if ( RC_BAD( rc = pFileHdl->size( &ui64FileSize))) + { + goto Exit; + } + + if( RC_BAD( rc = pFileHdl->write( ui64FileSize, f_strlen( pszData), + (void *)pszData, &uiBytesWritten))) + { + goto Exit; + } + +Exit: + + if( pFileHdl) + { + pFileHdl->close(); + pFileHdl->Release(); + pFileHdl = NULL; + } + + return( rc); +} diff --git a/ftk/src/ftkini.cpp b/ftk/src/ftkini.cpp new file mode 100644 index 0000000..87c1d3a --- /dev/null +++ b/ftk/src/ftkini.cpp @@ -0,0 +1,1074 @@ +//------------------------------------------------------------------------------ +// Desc: Class to support reading/writing/parsing .ini files +// +// Tabs: 3 +// +// Copyright (c) 2002-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: inifile.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +/**************************************************************************** +Desc: +****************************************************************************/ +F_IniFile::F_IniFile() +{ + m_pFirstLine = NULL; + m_pLastLine = NULL; + m_bReady = FALSE; + m_bModified = FALSE; + m_pszFileName = NULL; + m_pFileHdl = NULL; + m_pPool = NULL; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_IniFile::~F_IniFile() +{ + if( m_pszFileName) + { + f_free( &m_pszFileName); + } + + if( m_pPool) + { + m_pPool->Release(); + } + + if( m_pFileHdl) + { + m_pFileHdl->Release(); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FLMAPI F_IniFile::init() +{ + RCODE rc = NE_FLM_OK; + + if( m_pPool) + { + m_pPool->Release(); + } + + if (( m_pPool = f_new F_Pool) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + m_pPool->poolInit( 512); + m_pFirstLine = NULL; + m_pLastLine = NULL; + m_bReady = TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Read the ini file and parse its contents +****************************************************************************/ +RCODE FLMAPI F_IniFile::read( + const char * pszFileName) +{ + RCODE rc = NE_FLM_OK; + FLMBOOL bMore = FALSE; + FLMBOOL bEOF = FALSE; +#define INITIAL_READ_BUF_SIZE 100 + FLMUINT uiReadBufSize = 0; + FLMUINT uiBytesAvail = 0; + FLMUINT uiBytesInLine = 0; + char * pszReadBuf = NULL; + FLMUINT uiLineNum = 0; + + flmAssert( m_bReady); + flmAssert( !m_pFileHdl); + + // Open the file + + if (RC_BAD( rc = f_alloc( f_strlen( pszFileName) + 1, &m_pszFileName))) + { + goto Exit; + } + + f_strcpy( m_pszFileName, pszFileName); + + // It's not an error if the file doesn't exist. If it does exist, + // we'll read in its data. + + if( RC_BAD( gv_pFileSystem->openFile( pszFileName, + FLM_IO_RDONLY, &m_pFileHdl))) + { + goto Exit; + } + + m_uiFileOffset = 0; + + // Read in and parse the file + + uiReadBufSize = INITIAL_READ_BUF_SIZE; + if (RC_BAD( rc = f_alloc( uiReadBufSize, &pszReadBuf))) + { + goto Exit; + } + + // Read in and parse each line in the file... + while (!bEOF) + { + uiLineNum++; + + uiBytesAvail = uiReadBufSize; + if( RC_BAD( rc = readLine( pszReadBuf, &uiBytesAvail, &bMore)) && + rc != NE_FLM_IO_END_OF_FILE) + { + goto Exit; + } + + if (rc == NE_FLM_IO_END_OF_FILE) + { + bEOF = TRUE; + } + + // While there are more bytes in the line, re-alloc the buffer, and do + // another read. + + uiBytesInLine = uiBytesAvail; + while( bMore) + { + uiBytesAvail = uiReadBufSize; + uiReadBufSize *= 2; + + if (RC_BAD( rc = f_realloc( uiReadBufSize, &pszReadBuf))) + { + goto Exit; + } + + if (RC_BAD( rc = readLine( pszReadBuf+uiBytesAvail, + &uiBytesAvail, &bMore)) && + (rc != NE_FLM_IO_END_OF_FILE) ) + { + goto Exit; + } + + if( rc == NE_FLM_IO_END_OF_FILE) + { + bEOF = TRUE; + } + uiBytesInLine += uiBytesAvail; + } + + if ( (RC_OK( rc) || (rc == NE_FLM_IO_END_OF_FILE)) && + (uiBytesInLine > 0) ) + { + // NumBytes will be 0 if the line was blank. No need + // to call parseBuffer in this case + if (RC_BAD( rc = parseBuffer( pszReadBuf, uiBytesInLine))) + { + if (rc == NE_FLM_SYNTAX) + { + rc = NE_FLM_OK; + } + else + { + goto Exit; + } + } + } + } // end of while (!bEOF) + +Exit: + + // Close the file + + if( m_pFileHdl) + { + m_pFileHdl->close(); + m_pFileHdl->Release(); + m_pFileHdl = NULL; + } + + // Free the buffer + + if (pszReadBuf) + { + f_free( &pszReadBuf); + } + + if (rc == NE_FLM_IO_END_OF_FILE) + { + rc = NE_FLM_OK; + } + + return rc; +} + +/**************************************************************************** +Desc: Copies the data stored in the INI_LINE structs to the ini file +****************************************************************************/ +RCODE FLMAPI F_IniFile::write( void) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiBytesWritten; + INI_LINE * pCurLine = NULL; + FLMBOOL uiFileOffset = 0; + + flmAssert( m_bReady); + + if (!m_bModified) + { + // Nothing needs to be written + goto Exit; + } + + // Open the file + + flmAssert( !m_pFileHdl); + + if (RC_BAD( rc = gv_pFileSystem->createFile( m_pszFileName, + FLM_IO_RDWR, &m_pFileHdl))) + { + goto Exit; + } + + pCurLine = m_pFirstLine; + while (pCurLine) + { + if (pCurLine->pszParamName) + { + // Output the param name + + if (RC_BAD (rc = m_pFileHdl->write( uiFileOffset, + f_strlen( pCurLine->pszParamName), pCurLine->pszParamName, + &uiBytesWritten))) + { + goto Exit; + } + uiFileOffset += uiBytesWritten; + + if (pCurLine->pszParamValue) + { + // Output the "=" and the value + if (RC_BAD (rc = m_pFileHdl->write( uiFileOffset, 1, + (void *)"=", &uiBytesWritten))) + { + goto Exit; + } + + uiFileOffset += uiBytesWritten; + + if (RC_BAD (rc = m_pFileHdl->write( uiFileOffset, + f_strlen( pCurLine->pszParamValue), pCurLine->pszParamValue, + &uiBytesWritten))) + { + goto Exit; + } + uiFileOffset += uiBytesWritten; + } + } + + + if (pCurLine->pszComment) + { + // Output the comment + if (pCurLine->pszParamName) + { + if (RC_BAD (rc = m_pFileHdl->write( uiFileOffset, 2, + (void *)" #", &uiBytesWritten))) + { + goto Exit; + } + } + else + { + if (RC_BAD (rc = m_pFileHdl->write( uiFileOffset, 1, + (void *)"#", &uiBytesWritten))) + { + goto Exit; + } + } + + uiFileOffset += uiBytesWritten; + + if (RC_BAD (rc = m_pFileHdl->write( uiFileOffset, + f_strlen( pCurLine->pszComment), pCurLine->pszComment, + &uiBytesWritten))) + { + goto Exit; + } + + uiFileOffset += uiBytesWritten; + + } + + // Write out a newline... + + if (RC_BAD (rc = m_pFileHdl->write( uiFileOffset, f_strlen( "\n"), + (void *)"\n", &uiBytesWritten))) + { + goto Exit; + } + + uiFileOffset += uiBytesWritten; + pCurLine = pCurLine->pNext; + } + + m_bModified = FALSE; + +Exit: + + if (m_pFileHdl) + { + m_pFileHdl->close(); + m_pFileHdl->Release(); + m_pFileHdl = NULL; + } + + return( rc); +} + +/**************************************************************************** +Desc: Retrieves the value associated with the specified name from the list + of INI_STRUCTs +****************************************************************************/ +FLMBOOL FLMAPI F_IniFile::getParam( + const char * pszParamName, + FLMUINT * puiParamVal) +{ + FLMBOOL bFound = FALSE; + INI_LINE * pLine = NULL; + + flmAssert( m_bReady); + + pLine = findParam( pszParamName); + if( !pLine) + { + goto Exit; + } + + if( !pLine->pszParamValue) + { + goto Exit; + } + + fromAscii( puiParamVal, pLine->pszParamValue); + bFound = TRUE; + +Exit: + + return( bFound); +} + +/**************************************************************************** +Desc: Stores a new value for the specified name (or creates a new name/value + pair) in the list of INI_STRUCTs +****************************************************************************/ +RCODE FLMAPI F_IniFile::setParam( + const char * pszParamName, + FLMUINT uiParamVal) +{ + RCODE rc = NE_FLM_OK; + INI_LINE * pLine; + + flmAssert( m_bReady); + + // If the parameter exists in the list, just store the new value. + // Othewise, create a new INI_LINE and add it to the list + + pLine = findParam( pszParamName); + if( !pLine) + { + if (RC_BAD( rc = setParamCommon( &pLine, pszParamName))) + { + goto Exit; + } + } + + if( RC_BAD( rc = toAscii( &pLine->pszParamValue, uiParamVal))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Retrieves the value associated with the specified name from the list + of INI_STRUCTs +****************************************************************************/ +FLMBOOL FLMAPI F_IniFile::getParam( + const char * pszParamName, + FLMBOOL * pbParamVal) // Out: The value associated with name +{ + FLMBOOL bFound = FALSE; + INI_LINE * pLine = NULL; + + flmAssert( m_bReady); + + pLine = findParam( pszParamName); + + if( !pLine) + { + goto Exit; + } + + if( !pLine->pszParamValue) + { + goto Exit; + } + + fromAscii( pbParamVal, pLine->pszParamValue); + bFound = TRUE; + +Exit: + + return( bFound); +} + +/**************************************************************************** +Desc: Stores a new value for the specified name (or creates a new name/value + pair) in the list of INI_STRUCTs +****************************************************************************/ +RCODE FLMAPI F_IniFile::setParam( + const char * pszParamName, + FLMBOOL bParamVal) +{ + RCODE rc = NE_FLM_OK; + INI_LINE * pLine; + + flmAssert( m_bReady); + + // If the parameter exists in the list, just store the new value. + // Othewise, create a new INI_LINE and add it to the list + + pLine = findParam( pszParamName); + if( !pLine) + { + if (RC_BAD( rc = setParamCommon( &pLine, pszParamName))) + { + goto Exit; + } + } + + if( RC_BAD( rc = toAscii( &pLine->pszParamValue, bParamVal))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Retrieves the value associated with the specified name from the list + of INI_STRUCTs +****************************************************************************/ +FLMBOOL FLMAPI F_IniFile::getParam( + const char * pszParamName, + char ** ppszParamVal) +{ + FLMBOOL bFound = FALSE; + INI_LINE * pLine = NULL; + + flmAssert( m_bReady); + *ppszParamVal = NULL; + + pLine = findParam( pszParamName); + + if( !pLine) + { + goto Exit; + } + + if( pLine->pszParamValue == NULL) + { + goto Exit; + } + + *ppszParamVal = pLine->pszParamValue; + bFound = TRUE; + +Exit: + + return( bFound); +} + +/**************************************************************************** +Desc: Stores a new value for the specified name (or creates a new name/value + pair) in the list of INI_STRUCTs +****************************************************************************/ +RCODE FLMAPI F_IniFile::setParam( + const char * pszParamName, + const char * pszParamVal) +{ + RCODE rc = NE_FLM_OK; + INI_LINE * pLine; + + flmAssert( m_bReady); + + // If the parameter exists in the list, just store the new value. + // Othewise, create a new INI_LINE and add it to the list + + pLine = findParam( pszParamName); + if( !pLine) + { + if( RC_BAD( rc = setParamCommon( &pLine, pszParamName))) + { + goto Exit; + } + } + + if( RC_BAD( rc = toAscii( &pLine->pszParamValue, pszParamVal))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Read a line from the ini file and store it in pszBuf +****************************************************************************/ +RCODE F_IniFile::readLine( + char * pszBuf, + FLMUINT * puiBytes, + FLMBOOL * pbMore) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiBytesRead = 0; + FLMUINT uiBytesInLine = 0; + FLMUINT uiEOLBytes = 0; + FLMBOOL bEOL = FALSE; + + flmAssert( m_pFileHdl); + + rc = m_pFileHdl->read( m_uiFileOffset, *puiBytes, + (FLMBYTE *)pszBuf, &uiBytesRead); + + if ( RC_OK( rc) || rc == NE_FLM_IO_END_OF_FILE) + { + // Check to see if we got more than one line... + + while( !bEOL && (uiBytesInLine < uiBytesRead) ) + { + if( pszBuf[ uiBytesInLine] == 13 || pszBuf[ uiBytesInLine] == 10) + { + // NOTE: If we end up reading the first byte of a CR/LF pair, but + // but the second byte is read on the next call, then it will get + // counted as a new (but empty) line. We're not going to worry + // about it though, because empty lines end up getting ignored + // and this isn't likely to happen often enough to effect + // performance. + + bEOL = TRUE; + *puiBytes = uiBytesInLine; + uiEOLBytes=1; + + // Check for a CR/LF pair (or a LF/CR pair...) + + if( (uiBytesInLine + 1 < uiBytesRead) && + (pszBuf[ uiBytesInLine + 1] == 13 || + pszBuf[ uiBytesInLine + 1] == 10)) + { + uiEOLBytes++; + } + } + else + { + uiBytesInLine++; + } + } + + // Set the file position variable forward appropriately... + + m_uiFileOffset += uiBytesInLine + uiEOLBytes; + } + + // If we read in more than one line, then don't want to return + // NE_FLM_IO_END_OF_FILE... + + if( rc == NE_FLM_IO_END_OF_FILE && + (uiBytesInLine + uiEOLBytes) < uiBytesRead) + { + rc = NE_FLM_OK; + } + + // Last step - update pbMore + + *pbMore = (bEOL || (uiBytesRead == 0)) + ? FALSE + : TRUE; + + return( rc); +} + +/**************************************************************************** +Desc: Parse a single line from the ini file into its name, value and comment + parts. +****************************************************************************/ +RCODE F_IniFile::parseBuffer( + char * pszBuf, + FLMUINT uiNumBytes) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiCurrentChar = 0; + char * pszNameStart = NULL; + char * pszNameEnd = NULL; + char * pszValStart = NULL; + char * pszValEnd = NULL; + char * pszCommentStart = NULL; + INI_LINE * pLine = NULL; + FLMUINT uiStrLen = 0; + + flmAssert( pszBuf); + flmAssert( uiNumBytes); + + // Start looking for the parameter name... + + while (uiCurrentChar < uiNumBytes) + { + if( !isWhiteSpace( pszBuf[uiCurrentChar])) + { + if (pszBuf[uiCurrentChar] == '#') + { + goto Comment; + } + else + { + pszNameStart = &pszBuf[uiCurrentChar]; + break; + } + } + uiCurrentChar++; + } + + // We've found a param name, now mark the end of it + // We determine the end by looking for whitespace or '=' + // or '#' + + while (uiCurrentChar < uiNumBytes) + { + if( isWhiteSpace( pszBuf[uiCurrentChar]) || + (pszBuf[uiCurrentChar] == '=') || + (pszBuf[uiCurrentChar] == '#')) + { + pszNameEnd = &pszBuf[uiCurrentChar-1]; + break; + } + + uiCurrentChar++; + } + + if( (uiCurrentChar == uiNumBytes) && + (pszNameEnd == NULL) ) + { + pszNameEnd = &pszBuf[uiCurrentChar - 1]; + } + + // Now, there may be a value part or a comment part next. If there's a + // value, it had better be preceeded by an '=' + + while( (uiCurrentChar < uiNumBytes) && + isWhiteSpace( pszBuf[uiCurrentChar]) ) + { + uiCurrentChar++; + } + + if( uiCurrentChar < uiNumBytes && pszBuf[ uiCurrentChar] == '#') + { + goto Comment; + } + + if( uiCurrentChar < uiNumBytes && pszBuf[uiCurrentChar] != '=' ) + { + rc = RC_SET( NE_FLM_SYNTAX); + goto Exit; + } + + // Ok - at this point pszBuf[uiCurrentChar] contains an =. Skip over + // the = and any whitespace that follows. + + while( uiCurrentChar < uiNumBytes) + { + uiCurrentChar++; + if( !isWhiteSpace( pszBuf[uiCurrentChar])) + { + pszValStart = &pszBuf[uiCurrentChar]; + break; + } + } + + // Now mark the end of the value. + // We determine the end by looking for whitespace or '#' + + while( uiCurrentChar < uiNumBytes) + { + if( isWhiteSpace( pszBuf[uiCurrentChar]) || + (pszBuf[uiCurrentChar] == '#')) + { + pszValEnd = &pszBuf[uiCurrentChar-1]; + break; + } + uiCurrentChar++; + } + + if( uiCurrentChar == uiNumBytes && !pszValEnd) + { + pszValEnd = &pszBuf[uiCurrentChar-1]; + } + +Comment: + + // Check out the rest of the line to see if there's a comment + + while( uiCurrentChar < uiNumBytes) + { + if( !isWhiteSpace( pszBuf[ uiCurrentChar]) && + pszBuf[ uiCurrentChar] != '#') + { + rc = RC_SET( NE_FLM_SYNTAX); + goto Exit; + } + else if( pszBuf[ uiCurrentChar] == '#') + { + // Comment found. Set pszCommentStart to the next char + + pszCommentStart = &pszBuf[uiCurrentChar+1]; + break; + } + uiCurrentChar++; + } + + // Done parsing. Now, assuming the line had any info in it, + // store all the strings... + + if( pszNameStart || pszCommentStart) + { + if( RC_BAD( rc = m_pPool->poolCalloc( sizeof( INI_LINE), + (void **)&pLine))) + { + goto Exit; + } + + if( pszNameStart) + { + uiStrLen = pszNameEnd - pszNameStart + 1; + if( RC_BAD( rc = m_pPool->poolAlloc( uiStrLen + 1, + (void **)&pLine->pszParamName))) + { + goto Exit; + } + + f_memcpy( pLine->pszParamName, pszNameStart, uiStrLen); + pLine->pszParamName[uiStrLen] = '\0'; + } + + if( pszValStart) + { + uiStrLen = pszValEnd - pszValStart + 1; + if( RC_BAD( rc = m_pPool->poolAlloc( uiStrLen + 1, + (void **)&pLine->pszParamValue))) + { + goto Exit; + } + + f_memcpy(pLine->pszParamValue, pszValStart, uiStrLen); + pLine->pszParamValue[uiStrLen] = '\0'; + } + + if (pszCommentStart) + { + uiStrLen = uiNumBytes-(pszCommentStart-pszBuf); + if (RC_BAD( rc = m_pPool->poolAlloc( uiStrLen + 1, + (void **)&pLine->pszComment))) + { + goto Exit; + } + + f_memcpy(pLine->pszComment, pszCommentStart, uiStrLen); + pLine->pszComment[uiStrLen] = '\0'; + } + + // Insert this struct into the linked list + + if( m_pLastLine) + { + m_pLastLine->pNext = pLine; + } + + pLine->pPrev = m_pLastLine; + pLine->pNext = NULL; + m_pLastLine = pLine; + + if( !m_pFirstLine) + { + m_pFirstLine = pLine; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Search through the list of INI_STRUCTs for a particular name +****************************************************************************/ +INI_LINE * F_IniFile::findParam( + const char * pszParamName) +{ + INI_LINE * pCurLine = m_pFirstLine; + + while( pCurLine) + { + if (pCurLine->pszParamName) + { + if (f_strcmp( pszParamName, pCurLine->pszParamName) == 0) + { + return pCurLine; + } + } + + pCurLine = pCurLine->pNext; + } + + return( NULL); +} + +/**************************************************************************** +Desc: This code is common to all of the SetParam() functions +****************************************************************************/ +RCODE F_IniFile::setParamCommon( + INI_LINE ** ppLine, + const char * pszParamName) +{ + RCODE rc = NE_FLM_OK; + INI_LINE * pLine; + + if( RC_BAD( rc = m_pPool->poolCalloc( + sizeof( INI_LINE), (void **)&pLine))) + { + goto Exit; + } + + if( m_pLastLine) + { + m_pLastLine->pNext = pLine; + } + + pLine->pPrev = m_pLastLine; + m_pLastLine = pLine; + + if( !m_pFirstLine) + { + m_pFirstLine = pLine; + } + + if( RC_BAD( rc = m_pPool->poolAlloc( f_strlen(pszParamName)+1, + (void **)&pLine->pszParamName))) + { + goto Exit; + } + + f_strcpy( pLine->pszParamName, pszParamName); + +Exit: + + if( RC_OK( rc)) + { + *ppLine = pLine; + } + + return( rc); +} + +/**************************************************************************** +Desc: All of the fromAscii() functions convert values stored in strings to + various native formats +****************************************************************************/ +void F_IniFile::fromAscii( + FLMUINT * puiVal, + const char * pszBuf) +{ + FLMUINT uiValue; + FLMBOOL bAllowHex = FALSE; + + if( *pszBuf == '0' && + (*(pszBuf + 1) == 'x' || *(pszBuf + 1) == 'X')) + { + pszBuf += 2; + bAllowHex = TRUE; + } + + uiValue = 0; + while( *pszBuf) + { + if( *pszBuf >= '0' && *pszBuf <= '9') + { + if( !bAllowHex) + { + uiValue *= 10; + } + else + { + uiValue <<= 4; + } + + uiValue += (FLMUINT)(*pszBuf - '0'); + } + else if( bAllowHex) + { + if( *pszBuf >= 'A' && *pszBuf <= 'F') + { + uiValue <<= 4; + uiValue += (FLMUINT)(*pszBuf - 'A') + 10; + } + else if( *pszBuf >= 'a' && *pszBuf <= 'f') + { + uiValue <<= 4; + uiValue += (FLMUINT)(*pszBuf - 'a') + 10; + } + else + { + break; + } + } + else + { + break; + } + pszBuf++; + } + + *puiVal = uiValue; +} + +/**************************************************************************** +Desc: All of the fromAscii() functions convert values stored in strings to + various native formats +****************************************************************************/ +void F_IniFile::fromAscii( + FLMBOOL * pbVal, + const char * pszParamValue) +{ + if( f_stricmp( pszParamValue, "true") == 0 || + f_stricmp( pszParamValue, "enabled") == 0 || + f_stricmp( pszParamValue, "on") == 0 || + f_stricmp( pszParamValue, "1") == 0) + { + *pbVal = TRUE; + } + else + { + *pbVal = FALSE; + } +} + +/**************************************************************************** +Desc: All of the toAscii() functions convert values from their native + formats to a string representation +****************************************************************************/ +RCODE F_IniFile::toAscii( + char ** ppszParamValue, + FLMUINT puiVal) +{ + RCODE rc = NE_FLM_OK; + char szTemp[ 50]; + + f_sprintf( szTemp, "%*.*lu", sizeof(szTemp), sizeof(szTemp), puiVal); + + if( RC_BAD( rc = m_pPool->poolAlloc( f_strlen( szTemp), + (void **)ppszParamValue))) + { + goto Exit; + } + + f_strcpy( *ppszParamValue, szTemp); + m_bModified = TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: All of the toAscii() functions convert values from their native + formats to a string representation +****************************************************************************/ +RCODE F_IniFile::toAscii( + char ** ppszParamValue, + FLMBOOL bVal) +{ + RCODE rc = NE_FLM_OK; + + if( RC_BAD( rc = m_pPool->poolAlloc( 6, (void **)ppszParamValue))) + { + goto Exit; + } + + if( bVal) + { + f_memcpy( *ppszParamValue, "TRUE ", 6); + } + else + { + f_memcpy( *ppszParamValue, "FALSE", 6); + } + + m_bModified = TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: All of the toAscii() functions convert values from their native + formats to a string representation +****************************************************************************/ +RCODE F_IniFile::toAscii( + char ** ppszParamValue, + const char * pszVal) +{ + RCODE rc = NE_FLM_OK; + + if( RC_BAD( rc = m_pPool->poolAlloc( f_strlen( pszVal), + (void **)ppszParamValue))) + { + goto Exit; + } + + f_strcpy( *ppszParamValue, pszVal); + m_bModified = TRUE; + +Exit: + + return( rc); +} diff --git a/ftk/src/ftkiobuf.cpp b/ftk/src/ftkiobuf.cpp new file mode 100644 index 0000000..693786b --- /dev/null +++ b/ftk/src/ftkiobuf.cpp @@ -0,0 +1,542 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains the F_IOBuffer and F_IOBufferMgr classes. +// +// Tabs: 3 +// +// Copyright (c) 2001-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: fbuff.cpp 3111 2006-01-19 13:10:50 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +/**************************************************************************** +Desc: +****************************************************************************/ +F_IOBufferMgr::F_IOBufferMgr() +{ + m_pFirstPending = NULL; + m_pFirstAvail = NULL; + m_pFirstUsed = NULL; + m_uiMaxBuffers = 0; + m_uiMaxBufferBytesToUse = 0; + m_uiBufferBytesInUse = 0; + m_uiBuffersInUse = 0; + m_completionRc = NE_FLM_OK; + m_bKeepBuffers = FALSE; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_IOBufferMgr::~F_IOBufferMgr() +{ + flmAssert( !m_pFirstPending && !m_pFirstUsed); + while (m_pFirstPending) + { + m_pFirstPending->Release(); + } + + while (m_pFirstAvail) + { + m_pFirstAvail->Release(); + } + + while (m_pFirstUsed) + { + m_pFirstUsed->Release(); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FLMAPI F_IOBufferMgr::waitForAllPendingIO( void) +{ + RCODE rc = NE_FLM_OK; + F_IOBuffer * pBuf; + + while( (pBuf = m_pFirstPending) != NULL) + { + (void)pBuf->waitToComplete(); + } + + rc = m_completionRc; + m_completionRc = NE_FLM_OK; + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_IOBufferMgr::linkToList( + F_IOBuffer ** ppListHead, + F_IOBuffer * pIOBuffer) +{ + flmAssert( pIOBuffer->m_eList == F_IOBuffer::MGR_LIST_NONE); + pIOBuffer->m_pPrev = NULL; + if ((pIOBuffer->m_pNext = *ppListHead) != NULL) + { + (*ppListHead)->m_pPrev = pIOBuffer; + } + *ppListHead = pIOBuffer; + if (ppListHead == &m_pFirstPending || + ppListHead == &m_pFirstUsed) + { + pIOBuffer->m_eList = (ppListHead == &m_pFirstPending + ? F_IOBuffer::MGR_LIST_PENDING + : F_IOBuffer::MGR_LIST_USED); + pIOBuffer->m_bDeleteOnNotify = (m_bKeepBuffers + ? FALSE + : TRUE); + m_uiBuffersInUse++; + m_uiBufferBytesInUse += pIOBuffer->m_uiBufferSize; + } + else + { + pIOBuffer->m_eList = F_IOBuffer::MGR_LIST_AVAIL; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_IOBufferMgr::unlinkFromList( + F_IOBuffer * pIOBuffer) +{ + if (pIOBuffer->m_pNext) + { + pIOBuffer->m_pNext->m_pPrev = pIOBuffer->m_pPrev; + } + if (pIOBuffer->m_pPrev) + { + pIOBuffer->m_pPrev->m_pNext = pIOBuffer->m_pNext; + } + else if (pIOBuffer->m_eList == F_IOBuffer::MGR_LIST_AVAIL) + { + m_pFirstAvail = pIOBuffer->m_pNext; + } + else if (pIOBuffer->m_eList == F_IOBuffer::MGR_LIST_PENDING) + { + m_pFirstPending = pIOBuffer->m_pNext; + } + else if (pIOBuffer->m_eList == F_IOBuffer::MGR_LIST_USED) + { + m_pFirstUsed = pIOBuffer->m_pNext; + } + else + { + flmAssert( 0); + } + if (pIOBuffer->m_eList == F_IOBuffer::MGR_LIST_PENDING || + pIOBuffer->m_eList == F_IOBuffer::MGR_LIST_USED) + { + m_uiBuffersInUse--; + flmAssert( m_uiBufferBytesInUse >= pIOBuffer->m_uiBufferSize); + m_uiBufferBytesInUse -= pIOBuffer->m_uiBufferSize; + } + pIOBuffer->m_eList = F_IOBuffer::MGR_LIST_NONE; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FLMAPI F_IOBufferMgr::getBuffer( + IF_IOBuffer ** ppIOBuffer, + FLMUINT uiBufferSize, + FLMUINT uiBlockSize) +{ + RCODE rc = NE_FLM_OK; + F_IOBuffer * pIOBuffer = NULL; + F_IOBuffer * pBuf; + + if( RC_BAD( m_completionRc)) + { + rc = m_completionRc; + goto Exit; + } + + if ((m_uiBufferBytesInUse + uiBufferSize > m_uiMaxBufferBytesToUse && + m_pFirstPending) || + m_uiBuffersInUse == m_uiMaxBuffers) + { + pBuf = m_pFirstPending; + for (;;) + { + if( pBuf->isIOComplete()) + { + if( RC_BAD( rc = pBuf->waitToComplete())) + { + goto Exit; + } + pBuf = m_pFirstPending; + if (m_uiBufferBytesInUse + uiBufferSize > m_uiMaxBufferBytesToUse && + m_pFirstPending) + { + continue; + } + else + { + flmAssert( m_uiBuffersInUse < m_uiMaxBuffers); + break; + } + } + + if ((pBuf = pBuf->m_pNext) == NULL) + { + f_yieldCPU(); + pBuf = m_pFirstPending; + } + } + } + + // If we are set up to keep buffers, caller better always ask + // for the same size. + + if (m_pFirstAvail) + { + pIOBuffer = m_pFirstAvail; + unlinkFromList( pIOBuffer); + flmAssert( pIOBuffer->getBufferSize() == uiBufferSize); + } + else + { + if ((pIOBuffer = f_new F_IOBuffer) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + pIOBuffer->m_pIOBufferMgr = this; + if (RC_BAD( rc = pIOBuffer->setupBuffer( uiBufferSize, + uiBlockSize))) + { + goto Exit; + } + } + + // An F_IOBuffer object, once created must ALWAYS be linked + // into the buffer manager's used list. + + linkToList( &m_pFirstUsed, pIOBuffer); +#ifdef FLM_NLM + flmAssert( kSemaphoreExamineCount( (SEMAPHORE)(pIOBuffer->m_hSem)) == 0); +#endif + +Exit: + + if (RC_BAD( rc) && pIOBuffer) + { + pIOBuffer->Release(); + pIOBuffer = NULL; + } + *ppIOBuffer = pIOBuffer; + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_IOBuffer::F_IOBuffer() +{ + m_pIOBufferMgr = NULL; + m_pucBuffer = NULL; +#ifdef FLM_DEBUG + f_memset( m_UserData, 0, sizeof( m_UserData)); +#endif + m_uiBufferSize = 0; + m_uiBlockSize = 0; + m_eList = MGR_LIST_NONE; + m_bDeleteOnNotify = TRUE; + m_pNext = NULL; + m_pPrev = NULL; + m_fnCompletion = NULL; + m_completionRc = NE_FLM_OK; +#if defined( FLM_WIN) + m_FileHandle = INVALID_HANDLE_VALUE; + m_Overlapped.hEvent = 0; +#elif defined( FLM_LINUX) || defined( FLM_SOLARIS) + m_aio.aio_fildes = -1; +#elif defined( FLM_NLM) + m_hSem = F_SEM_NULL; +#endif +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_IOBuffer::~F_IOBuffer() +{ + // Unlink from list object is in, if any. + + if (m_eList != MGR_LIST_NONE) + { + flmAssert( m_pIOBufferMgr); + m_pIOBufferMgr->unlinkFromList( this); + } + +#if defined( FLM_WIN) + if( m_Overlapped.hEvent) + { + CloseHandle( m_Overlapped.hEvent); + } +#endif + +#ifdef FLM_NLM + if (m_hSem != F_SEM_NULL) + { + f_semDestroy( &m_hSem); + } +#endif + + if (m_pucBuffer) + { +#ifdef FLM_WIN + (void)VirtualFree( m_pucBuffer, 0, MEM_RELEASE); + m_pucBuffer = NULL; +#elif defined( FLM_LINUX) || defined( FLM_SOLARIS) + free( m_pucBuffer); + m_pucBuffer = NULL; +#else + f_free( &m_pucBuffer); +#endif + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void FLMAPI F_IOBuffer::makePending( void) +{ + flmAssert( m_eList == MGR_LIST_USED); + + // Unlink from used list + + m_pIOBufferMgr->unlinkFromList( this); + + // Link into pending list. + + m_pIOBufferMgr->linkToList( &m_pIOBufferMgr->m_pFirstPending, this); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FLMAPI F_IOBuffer::setupBuffer( + FLMUINT uiBufferSize, + FLMUINT uiBlockSize) +{ + RCODE rc = NE_FLM_OK; + +#if defined( FLM_WIN) + if( (m_Overlapped.hEvent = CreateEvent( NULL, TRUE, + FALSE, NULL)) == NULL) + { + rc = MapPlatformError( GetLastError(), NE_FLM_SETTING_UP_FOR_WRITE); + goto Exit; + } +#endif + +#ifdef FLM_NLM + if (RC_BAD( rc = f_semCreate( &m_hSem))) + { + goto Exit; + } +#endif + + // Allocate a buffer + +#ifdef FLM_WIN + if ((m_pucBuffer = (FLMBYTE *)VirtualAlloc( NULL, + (DWORD)uiBufferSize, + MEM_COMMIT, PAGE_READWRITE)) == NULL) + { + rc = MapPlatformError( GetLastError(), NE_FLM_MEM); + goto Exit; + } +#elif defined( FLM_LINUX) + if( posix_memalign( (void **)&m_pucBuffer, + sysconf( _SC_PAGESIZE), uiBufferSize) != 0) + { + rc = MapPlatformError( errno, NE_FLM_MEM); + goto Exit; + } +#elif defined( FLM_SOLARIS) + if( (m_pucBuffer = (FLMBYTE *)memalign( sysconf( _SC_PAGESIZE), + uiBufferSize)) == NULL) + { + rc = MapPlatformError( errno, NE_FLM_MEM); + goto Exit; + } +#else + if (RC_BAD( rc = f_alloc( uiBufferSize, &m_pucBuffer))) + { + goto Exit; + } +#endif + + m_uiBufferSize = uiBufferSize; + m_uiBlockSize = uiBlockSize; + +Exit: + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void FLMAPI F_IOBuffer::notifyComplete( + RCODE rc) +{ + flmAssert( m_eList == MGR_LIST_PENDING || + m_eList == MGR_LIST_USED); + + m_completionRc = rc; + if( m_fnCompletion) + { + m_fnCompletion( this); + + // Fix so completion callback won't be called twice. + + m_fnCompletion = NULL; + } + + if (RC_BAD( rc) && RC_OK( m_pIOBufferMgr->m_completionRc)) + { + m_pIOBufferMgr->m_completionRc = rc; + } + + if (m_bDeleteOnNotify) + { + Release(); + } + else + { + m_pIOBufferMgr->unlinkFromList( this); + m_pIOBufferMgr->linkToList( &m_pIOBufferMgr->m_pFirstAvail, this); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL F_IOBuffer::isIOComplete( void) +{ + FLMBOOL bComplete = FALSE; +#ifdef FLM_NLM + FLMUINT uiSemCount; +#endif + + if( m_eList != MGR_LIST_PENDING) + { + bComplete = TRUE; + goto Exit; + } + +#ifdef FLM_WIN + if (m_FileHandle == INVALID_HANDLE_VALUE || + HasOverlappedIoCompleted( &m_Overlapped)) + { + bComplete = TRUE; + } +#endif + +#if defined( FLM_LINUX) || defined( FLM_SOLARIS) + if( m_aio.aio_fildes == -1 || aio_error( &m_aio) != EINPROGRESS) + { + bComplete = TRUE; + } +#endif + +#ifdef FLM_NLM + if( (uiSemCount = (FLMUINT)kSemaphoreExamineCount( (SEMAPHORE)m_hSem)) != 0) + { + flmAssert( uiSemCount == 1); + bComplete = TRUE; + } +#endif + +Exit: + return( bComplete); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_IOBuffer::waitToComplete( void) +{ + RCODE rc = NE_FLM_OK; + + // IMPORTANT NOTE! The call to notifyComplete will destroy this + // object, so nothing in the object can be accessed after notifyComplete + // is called. + +#ifdef FLM_WIN + if (m_FileHandle != INVALID_HANDLE_VALUE) + { + DWORD udBytesWritten; + + if (!GetOverlappedResult( m_FileHandle, &m_Overlapped, + &udBytesWritten, TRUE)) + { + rc = MapPlatformError( GetLastError(), NE_FLM_WRITING_FILE); + } + + notifyComplete( rc); + } +#endif + +#if defined( FLM_LINUX) || defined( FLM_SOLARIS) + if( m_aio.aio_fildes != -1) + { + const struct aiocb * pAio = &m_aio; + + if( aio_suspend( &pAio, 1, NULL) == -1) + { + rc = MapPlatformError( errno, NE_FLM_MEM); + } + + notifyComplete( rc); + } +#endif + +#ifdef FLM_NLM + if( kSemaphoreWait( (SEMAPHORE)m_hSem) != 0) + { + flmAssert( 0); + } + flmAssert( kSemaphoreExamineCount( (SEMAPHORE)m_hSem) == 0); + rc = m_completionRc; + notifyComplete( m_completionRc); +#endif + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_NLM +void FLMAPI F_IOBuffer::signalComplete( + RCODE rc) +{ + m_completionRc = rc; + flmAssert( kSemaphoreExamineCount( (SEMAPHORE)m_hSem) == 0); + kSemaphoreSignal( (SEMAPHORE)m_hSem); +} +#endif diff --git a/ftk/src/ftklog.cpp b/ftk/src/ftklog.cpp new file mode 100644 index 0000000..bca01d8 --- /dev/null +++ b/ftk/src/ftklog.cpp @@ -0,0 +1,507 @@ +//------------------------------------------------------------------------------ +// Desc: Contains routines for logging messages from within FLAIM. +// +// Tabs: 3 +// +// Copyright (c) 2001-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: flog.cpp 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +static F_MUTEX gv_hLoggerMutex = F_MUTEX_NULL; +static FLMUINT gv_uiPendingLogMessages = 0; +static IF_LoggerClient * gv_pLogger = NULL; + +FSTATIC void f_logProcessFormatString( + FLMUINT uiLen, + IF_LogMessageClient * pLogMessage, ...); + +FSTATIC void f_logParsePrintfArgs( + FLMBYTE * pszFormat, + f_va_list * args, + IF_LogMessageClient * pLogMessage); + +FSTATIC void f_logStringFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + IF_LogMessageClient * pLogMessage, + f_va_list * args); + +FSTATIC void f_logNumberFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + IF_LogMessageClient * pLogMessage, + f_va_list * args); + +FSTATIC void f_logErrorFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + IF_LogMessageClient * pLogMessage, + f_va_list * args); + +FSTATIC void f_logColorFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + IF_LogMessageClient * pLogMessage, + f_va_list * args); + +FSTATIC void f_logCharFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + IF_LogMessageClient * pLogMessage, + f_va_list * args); + +FSTATIC void f_logNotHandledFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + IF_LogMessageClient * pLogMessage, + f_va_list * args); + +/**************************************************************************** +Desc: Handle text portions of the format string +****************************************************************************/ +FSTATIC void f_logProcessFormatString( + FLMUINT uiLen, + IF_LogMessageClient * pLogMessage, ...) +{ + f_va_list args; + + f_va_start( args, pLogMessage); + if( uiLen) + { + f_logStringFormatter( 0, uiLen, uiLen, 0, pLogMessage, &args); + } + f_va_end(args); +} + +/**************************************************************************** +Desc: Parse arguments in format string, calling appropriate handlers +****************************************************************************/ +FSTATIC void f_logParsePrintfArgs( + FLMBYTE * pszFormat, + f_va_list * args, + IF_LogMessageClient * pLogMessage) +{ + FLMBYTE ucChar; + FLMUINT uiFlags; + FLMUINT uiWidth; + FLMUINT uiPrecision; + FLMBYTE * pszTextStart = pszFormat; + + while( (ucChar = *pszFormat++) != 0) + { + if( ucChar != '%') + { + // Handle invalid characters + if( ucChar < ASCII_SPACE || ucChar > ASCII_TILDE) + { + uiWidth = (FLMUINT)(pszFormat - pszTextStart - 1); + + if( uiWidth) + { + f_logProcessFormatString( uiWidth, + pLogMessage, pszTextStart); + } + + // Only call newline() if ASCII_NEWLINE character. + // We will skip ASCII_CR characters + + if( ucChar == ASCII_NEWLINE) + { + pLogMessage->newline(); + } + pszTextStart = pszFormat; + } + + continue; + } + + uiWidth = (FLMUINT)(pszFormat - pszTextStart - 1); + f_logProcessFormatString( uiWidth, pLogMessage, pszTextStart); + + f_sprintfProcessFieldInfo( &pszFormat, &uiWidth, + &uiPrecision, &uiFlags, args); + + ucChar = (unsigned char)*pszFormat++; + switch( ucChar) + { + case '%': + case 'c': + f_logCharFormatter( ucChar, uiWidth, + uiPrecision, uiFlags, pLogMessage, args); + break; + case 'C': + f_logColorFormatter( ucChar, uiWidth, + uiPrecision, uiFlags, pLogMessage, args); + break; + case 'd': + case 'o': + case 'u': + case 'x': + case 'X': + f_logNumberFormatter( ucChar, uiWidth, + uiPrecision, uiFlags, pLogMessage, args); + break; + case 's': + case 'S': + f_logStringFormatter( ucChar, uiWidth, + uiPrecision, uiFlags, pLogMessage, args); + break; + case 'e': + f_logErrorFormatter( ucChar, uiWidth, + uiPrecision, uiFlags, pLogMessage, args); + break; + default: + f_logNotHandledFormatter( ucChar, uiWidth, + uiPrecision, uiFlags, pLogMessage, args); + break; + } + + pszTextStart = pszFormat; + } + + f_logProcessFormatString( (FLMUINT)(pszFormat - pszTextStart - 1), + pLogMessage, pszTextStart); +} + +/**************************************************************************** +Desc: Default string formatter. +****************************************************************************/ +FSTATIC void f_logStringFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + IF_LogMessageClient * pLogMessage, + f_va_list * args) +{ + FLMBYTE ucOutputBuf[ 128]; + FLMBYTE * pszDest = &ucOutputBuf[ 0]; + FLMUINT uiMaxLen = sizeof( ucOutputBuf) - 1; + void * pAllocBuf = NULL; + F_SPRINTF_INFO info; + + if( uiWidth >= uiMaxLen) + { + // Need to allocate a temporary buffer + + uiMaxLen = uiWidth; + if (RC_BAD( f_alloc( (FLMUINT)(uiMaxLen + 1), &pAllocBuf))) + { + uiWidth = uiMaxLen; + } + else + { + pszDest = (FLMBYTE *)pAllocBuf; + } + } + + f_memset( &info, 0, sizeof( info)); + info.pszDestStr = pszDest; + + f_sprintfStringFormatter( ucFormatChar, uiWidth, uiPrecision, + uiFlags, &info, args); + + pLogMessage->appendString( (char *)pszDest); + + if( pAllocBuf) + { + f_free( &pAllocBuf); + } +} + +/**************************************************************************** +Desc: Default number formatter. +****************************************************************************/ +FSTATIC void f_logNumberFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + IF_LogMessageClient * pLogMessage, + f_va_list * args) +{ + FLMBYTE ucOutputBuf[ 128]; + F_SPRINTF_INFO info; + + f_memset( &info, 0, sizeof( info)); + info.pszDestStr = &ucOutputBuf[ 0]; + f_sprintfNumberFormatter( ucFormatChar, uiWidth, + uiPrecision, uiFlags, &info, args); + pLogMessage->appendString( (char *)ucOutputBuf); +} + +/**************************************************************************** +Desc: Default error formatter. +****************************************************************************/ +FSTATIC void f_logErrorFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + IF_LogMessageClient * pLogMessage, + f_va_list * args) +{ + FLMBYTE ucOutputBuf[ 128]; + F_SPRINTF_INFO info; + + f_memset( &info, 0, sizeof( info)); + info.pszDestStr = &ucOutputBuf[ 0]; + f_sprintfErrorFormatter( ucFormatChar, uiWidth, + uiPrecision, uiFlags, &info, args); + pLogMessage->appendString( (char *)ucOutputBuf); +} + +/**************************************************************************** +Desc: Color formatter +****************************************************************************/ +FSTATIC void f_logColorFormatter( + FLMBYTE, // ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + IF_LogMessageClient * pLogMessage, + f_va_list * // args + ) +{ + if( uiFlags & FLM_PRINTF_PLUS_FLAG) + { + // Push a color onto the stack + + if( !uiWidth) + { + // Foreground + + pLogMessage->pushForegroundColor(); + } + else + { + // Background + + pLogMessage->pushBackgroundColor(); + } + } + else if( uiFlags & FLM_PRINTF_MINUS_FLAG) + { + // Pop a color from the color stack + + if( !uiWidth) + { + // Foreground + + pLogMessage->popForegroundColor(); + } + else + { + // Background + + pLogMessage->popBackgroundColor(); + } + } + else + { + eColorType eForeground = (eColorType)(uiWidth + 1); + eColorType eBackground = (eColorType)(uiPrecision + 1); + + // Set a new foreground and/or background color + + if( eForeground >= FLM_NUM_COLORS || eBackground >= FLM_NUM_COLORS) + { + goto Exit; + } + + pLogMessage->changeColor( eForeground, eBackground); + } + +Exit: + + return; +} + +/**************************************************************************** +Desc: Default character formatter. + Prints the character specified by VALUE in 'c', or the '%' character. + Format: %[flags][width][.prec]'c' + flags = + width = + prec = +****************************************************************************/ +FSTATIC void f_logCharFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + IF_LogMessageClient * pLogMessage, + f_va_list * args) +{ + FLMBYTE ucOutputBuf[ 32]; + F_SPRINTF_INFO info; + + f_memset( &info, 0, sizeof( info)); + info.pszDestStr = &ucOutputBuf[ 0]; + f_sprintfCharFormatter( ucFormatChar, uiWidth, + uiPrecision, uiFlags, &info, args); + pLogMessage->appendString( (char *)ucOutputBuf); +} + +/**************************************************************************** +Desc: Unhandled format strings +****************************************************************************/ +FSTATIC void f_logNotHandledFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + IF_LogMessageClient * pLogMessage, + f_va_list * args) +{ + FLMBYTE ucOutputBuf[ 64]; + F_SPRINTF_INFO info; + + f_memset( &info, 0, sizeof( info)); + info.pszDestStr = &ucOutputBuf[ 0]; + f_sprintfNotHandledFormatter( ucFormatChar, uiWidth, + uiPrecision, uiFlags, &info, args); + pLogMessage->appendString( (char *)ucOutputBuf); +} + +/**************************************************************************** +Desc: Main entry point for printf functionality. +****************************************************************************/ +void f_logPrintf( + IF_LogMessageClient * pLogMessage, + const char * szFormatStr, ...) +{ + f_va_list args; + + f_va_start( args, szFormatStr); + f_logParsePrintfArgs( (FLMBYTE *)szFormatStr, &args, pLogMessage); + f_va_end( args); +} + +/**************************************************************************** +Desc: Printf routine that accepts a va_list argument +****************************************************************************/ +void FLMAPI f_logVPrintf( + IF_LogMessageClient * pLogMessage, + const char * szFormatStr, + f_va_list * args) +{ + f_logParsePrintfArgs( (FLMBYTE *)szFormatStr, args, pLogMessage); +} + +/**************************************************************************** +Desc: Returns an IF_LogMessageClient object if logging is enabled for the + specified message type +****************************************************************************/ +IF_LogMessageClient * FLMAPI f_beginLogMessage( + FLMUINT uiMsgType) +{ + IF_LogMessageClient * pNewMsg = NULL; + + f_mutexLock( gv_hLoggerMutex); + + if( !gv_pLogger) + { + goto Exit; + } + + if( (pNewMsg = gv_pLogger->beginMessage( uiMsgType)) != NULL) + { + gv_uiPendingLogMessages++; + } + +Exit: + + f_mutexUnlock( gv_hLoggerMutex); + return( pNewMsg); +} + +/**************************************************************************** +Desc: Logs information about an error +****************************************************************************/ +void FLMAPI f_logError( + RCODE rc, + const char * pszDoing, + const char * pszFileName, + FLMINT iLineNumber) +{ + FLMBYTE * pszMsgBuf = NULL; + IF_LogMessageClient * pLogMsg = NULL; + + if( (pLogMsg = f_beginLogMessage( 0)) != NULL) + { + if( RC_OK( f_alloc( 512, &pszMsgBuf))) + { + if( pszFileName) + { + f_sprintf( (char *)pszMsgBuf, + "Error %s: %e, File=%s, Line=%d.", + pszDoing, rc, pszFileName, (int)iLineNumber); + } + else + { + f_sprintf( (char *)pszMsgBuf, "Error %s: %e.", pszDoing, rc); + } + + pLogMsg->changeColor( FLM_YELLOW, FLM_BLACK); + pLogMsg->appendString( (char *)pszMsgBuf); + } + + f_endLogMessage( &pLogMsg); + } + + if( pszMsgBuf) + { + f_free( &pszMsgBuf); + } +} + +/**************************************************************************** +Desc: Ends a logging message +****************************************************************************/ +void FLMAPI f_endLogMessage( + IF_LogMessageClient ** ppLogMessage) +{ + if( *ppLogMessage) + { + f_mutexLock( gv_hLoggerMutex); + flmAssert( gv_uiPendingLogMessages); + + (*ppLogMessage)->endMessage(); + (*ppLogMessage)->Release(); + *ppLogMessage = NULL; + + gv_uiPendingLogMessages--; + f_mutexUnlock( gv_hLoggerMutex); + } +} diff --git a/ftk/src/ftkmem.cpp b/ftk/src/ftkmem.cpp new file mode 100644 index 0000000..6f42089 --- /dev/null +++ b/ftk/src/ftkmem.cpp @@ -0,0 +1,4107 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains the f_alloc, f_calloc, f_realloc, f_recalloc, +// and f_free routines. +// +// Tabs: 3 +// +// Copyright (c) 1991, 1993, 1995-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +typedef struct F_MemHdrTag +{ + FLMUINT uiDataSize; +#ifdef FLM_DEBUG + const char * pszFileName; + FLMINT iLineNumber; + FLMBOOL bAllocFromNewOp; + FLMUINT uiAllocationId; + FLMUINT uiAllocCnt; + FLMUINT * puiStack; +#endif +#if FLM_ALIGN_SIZE == 8 + FLMUINT uiDummy; +#endif +} F_MEM_HDR; + +#define F_GET_ALLOC_PTR( pDataPtr) \ + (FLMBYTE *)((FLMBYTE *)(pDataPtr) - sizeof( F_MEM_HDR)) + +#define F_GET_DATA_PTR( pAllocPtr) \ + (FLMBYTE *)((FLMBYTE *)(pAllocPtr) + sizeof( F_MEM_HDR)) + +#define F_GET_MEM_DATA_SIZE( pDataPtr) \ + (((F_MEM_HDR *)(F_GET_ALLOC_PTR( pDataPtr)))->uiDataSize) + +#if defined( FLM_UNIX) || defined( FLM_NLM) || defined( FLM_WIN) + #define PTR_IN_MBLK(p,bp,offs) \ + (((FLMBYTE *)(p) > (FLMBYTE *)(bp)) && \ + ((FLMBYTE *)(p) <= (FLMBYTE *)(bp) + (offs))) +#else + #error Platform not supported +#endif + +#define MEM_PTR_INIT_ARRAY_SIZE 512 +#define MEM_MAX_STACK_WALK_DEPTH 32 + +#define F_PICKET_FENCE "FFFFFFFF" +#if defined( FLM_DEBUG) + #define F_PICKET_FENCE_SIZE 8 +#else + #define F_PICKET_FENCE_SIZE 0 +#endif + +#ifdef FLM_NLM + extern "C" + { + void GetClosestSymbol( + BYTE * szBuffer, + LONG udAddress); + } +#endif + +static FLMBOOL gv_bMemTrackingInitialized = FALSE; +static FLMUINT gv_uiInitThreadId = 0; +static F_MUTEX gv_hMemTrackingMutex = F_MUTEX_NULL; +static FLMUINT gv_uiMemTrackingPtrArraySize = 0; +static FLMBOOL gv_bTrackLeaks = FALSE; +static FLMUINT gv_uiNumMemPtrs = 0; +static void ** gv_ppvMemTrackingPtrs = NULL; +static FLMUINT gv_uiNextMemPtrSlotToUse = 0; +static FLMUINT gv_uiAllocCnt = 0; +static FLMBOOL gv_bStackWalk = FALSE; +static FLMBOOL gv_bLogLeaks = FALSE; + +#ifdef FLM_WIN + static HANDLE gv_hMemProcess; +#endif + +FSTATIC FLMBOOL initMemTracking( void); + +FSTATIC void saveMemTrackingInfo( + F_MEM_HDR * pHdr); + +FSTATIC void updateMemTrackingInfo( + F_MEM_HDR * pHdr); + +FSTATIC void freeMemTrackingInfo( + FLMBOOL bMutexAlreadyLocked, + FLMUINT uiId, + FLMUINT * puiStack); + +/************************************************************************ +Desc: +*************************************************************************/ +FINLINE FLMUINT f_msize( + void * pvPtr) +{ +#if defined( FLM_UNIX) + return( pvPtr ? F_GET_MEM_DATA_SIZE( (pvPtr)) : 0); +#elif defined( FLM_NLM) + return( pvPtr ? msize( (F_GET_ALLOC_PTR( (pvPtr)))) : 0); +#else + return( pvPtr ? _msize( (F_GET_ALLOC_PTR( (pvPtr)))) : 0); +#endif +} + +/************************************************************************ +Desc: Returns the current value of EBP--the value of the caller's stack + frame pointer. +*************************************************************************/ +#ifdef FLM_NLM + void * memGetEBP(void); + + #ifdef FLM_MWERKS_NLM + void * memGetEBP(void) + { + __asm + { + mov eax,[ebp] + } + } + #else + #pragma aux memGetEBP = "mov eax,ebp"; + #endif +#endif + +/************************************************************************ +Desc: Returns the value at SS:[POS+OFFSET]. +*************************************************************************/ +#ifdef FLM_NLM + + void * memValueAtStackOffset(void *pos, int offset); + + #ifdef FLM_MWERKS_NLM + void * memValueAtStackOffset(void *, int) + { + __asm + { + mov eax,[ebp+0x8] + mov ebx,[ebp+0xC] + mov eax,ss:[eax+ebx] + } + } + #else + #pragma aux memValueAtStackOffset = "mov eax,ss:[eax+ebx]" parm [eax] [ebx]; + #endif + +#endif + +/************************************************************************ +Desc: +*************************************************************************/ +#ifdef FLM_NLM +FLMUINT * memWalkStack() +{ + FLMUINT uiLoop; + FLMUINT uiRtnAddr; + FLMUINT uiEbp = (FLMUINT) memGetEBP(); + FLMUINT uiAddresses [MEM_MAX_STACK_WALK_DEPTH + 1]; + FLMUINT * puiAddresses; + + uiEbp = (FLMUINT) memValueAtStackOffset( (void *)uiEbp, 0); + uiRtnAddr = (FLMUINT) memValueAtStackOffset( (void *)uiEbp, 4); + + for (uiLoop = 0; uiLoop < MEM_MAX_STACK_WALK_DEPTH; uiLoop++) + { + FLMUINT uiOldEbp; + + uiAddresses [uiLoop] = uiRtnAddr; + if (!uiEbp) + { + break; + } + + uiOldEbp = uiEbp; + uiEbp = (FLMUINT) memValueAtStackOffset( (void *)uiEbp, 0); + + if (!uiEbp || uiEbp <= uiOldEbp || uiEbp > uiOldEbp + 5000) + { + break; + } + + uiRtnAddr = (FLMUINT) memValueAtStackOffset( (void *) uiEbp, 4); + } + uiAddresses [uiLoop] = 0; + if ((puiAddresses = (FLMUINT *)malloc( + sizeof( FLMUINT) * (uiLoop+1))) != NULL) + { + f_memcpy( puiAddresses, &uiAddresses [0], sizeof( FLMUINT) * (uiLoop + 1)); + } + return( puiAddresses); +} +#endif + +/******************************************************************** +Desc: Walk the call stack. +*********************************************************************/ +#ifdef FLM_WIN +FLMUINT * memWalkStack() +{ + STACKFRAME64 stackFrame; + CONTEXT context; + DWORD machineType; + FLMUINT uiLoop; + FLMUINT uiAddresses [MEM_MAX_STACK_WALK_DEPTH + 1]; + FLMUINT * puiAddresses; + HANDLE hThread; + HANDLE hProcess; + FLMUINT uiAddrCount; + + f_memset( &stackFrame, 0, sizeof( stackFrame)); + f_memset( &context, 0, sizeof( context)); + +#ifdef FLM_64BIT + machineType = IMAGE_FILE_MACHINE_IA64; +#else + machineType = IMAGE_FILE_MACHINE_I386; +#endif + + // While you can continue walking the stack... + + unsigned vEBP, vEIP; + __asm mov vEBP, ebp + __asm call near nextinstr +nextinstr: + __asm pop vEIP; + + context.Ebp = vEBP; + context.Eip = vEIP; + stackFrame.AddrPC.Offset = vEIP; + stackFrame.AddrFrame.Offset = vEBP; + stackFrame.AddrPC.Mode = AddrModeFlat; + stackFrame.AddrFrame.Mode = AddrModeFlat; + + // Must lock the mutex because StackWalk is not thread safe. + + f_mutexLock( gv_hMemTrackingMutex); + hProcess = OpenProcess( PROCESS_VM_READ, FALSE, GetCurrentProcessId()); + hThread = OpenThread( THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME, + FALSE, GetCurrentThreadId()); + + // We have already processed the address inside memWalkStack + + uiAddrCount = 1; + uiLoop = 0; + for (;;) + { + if (!StackWalk64( machineType, hProcess, hThread, &stackFrame, + &context, NULL, + SymFunctionTableAccess64, SymGetModuleBase64, NULL)) + { + break; + } + + // Skip the first two addresses. These represent the following: + // 1) memWalkStack + // 2) saveMemTrackingInfo or updateMemTrackingInfo + // We don't need to see them in the stack trace. + + uiAddrCount++; + if (uiAddrCount > 2) + { + uiAddresses [uiLoop] = (FLMUINT)stackFrame.AddrReturn.Offset; + uiLoop++; + if (uiLoop == MEM_MAX_STACK_WALK_DEPTH) + { + break; + } + } + } + + f_mutexUnlock( gv_hMemTrackingMutex); + + uiAddresses [uiLoop] = 0; + if ((puiAddresses = (FLMUINT *)malloc( + sizeof( FLMUINT) * (uiLoop+1))) != NULL) + { + f_memcpy( puiAddresses, &uiAddresses [0], sizeof( FLMUINT) * (uiLoop + 1)); + } + return( puiAddresses); +} +#endif + +/******************************************************************** +Desc: Initialize memory tracking +*********************************************************************/ +#ifdef FLM_DEBUG +FSTATIC FLMBOOL initMemTracking( void) +{ + RCODE rc; + F_MUTEX memMutex; + + if (!gv_bMemTrackingInitialized && !gv_uiInitThreadId) + { + gv_uiInitThreadId = f_threadId(); + rc = f_mutexCreate( &memMutex); + f_sleep( 50); + + // Only set to initialized if we were the last thread + // to set gv_uiInitThreadId + + if (f_threadId() == gv_uiInitThreadId) + { + if (RC_OK( rc)) + { + gv_hMemTrackingMutex = memMutex; + } + else + { + gv_hMemTrackingMutex = F_MUTEX_NULL; + } +#ifdef FLM_WIN + SymSetOptions( SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); + gv_hMemProcess = GetCurrentProcess(); + SymInitialize( gv_hMemProcess, NULL, TRUE); +#endif + gv_bMemTrackingInitialized = TRUE; + } + else + { + if (RC_OK( rc)) + { + f_mutexDestroy( &memMutex); + } + } + } + + // Go into a loop until we see initialized flag set to TRUE + // Could be another thread that is doing it. + + while (!gv_bMemTrackingInitialized) + { + f_sleep( 10); + } + + return( (gv_hMemTrackingMutex != F_MUTEX_NULL) ? TRUE : FALSE); +} +#endif + +/******************************************************************** +Desc: Save memory tracking information - called on alloc or realloc. +*********************************************************************/ +#ifdef FLM_DEBUG +FSTATIC void saveMemTrackingInfo( + F_MEM_HDR * pHdr) +{ + FLMUINT uiNewCnt; + FLMUINT uiId; + void ** pNew; + + if (gv_bTrackLeaks && initMemTracking()) + { + f_mutexLock( gv_hMemTrackingMutex); + + // See if there is enough room in the array + + if (gv_uiNumMemPtrs == gv_uiMemTrackingPtrArraySize) + { + + // If array is not initialized, use initial count. Otherwise + // double the size. + + uiNewCnt = (FLMUINT)((!gv_uiMemTrackingPtrArraySize) + ? MEM_PTR_INIT_ARRAY_SIZE + : gv_uiMemTrackingPtrArraySize * 2); + if ((pNew = (void **)malloc( sizeof( void *) * uiNewCnt)) != NULL) + { + + // Copy the pointers from the old array, if any, + // into the newly allocated array. + + if (gv_uiMemTrackingPtrArraySize) + { + f_memcpy( pNew, gv_ppvMemTrackingPtrs, + sizeof( void *) * gv_uiMemTrackingPtrArraySize); + free( gv_ppvMemTrackingPtrs); + gv_ppvMemTrackingPtrs = NULL; + } + f_memset( &pNew [gv_uiMemTrackingPtrArraySize], 0, + sizeof( void *) * (uiNewCnt - gv_uiMemTrackingPtrArraySize)); + gv_ppvMemTrackingPtrs = pNew; + gv_uiMemTrackingPtrArraySize = uiNewCnt; + } + } + + // If we are still full, we were not able to reallocate memory, so we + // do nothing. + + if (gv_uiNumMemPtrs == gv_uiMemTrackingPtrArraySize) + { + pHdr->uiAllocationId = 0; + } + else + { + // Find an empty slot - there has to be one! + + uiId = gv_uiNextMemPtrSlotToUse; + while (gv_ppvMemTrackingPtrs [uiId]) + { + if (++uiId == gv_uiMemTrackingPtrArraySize) + { + uiId = 0; + } + } + + // Allocation ID in the header is offset by one to avoid + // using a value of zero. + + pHdr->uiAllocationId = uiId + 1; + gv_ppvMemTrackingPtrs [uiId] = pHdr; + gv_uiNumMemPtrs++; + if ((gv_uiNextMemPtrSlotToUse = uiId + 1) == + gv_uiMemTrackingPtrArraySize) + { + gv_uiNextMemPtrSlotToUse = 0; + } + } + pHdr->uiAllocCnt = ++gv_uiAllocCnt; + f_mutexUnlock( gv_hMemTrackingMutex); + } + else + { + pHdr->uiAllocationId = 0; + pHdr->uiAllocCnt = 0; + } + + // Follow the stack. + + if (gv_bTrackLeaks && gv_bStackWalk) + { + pHdr->puiStack = memWalkStack(); + } + else + { + pHdr->puiStack = NULL; + } +} +#endif + +/******************************************************************** +Desc: Update memory tracking information - called after realloc +*********************************************************************/ +#ifdef FLM_DEBUG +FSTATIC void updateMemTrackingInfo( + F_MEM_HDR * pHdr) +{ + if (pHdr->puiStack) + { + free( pHdr->puiStack); + pHdr->puiStack = NULL; + } + if (gv_bTrackLeaks && gv_bStackWalk) + { + pHdr->puiStack = memWalkStack(); + } +} +#endif + +/******************************************************************** +Desc: Free memory tracking information - called on free. +*********************************************************************/ +#ifdef FLM_DEBUG +FSTATIC void freeMemTrackingInfo( + FLMBOOL bMutexAlreadyLocked, + FLMUINT uiId, + FLMUINT * puiStack) +{ + if (uiId) + { + // NOTE: If uiId is non-zero, it means we had to have + // successfully initialized, so we are guaranteed to + // have a mutex. + + if ( !bMutexAlreadyLocked) + { + f_mutexLock( gv_hMemTrackingMutex); + } + + // Allocation ID in the header is offset by one so that it + // is never zero - a value of zero means that the allocation + // does not have a slot for tracking it in the array. + + gv_ppvMemTrackingPtrs [uiId - 1] = NULL; + flmAssert( gv_uiNumMemPtrs); + gv_uiNumMemPtrs--; + + if ( !bMutexAlreadyLocked) + { + f_mutexUnlock( gv_hMemTrackingMutex); + } + } + + // Free the stack information, if any. + + if (puiStack) + { + free( puiStack); + } +} +#endif + +/******************************************************************** +Desc: Log memory leaks. +*********************************************************************/ +#ifdef FLM_DEBUG +void logMemLeak( + F_MEM_HDR * pHdr) +{ + char szTmpBuffer [1024]; + FLMUINT uiMsgBufSize; + char * pszMessageBuffer; + char * pszTmp; + IF_FileHdl * pFileHdl = NULL; + F_FileSystem fileSys; + FLMBOOL bClearFileSys = FALSE; + FLMBOOL bSaveTrackLeaks = gv_bTrackLeaks; + + gv_bTrackLeaks = FALSE; + + // Need a big buffer to show an entire stack. + + uiMsgBufSize = 20480; + if ((pszMessageBuffer = (char *)malloc( uiMsgBufSize)) == NULL) + { + pszMessageBuffer = &szTmpBuffer [0]; + uiMsgBufSize = sizeof( szTmpBuffer); + } + pszTmp = pszMessageBuffer; + + if( !gv_pFileSystem) + { + gv_pFileSystem = &fileSys; + bClearFileSys = TRUE; + } + + // Format message to be logged. + + f_strcpy( pszTmp, "Abort=Debug, Retry=Continue, Ignore=Don't Show\r\n"); + while (*pszTmp) + { + pszTmp++; + } +#if defined( FLM_WIN) && defined( FLM_64BIT) + f_sprintf( pszTmp, "Unfreed Pointer: 0x%016I64x\r\n", (FLMUINT)(&pHdr [1])); +#else + f_sprintf( pszTmp, "Unfreed Pointer: 0x%08x\r\n", + (unsigned)((FLMUINT)(&pHdr [1]))); +#endif + while (*pszTmp) + { + pszTmp++; + } + + if (pHdr->pszFileName) + { + f_sprintf( pszTmp, "Source: %s, Line#: %u\r\n", pHdr->pszFileName, + (unsigned)pHdr->iLineNumber); + while (*pszTmp) + { + pszTmp++; + } + } + + if (pHdr->uiAllocCnt) + { + f_sprintf( pszTmp, "Malloc #: %u\r\n", (unsigned)pHdr->uiAllocCnt); + while (*pszTmp) + { + pszTmp++; + } + } + f_sprintf( (char *)pszTmp, "Size: %u bytes\r\n", (unsigned)pHdr->uiDataSize); + while (*pszTmp) + { + pszTmp++; + } + + if (pHdr->puiStack) + { + FLMUINT * puiStack = pHdr->puiStack; + FLMUINT uiLen = pszTmp - pszMessageBuffer; + char szFuncName [200]; + char * pszFuncName; +#ifdef FLM_WIN + IMAGEHLP_SYMBOL * pImgHlpSymbol; + + pImgHlpSymbol = (IMAGEHLP_SYMBOL *)malloc( + sizeof( IMAGEHLP_SYMBOL) + 100); +#endif + + while (*puiStack) + { + szFuncName [0] = 0; +#ifdef FLM_WIN + if (pImgHlpSymbol) + { + #ifdef FLM_64BIT + DWORD64 udDisplacement; + #else + DWORD udDisplacement; + #endif + + pImgHlpSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL); + pImgHlpSymbol->Address = *puiStack; + pImgHlpSymbol->MaxNameLength = 100; + + if (SymGetSymFromAddr( gv_hMemProcess, *puiStack, + &udDisplacement, pImgHlpSymbol)) + { + f_sprintf( szFuncName, "\t%s + %X\r\n", + (char *)(&pImgHlpSymbol->Name [0]), + udDisplacement); + } + } +#elif defined( FLM_NLM) + { + szFuncName [0] = '\t'; + GetClosestSymbol( (BYTE *)(&szFuncName[1]), (LONG)(*puiStack)); + } +#else + +#ifdef HAVE_DLADDR + { + Dl_info dlip; + + if (dladdr( (void *)(*puiStack), &dlip) != 0 && dlip.dli_sname) + { + const char * pszFileName; + if (dlip.dli_saddr != (void *)(*puiStack)) + { + pszFileName = strrchr(dlip.dli_fname, '/'); + if (!pszFileName) + { + pszFileName = dlip.dli_fname; + } + else + { + pszFileName++; // skip over slash + } + f_sprintf( szFuncName, "\t0x%08x (%s)\r\n", + (unsigned)(*puiStack), pszFileName); + } + else + { + f_sprintf( szFuncName, "\t%s\r\n", dlip.dli_sname); + } + } + } +#endif + +#endif + + // If szFuncName [0] is zero, we didn't find a name, so we + // just output the address in HEX. + + if (!szFuncName [0]) + { + f_sprintf( szFuncName, "\t0x%08X\r\n", (unsigned)*puiStack ); + } + + // Output whatever portion of the name will fit into the + // message buffer. + + pszFuncName = &szFuncName [0]; + while (*pszFuncName && uiLen < uiMsgBufSize - 1) + { + *pszTmp++ = *pszFuncName++; + uiLen++; + } + + // Process next address in the stack. + + puiStack++; + } + *pszTmp = 0; +#ifdef FLM_WIN + if (pImgHlpSymbol) + { + free( pImgHlpSymbol); + } +#endif + } + +#ifdef FLM_WIN + FLMINT iRet; + + iRet = MessageBox( NULL, (LPCTSTR)pszMessageBuffer, "WIN32 Memory Testing", + MB_ABORTRETRYIGNORE | MB_ICONINFORMATION | MB_TASKMODAL + | MB_SETFOREGROUND | MB_DEFBUTTON2); + if (iRet == IDIGNORE) + { + gv_bLogLeaks = TRUE; + } + else if (iRet == IDABORT) + { + flmAssert( 0); + } +#else + gv_bLogLeaks = TRUE; +#endif + + if (gv_bLogLeaks) + { + F_FileSystem FileSystem; + RCODE rc; + FLMUINT uiDummy; +#ifdef FLM_NLM + const char * pszErrPath = "sys:\\memtest.ert"; +#else + const char * pszErrPath = "memtest.ert"; +#endif + + if (RC_BAD( rc = FileSystem.openFile( pszErrPath, + FLM_IO_RDWR | FLM_IO_SH_DENYNONE, &pFileHdl))) + { + if (rc == NE_FLM_IO_PATH_NOT_FOUND) + { + rc = FileSystem.createFile( pszErrPath, + FLM_IO_RDWR | FLM_IO_SH_DENYNONE, &pFileHdl); + } + } + else + { + // Position to append to file. + + rc = pFileHdl->seek( 0, FLM_IO_SEEK_END, NULL); + } + + // If we successfully opened the file, write to it. + + if (RC_OK( rc)) + { + if (RC_OK( pFileHdl->write( FLM_IO_CURRENT_POS, + (FLMUINT)(pszTmp - pszMessageBuffer), + pszMessageBuffer, &uiDummy))) + { + (void)pFileHdl->flush(); + } + pFileHdl->close(); + } + } + + if (pFileHdl) + { + pFileHdl->Release(); + } + + if( bClearFileSys) + { + gv_pFileSystem = NULL; + } + + if (pszMessageBuffer != &szTmpBuffer [0]) + { + free( pszMessageBuffer); + } + + gv_bTrackLeaks = bSaveTrackLeaks; +} +#endif + +/******************************************************************** +Desc: Initialize memory - if not already done. +*********************************************************************/ +void f_memoryInit( void) +{ +#ifdef FLM_DEBUG + (void)initMemTracking(); +#endif +} + +/******************************************************************** +Desc: Clean up memory and check for unfreed memory. +*********************************************************************/ +void f_memoryCleanup( void) +{ +#ifdef FLM_DEBUG + if (initMemTracking()) + { + FLMUINT uiId; + F_MEM_HDR * pHdr; + + f_mutexLock( gv_hMemTrackingMutex); + for (uiId = 0; uiId < gv_uiMemTrackingPtrArraySize; uiId++) + { + if ((pHdr = (F_MEM_HDR *)gv_ppvMemTrackingPtrs [uiId]) != NULL) + { + logMemLeak( pHdr); + freeMemTrackingInfo( TRUE, uiId + 1, pHdr->puiStack); + } + } + + // Free the memory pointer array. + + free( gv_ppvMemTrackingPtrs); + gv_ppvMemTrackingPtrs = NULL; + gv_uiMemTrackingPtrArraySize = 0; + gv_uiNumMemPtrs = 0; + + f_mutexUnlock( gv_hMemTrackingMutex); + + // Free up the mutex. + + f_mutexDestroy( &gv_hMemTrackingMutex); + + // Reset to unitialized state. + + gv_uiInitThreadId = 0; + gv_hMemTrackingMutex = F_MUTEX_NULL; + gv_bMemTrackingInitialized = FALSE; +#ifdef FLM_WIN + SymCleanup( gv_hMemProcess); +#endif + } +#endif +} + +/******************************************************************** +Desc: Allocate Memory. +*********************************************************************/ +#ifdef FLM_DEBUG +RCODE f_allocImp( + FLMUINT uiSize, + void ** ppvPtr, + FLMBOOL bAllocFromNewOp, + const char * pszFileName, + FLMINT iLineNumber) +#else +RCODE f_allocImp( + FLMUINT uiSize, + void ** ppvPtr) +#endif +{ + RCODE rc = NE_FLM_OK; + F_MEM_HDR * pHdr; + + if( (pHdr = (F_MEM_HDR *)malloc( uiSize + sizeof( F_MEM_HDR) + + F_PICKET_FENCE_SIZE)) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + pHdr->uiDataSize = uiSize; + *ppvPtr = (void *)(&pHdr [1]); + +#ifdef FLM_DEBUG + pHdr->bAllocFromNewOp = bAllocFromNewOp; + pHdr->iLineNumber = iLineNumber; + pHdr->pszFileName = pszFileName; + saveMemTrackingInfo( pHdr); + + #if F_PICKET_FENCE_SIZE + f_memcpy( ((FLMBYTE *)(*ppvPtr)) + uiSize, + F_PICKET_FENCE, F_PICKET_FENCE_SIZE); + #endif + +#endif + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Allocate and initialize memory. +*********************************************************************/ +#ifdef FLM_DEBUG +RCODE f_callocImp( + FLMUINT uiSize, + void ** ppvPtr, + const char * pszFileName, + FLMINT iLineNumber + ) +#else +RCODE f_callocImp( + FLMUINT uiSize, + void ** ppvPtr + ) +#endif +{ + RCODE rc = NE_FLM_OK; + F_MEM_HDR * pHdr; + + if ((pHdr = (F_MEM_HDR *)malloc( uiSize + sizeof( F_MEM_HDR) + + F_PICKET_FENCE_SIZE)) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + pHdr->uiDataSize = uiSize; + *ppvPtr = (void *)(&pHdr [1]); + f_memset( *ppvPtr, 0, uiSize); +#ifdef FLM_DEBUG + pHdr->bAllocFromNewOp = FALSE; + pHdr->iLineNumber = iLineNumber; + pHdr->pszFileName = pszFileName; + saveMemTrackingInfo( pHdr); + + #if F_PICKET_FENCE_SIZE + + f_memcpy( ((FLMBYTE *)(*ppvPtr)) + uiSize, + F_PICKET_FENCE, F_PICKET_FENCE_SIZE); + + #endif + +#endif +Exit: + return( rc); +} + +/******************************************************************** +Desc: Reallocate memory. +*********************************************************************/ +#ifdef FLM_DEBUG +RCODE f_reallocImp( + FLMUINT uiSize, + void ** ppvPtr, + const char * pszFileName, + FLMINT iLineNumber + ) +#else +RCODE f_reallocImp( + FLMUINT uiSize, + void ** ppvPtr + ) +#endif +{ + RCODE rc = NE_FLM_OK; + F_MEM_HDR * pNewHdr; +#ifdef FLM_DEBUG + F_MEM_HDR * pOldHdr; + FLMUINT uiOldAllocationId; + FLMUINT * puiOldStack; +#endif + + if (!(*ppvPtr)) + { +#ifdef FLM_DEBUG + rc = f_allocImp( uiSize, ppvPtr, FALSE, pszFileName, iLineNumber); +#else + rc = f_allocImp( uiSize, ppvPtr); +#endif + goto Exit; + } + +#ifdef FLM_DEBUG + pOldHdr = (F_MEM_HDR *)F_GET_ALLOC_PTR( *ppvPtr); + + #if F_PICKET_FENCE_SIZE + + // Verify the old picket fence + + if (f_memcmp( ((FLMBYTE *)(*ppvPtr)) + pOldHdr->uiDataSize, + F_PICKET_FENCE, F_PICKET_FENCE_SIZE) != 0) + { + rc = RC_SET_AND_ASSERT( NE_FLM_MEM); + goto Exit; + } + + #endif + + // Cannot realloc memory that was allocated via a new operator + + if (pOldHdr->bAllocFromNewOp) + { + rc = RC_SET_AND_ASSERT( NE_FLM_MEM); + goto Exit; + } + + uiOldAllocationId = pOldHdr->uiAllocationId; + puiOldStack = pOldHdr->puiStack; +#endif + + if ((pNewHdr = (F_MEM_HDR *)realloc( F_GET_ALLOC_PTR( *ppvPtr), + uiSize + sizeof( F_MEM_HDR) + + F_PICKET_FENCE_SIZE)) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + pNewHdr->uiDataSize = uiSize; + *ppvPtr = (void *)(&pNewHdr [1]); +#ifdef FLM_DEBUG + pNewHdr->bAllocFromNewOp = FALSE; + pNewHdr->iLineNumber = iLineNumber; + pNewHdr->pszFileName = pszFileName; + if (pNewHdr != pOldHdr) + { + freeMemTrackingInfo( FALSE, uiOldAllocationId, puiOldStack); + saveMemTrackingInfo( pNewHdr); + } + else + { + updateMemTrackingInfo( pNewHdr); + } + + #if F_PICKET_FENCE_SIZE + + f_memcpy( ((FLMBYTE *)(*ppvPtr)) + uiSize, + F_PICKET_FENCE, F_PICKET_FENCE_SIZE); + + #endif + +#endif + +Exit: + return( rc); +} + +/******************************************************************** +Desc: Reallocate memory, and initialize the new part. +*********************************************************************/ +#ifdef FLM_DEBUG +RCODE f_recallocImp( + FLMUINT uiSize, + void ** ppvPtr, + const char * pszFileName, + FLMINT iLineNumber + ) +#else +RCODE f_recallocImp( + FLMUINT uiSize, + void ** ppvPtr + ) +#endif +{ + RCODE rc = NE_FLM_OK; + F_MEM_HDR * pNewHdr; + FLMUINT uiOldSize; +#ifdef FLM_DEBUG + F_MEM_HDR * pOldHdr; + FLMUINT uiOldAllocationId; + FLMUINT * puiOldStack; +#endif + + if (!(*ppvPtr)) + { +#ifdef FLM_DEBUG + rc = f_callocImp( uiSize, ppvPtr, pszFileName, iLineNumber); +#else + rc = f_callocImp( uiSize, ppvPtr); +#endif + goto Exit; + } + +#ifdef FLM_DEBUG + pOldHdr = (F_MEM_HDR *)F_GET_ALLOC_PTR( *ppvPtr); + + #if F_PICKET_FENCE_SIZE + + // Verify the old picket fence + + if (f_memcmp( ((FLMBYTE *)(*ppvPtr)) + pOldHdr->uiDataSize, + F_PICKET_FENCE, F_PICKET_FENCE_SIZE) != 0) + { + rc = RC_SET_AND_ASSERT( NE_FLM_MEM); + goto Exit; + } + + #endif + + // Cannot realloc memory that was allocated via a new operator + + if (pOldHdr->bAllocFromNewOp) + { + rc = RC_SET_AND_ASSERT( NE_FLM_MEM); + goto Exit; + } + + uiOldAllocationId = pOldHdr->uiAllocationId; + puiOldStack = pOldHdr->puiStack; + +#endif + + uiOldSize = F_GET_MEM_DATA_SIZE( *ppvPtr); + if ((pNewHdr = (F_MEM_HDR *)realloc( F_GET_ALLOC_PTR( *ppvPtr), + uiSize + sizeof( F_MEM_HDR) + + F_PICKET_FENCE_SIZE)) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + pNewHdr->uiDataSize = uiSize; + *ppvPtr = (void *)(&pNewHdr [1]); + if (uiOldSize < uiSize) + { + f_memset( ((FLMBYTE *)(*ppvPtr)) + uiOldSize, 0, + uiSize - uiOldSize); + } +#ifdef FLM_DEBUG + pNewHdr->bAllocFromNewOp = FALSE; + pNewHdr->iLineNumber = iLineNumber; + pNewHdr->pszFileName = pszFileName; + if (pNewHdr != pOldHdr) + { + freeMemTrackingInfo( FALSE, uiOldAllocationId, puiOldStack); + saveMemTrackingInfo( pNewHdr); + } + else + { + updateMemTrackingInfo( pNewHdr); + } + + #if F_PICKET_FENCE_SIZE + + f_memcpy( ((FLMBYTE *)(*ppvPtr)) + uiSize, + F_PICKET_FENCE, F_PICKET_FENCE_SIZE); + + #endif + +#endif +Exit: + return( rc); +} + +/******************************************************************** +Desc: Free previously allocated memory. +*********************************************************************/ +void f_freeImp( + void ** ppvPtr, + FLMBOOL bFreeFromDeleteOp) +{ +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( bFreeFromDeleteOp); +#endif + + if (*ppvPtr) + { +#ifdef FLM_DEBUG + + F_MEM_HDR * pHdr = (F_MEM_HDR *)F_GET_ALLOC_PTR( *ppvPtr); + + if (pHdr->bAllocFromNewOp && !bFreeFromDeleteOp || + !pHdr->bAllocFromNewOp && bFreeFromDeleteOp) + { + + // Either trying to free memory using f_free when + // allocated from new, or trying to free memory + // using delete when allocated from f_alloc, + // f_calloc, f_realloc, or f_recalloc. + + RC_UNEXPECTED_ASSERT( NE_FLM_MEM); + return; + } + + #if F_PICKET_FENCE_SIZE + + // Check the picket fence + + if (f_memcmp( ((FLMBYTE *)(*ppvPtr)) + pHdr->uiDataSize, + F_PICKET_FENCE, F_PICKET_FENCE_SIZE) != 0) + { + RC_UNEXPECTED_ASSERT( NE_FLM_MEM); + } + + #endif + + freeMemTrackingInfo( FALSE, pHdr->uiAllocationId, pHdr->puiStack); +#endif + + free( F_GET_ALLOC_PTR( *ppvPtr)); + *ppvPtr = NULL; + } +} + +/******************************************************************** +Desc: Reset the stack information for an allocation. +*********************************************************************/ +#ifdef FLM_DEBUG +void f_resetStackInfoImp( + void * pvPtr, + const char * pszFileName, + FLMINT iLineNumber) +{ + if (pvPtr) + { + + F_MEM_HDR * pHdr = (F_MEM_HDR *)F_GET_ALLOC_PTR( pvPtr); + + pHdr->iLineNumber = iLineNumber; + pHdr->pszFileName = pszFileName; + f_mutexLock( gv_hMemTrackingMutex); + pHdr->uiAllocCnt = ++gv_uiAllocCnt; + f_mutexUnlock( gv_hMemTrackingMutex); + updateMemTrackingInfo( pHdr); + } +} +#endif + +/************************************************************************ +Desc: Destructor +*************************************************************************/ +F_Pool::~F_Pool() +{ + poolFree(); +} + +/************************************************************************ +Desc: Initialize a smart pool memory structure. A smart pool is one that + will adjust it's block allocation size based on statistics it + gathers within the POOL_STATS structure. For each pool that user + wants to use smart memory management a global POOL_STATS structure + should be declared. The POOL_STATS structure is used to track the + total bytes allocated and determine what the correct pool block + size should be. +*************************************************************************/ +void F_Pool::smartPoolInit( + POOL_STATS * pPoolStats) +{ + m_pPoolStats = pPoolStats; + if (m_pPoolStats && m_pPoolStats->uiCount) + { + setInitialSmartPoolBlkSize(); + } + else + { + m_uiBlockSize = 2048; + } +} + +/**************************************************************************** +Desc: Allocates a block of memory from a memory pool. +Note: If the number of bytes is more than the what is left in the + current block then a new block will be allocated and the lbkl element + of the PMS will be updated. +****************************************************************************/ +RCODE FLMAPI F_Pool::poolAlloc( + FLMUINT uiSize, + void ** ppvPtr) +{ + RCODE rc = NE_FLM_OK; + MBLK * pBlock = m_pLastBlock; + MBLK * pOldLastBlock = pBlock; + FLMBYTE * pucFreePtr; + FLMUINT uiBlockSize; + + // Adjust the size to a machine word boundary + // NOTE: ORed and ANDed 0x800.. & 0x7FFF to prevent partial + // stalls on Netware + + if (uiSize & (FLM_ALLOC_ALIGN | 0x80000000)) + { + uiSize = ((uiSize + FLM_ALLOC_ALIGN) & (~(FLM_ALLOC_ALIGN) & 0x7FFFFFFF)); + } + + // Check if room in block + + if (!pBlock || uiSize > pBlock->uiFreeSize) + { + + // Check if previous block has space for allocation + + if (pBlock && + pBlock->pPrevBlock && + uiSize <= pBlock->pPrevBlock->uiFreeSize) + { + pBlock = pBlock->pPrevBlock; + goto Exit; + } + + // Not enough memory in block - allocate new block + + // Determine the block size: + // 1) start with max of last block size, initial pool size, or alloc size + // 2) if this is an extra block alloc then increase the size by 1/2 + // 3) adjust size to include block header + + uiBlockSize = (pBlock) ? pBlock->uiBlockSize : m_uiBlockSize; + uiBlockSize = f_max( uiSize, uiBlockSize); + + if (pBlock && + uiBlockSize == pBlock->uiBlockSize && + uiBlockSize <= 32769) + { + uiBlockSize += uiBlockSize / 2; + } + + // Add in extra bytes for block overhead + + uiBlockSize += sizeof( MBLK); + + if (RC_BAD( rc = f_alloc( uiBlockSize, &pBlock))) + { + goto Exit; + } + + // Initialize the block elements + + pBlock->uiBlockSize = uiBlockSize; + pBlock->uiFreeOffset = sizeof( MBLK); + pBlock->uiFreeSize = uiBlockSize - sizeof( MBLK); + + // Link in newly allocated block + + m_pLastBlock = pBlock; + pBlock->pPrevBlock = pOldLastBlock; + } + +Exit: + + if (RC_OK( rc)) + { + pucFreePtr = (FLMBYTE *)pBlock; + pucFreePtr += pBlock->uiFreeOffset; + pBlock->uiFreeOffset += uiSize; + pBlock->uiFreeSize -= uiSize; + + m_uiBytesAllocated += uiSize; + *ppvPtr = (void *)pucFreePtr; + } + else + { + *ppvPtr = NULL; + } + return( rc); +} + +/**************************************************************************** +Desc: Allocates a block of memory from a memory pool. +****************************************************************************/ +RCODE FLMAPI F_Pool::poolCalloc( + FLMUINT uiSize, + void ** ppvPtr) +{ + RCODE rc; + + if (RC_OK( rc = poolAlloc( uiSize, ppvPtr))) + { + f_memset( *ppvPtr, 0, uiSize); + } + return( rc); +} + +/**************************************************************************** +Desc : Releases all memory allocated to a pool. +Note : All memory allocated to the pool is returned to the operating system. +*****************************************************************************/ +void FLMAPI F_Pool::poolFree( void) +{ + MBLK * pBlock = m_pLastBlock; + MBLK * pPrevBlock; + + // Free all blocks in chain + + while (pBlock) + { + pPrevBlock = pBlock->pPrevBlock; + f_free( &pBlock); + pBlock = pPrevBlock; + } + + m_pLastBlock = NULL; + + // For Smart pools, update pool statictics + + if (m_pPoolStats) + { + updateSmartPoolStats(); + } +} + +/**************************************************************************** +Desc: Resets memory blocks allocated to a pool. +Note: Will reset the free space in the first memory block, and if + any extra blocks exist they will be freed (destroyed). +*****************************************************************************/ +void FLMAPI F_Pool::poolReset( + void * pvMark, + FLMBOOL bReduceFirstBlock) +{ + MBLK * pBlock = m_pLastBlock; + MBLK * pPrevBlock; + + if (!pBlock) + { + return; + } + + // For Smart Pools update pool statictics + + if (m_pPoolStats) + { + updateSmartPoolStats(); + } + + if (pvMark) + { + freeToMark( pvMark); + return; + } + + // Free all blocks except last one in chain -- which is really + // the first block allocated. This will help us keep memory from + // getting fragmented. + + while (pBlock->pPrevBlock) + { + pPrevBlock = pBlock->pPrevBlock; + f_free( &pBlock); + pBlock = pPrevBlock; + } + + if (pBlock->uiBlockSize - sizeof(MBLK) > m_uiBlockSize && bReduceFirstBlock) + { + // The first block was not the default size, so free it + + f_free( &pBlock); + m_pLastBlock = NULL; + } + else + { + // Reset the allocation pointers in the first block + + pBlock->uiFreeOffset = sizeof( MBLK); + pBlock->uiFreeSize = pBlock->uiBlockSize - sizeof( MBLK); + m_pLastBlock = pBlock; + } + + // On smart pools adjust the initial block size on pool resets + + if (m_pPoolStats) + { + setInitialSmartPoolBlkSize(); + } +} + +/**************************************************************************** +Desc: Frees memory until the pvMark is found. +****************************************************************************/ +void F_Pool::freeToMark( + void * pvMark) +{ + MBLK * pBlock = m_pLastBlock; + MBLK * pPrevBlock; + + // Initialize pool to no blocks + + m_pLastBlock = NULL; + while (pBlock) + { + pPrevBlock = pBlock->pPrevBlock; + + // Check for mark point + + if (PTR_IN_MBLK( pvMark, pBlock, pBlock->uiBlockSize)) + { + FLMUINT uiOldFreeOffset = pBlock->uiFreeOffset; + + // Reset uiFreeOffset and uiFreeSize variables + + pBlock->uiFreeOffset = (FLMUINT)((FLMBYTE *)pvMark - + (FLMBYTE *)pBlock); + pBlock->uiFreeSize = pBlock->uiBlockSize - pBlock->uiFreeOffset; + + // For Smart Pools deduct the bytes allocated since pool mark + + if (m_pPoolStats) + { + flmAssert( uiOldFreeOffset >= pBlock->uiFreeOffset); + m_uiBytesAllocated -= (uiOldFreeOffset - pBlock->uiFreeOffset); + } + + break; + } + + if (m_pPoolStats) + { + m_uiBytesAllocated -= (pBlock->uiFreeOffset - sizeof( MBLK)); + } + + f_free( &pBlock); + pBlock = pPrevBlock; + } + + if (pBlock) + { + m_pLastBlock = pBlock; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_SlabManager::F_SlabManager() +{ + m_hMutex = F_MUTEX_NULL; + m_uiTotalBytesAllocated = 0; + m_pFirstInSlabList = NULL; + m_pLastInSlabList = NULL; + m_uiTotalSlabs = 0; + m_uiAvailSlabs = 0; + m_uiInUseSlabs = 0; + m_uiPreallocSlabs = 0; +#ifdef FLM_SOLARIS + m_DevZero = -1; +#endif +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_SlabManager::~F_SlabManager() +{ + + flmAssert( !m_uiInUseSlabs); + flmAssert( m_uiAvailSlabs == m_uiTotalSlabs); + + freeAllSlabs(); + + flmAssert( !m_uiTotalBytesAllocated); + + if( m_hMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hMutex); + } + +#ifdef FLM_SOLARIS + if( m_DevZero > 0) + { + close( m_DevZero); + } +#endif +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FLMAPI F_SlabManager::setup( + FLMUINT uiPreallocSize) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiSysSlabSize = 0; + FLMUINT uiSlabSize = 64 * 1024; + + if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) + { + goto Exit; + } + + // Determine the slab size + +#ifdef FLM_WIN + { + SYSTEM_INFO sysInfo; + + GetSystemInfo( &sysInfo); + uiSysSlabSize = sysInfo.dwAllocationGranularity; + } +#endif + +#ifdef FLM_SOLARIS + if( (m_DevZero = open( "/dev/zero", O_RDWR)) == -1) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } +#endif + + if( !uiSysSlabSize) + { + uiSysSlabSize = uiSlabSize; + } + + // Round the given slab size up to the closest operating + // system slab size so we don't waste any memory. + + if( uiSlabSize % uiSysSlabSize) + { + m_uiSlabSize = ((uiSlabSize / uiSysSlabSize) + 1) * uiSysSlabSize; + } + else + { + m_uiSlabSize = uiSlabSize; + } + + // Pre-allocate the requested amount of memory from the system + + if( uiPreallocSize) + { + lockMutex(); + + if( RC_BAD( rc = resize( uiPreallocSize, NULL, TRUE))) + { + goto Exit; + } + + unlockMutex(); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FLMAPI F_SlabManager::resize( + FLMUINT uiNumBytes, + FLMUINT * puiActualSize, + FLMBOOL bMutexLocked) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiSlabsNeeded; + void * pSlab; + FLMBOOL bUnlockMutex = FALSE; + + if( !bMutexLocked) + { + lockMutex(); + bUnlockMutex = TRUE; + } + + if( puiActualSize) + { + *puiActualSize = 0; + } + + uiSlabsNeeded = (uiNumBytes / m_uiSlabSize) + + ((uiNumBytes % m_uiSlabSize) ? 1 : 0); + + if( !uiSlabsNeeded && !m_uiInUseSlabs) + { + freeAllSlabs(); + } + else if( m_uiTotalSlabs > uiSlabsNeeded) + { + // Do the best we can to free slabs. We can only get rid of + // slabs that aren't in use. + + if( RC_BAD( rc = sortSlabList())) + { + goto Exit; + } + + while( m_pLastInSlabList && m_uiTotalSlabs > uiSlabsNeeded) + { + pSlab = m_pLastInSlabList; + if( (m_pLastInSlabList = ((SLABHEADER *)pSlab)->pPrev) != NULL) + { + ((SLABHEADER *)m_pLastInSlabList)->pNext = NULL; + } + else + { + m_pFirstInSlabList = NULL; + } + + releaseSlabToSystem( pSlab); + + flmAssert( m_uiTotalSlabs); + flmAssert( m_uiInUseSlabs); + + m_uiAvailSlabs--; + m_uiTotalSlabs--; + } + } + else + { + // Allocate the required number of slabs + + while( m_uiTotalSlabs < uiSlabsNeeded) + { + if( (pSlab = allocSlabFromSystem()) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + // Touch every byte in the slab so that the operating system is + // forced to immediately assign physical memory. + + f_memset( pSlab, 0, m_uiSlabSize); + + // Link the slab into the avail list + + if( m_pFirstInSlabList) + { + ((SLABHEADER *)m_pFirstInSlabList)->pPrev = pSlab; + } + + ((SLABHEADER *)pSlab)->pNext = m_pFirstInSlabList; + m_pFirstInSlabList = pSlab; + + if( !m_pLastInSlabList) + { + m_pLastInSlabList = pSlab; + } + + m_uiTotalSlabs++; + m_uiAvailSlabs++; + } + } + + if( puiActualSize) + { + *puiActualSize = m_uiTotalSlabs * m_uiSlabSize; + } + + m_uiPreallocSlabs = m_uiTotalSlabs; + +Exit: + + if( RC_BAD( rc)) + { + freeAllSlabs(); + } + + if( bUnlockMutex) + { + unlockMutex(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FLMAPI F_SlabManager::allocSlab( + void ** ppSlab, + FLMBOOL bMutexLocked) +{ + RCODE rc = NE_FLM_OK; + FLMBOOL bUnlockMutex = FALSE; + + if( !bMutexLocked) + { + lockMutex(); + bUnlockMutex = TRUE; + } + + if( m_pFirstInSlabList) + { + *ppSlab = m_pFirstInSlabList; + if( (m_pFirstInSlabList = + ((SLABHEADER *)m_pFirstInSlabList)->pNext) != NULL) + { + ((SLABHEADER *)m_pFirstInSlabList)->pPrev = NULL; + } + else + { + m_pLastInSlabList = NULL; + } + + ((SLABHEADER *)*ppSlab)->pNext = NULL; + + flmAssert( m_uiAvailSlabs); + m_uiAvailSlabs--; + m_uiInUseSlabs++; + } + else + { + flmAssert( !m_uiAvailSlabs); + + if( (*ppSlab = allocSlabFromSystem()) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + m_uiTotalSlabs++; + m_uiInUseSlabs++; + } + +Exit: + + if( bUnlockMutex) + { + unlockMutex(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void FLMAPI F_SlabManager::freeSlab( + void ** ppSlab, + FLMBOOL bMutexLocked) +{ + FLMBOOL bUnlockMutex = FALSE; + + flmAssert( ppSlab && *ppSlab); + + if( !bMutexLocked) + { + lockMutex(); + bUnlockMutex = TRUE; + } + + if( m_uiTotalSlabs <= m_uiPreallocSlabs) + { + ((SLABHEADER *)*ppSlab)->pPrev = NULL; + if( (((SLABHEADER *)*ppSlab)->pNext = m_pFirstInSlabList) != NULL) + { + ((SLABHEADER *)m_pFirstInSlabList)->pPrev = *ppSlab; + } + else + { + m_pLastInSlabList = *ppSlab; + } + + m_pFirstInSlabList = *ppSlab; + *ppSlab = NULL; + + flmAssert( m_uiInUseSlabs); + m_uiInUseSlabs--; + m_uiAvailSlabs++; + } + else + { + releaseSlabToSystem( *ppSlab); + *ppSlab = NULL; + + flmAssert( m_uiTotalSlabs); + flmAssert( m_uiInUseSlabs); + + m_uiTotalSlabs--; + m_uiInUseSlabs--; + } + + if( bUnlockMutex) + { + unlockMutex(); + } +} + +/**************************************************************************** +Desc: Assumes that the mutex is locked +****************************************************************************/ +void F_SlabManager::freeAllSlabs( void) +{ + void * pNextSlab; + SLABHEADER * pSlabHeader; + + while( m_pFirstInSlabList) + { + pSlabHeader = (SLABHEADER *)m_pFirstInSlabList; + pNextSlab = pSlabHeader->pNext; + releaseSlabToSystem( m_pFirstInSlabList); + m_pFirstInSlabList = pNextSlab; + m_uiTotalSlabs--; + m_uiAvailSlabs--; + } + + flmAssert( !m_uiAvailSlabs); + m_pLastInSlabList = NULL; +} + +/**************************************************************************** +Desc: Assumes that the mutex is locked +****************************************************************************/ +void * F_SlabManager::allocSlabFromSystem( void) +{ + void * pSlab; + +#ifdef FLM_WIN + pSlab = VirtualAlloc( NULL, + (DWORD)m_uiSlabSize, MEM_COMMIT, PAGE_READWRITE); +#elif defined( FLM_SOLARIS) + if( (pSlab = mmap( 0, m_uiSlabSize, + PROT_READ | PROT_WRITE, MAP_PRIVATE, m_DevZero, 0)) == MAP_FAILED) + { + return( NULL); + } +#elif defined( FLM_UNIX) + +#ifndef MAP_ANONYMOUS + #define MAP_ANONYMOUS MAP_ANON +#endif + + if( (pSlab = mmap( 0, m_uiSlabSize, + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) == MAP_FAILED) + { + return( NULL); + } +#else + if( RC_BAD( f_alloc( m_uiSlabSize, &pSlab))) + { + return( NULL); + } +#endif + + incrementTotalBytesAllocated( m_uiSlabSize, TRUE); + + return( pSlab); +} + +/**************************************************************************** +Desc: Assumes that the mutex is locked +****************************************************************************/ +void F_SlabManager::releaseSlabToSystem( + void * pSlab) +{ + flmAssert( pSlab); + +#ifdef FLM_WIN + VirtualFree( pSlab, 0, MEM_RELEASE); +#elif defined( FLM_SOLARIS) + munmap( (char *)pSlab, m_uiSlabSize); +#elif defined( FLM_UNIX) + munmap( pSlab, m_uiSlabSize); +#else + f_free( &pSlab); +#endif + + decrementTotalBytesAllocated( m_uiSlabSize, TRUE); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_MEM_PROTECT +void F_SlabManager::protectSlab( + void * pSlab) +{ +#ifdef FLM_WIN + (void)pSlab; + DWORD dOldProtect; + VirtualProtect( pSlab, m_uiSlabSize, PAGE_READONLY, &dOldProtect); + flmAssert( dOldProtect == PAGE_READWRITE); +#elif defined( FLM_UNIX) + mprotect( pSlab, m_uiSlabSize, PROT_READ); +#endif +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_MEM_PROTECT +void F_SlabManager::unprotectSlab( + void * pSlab) +{ +#ifdef FLM_WIN + (void)pSlab; + DWORD dOldProtect; + VirtualProtect( pSlab, m_uiSlabSize, PAGE_READWRITE, &dOldProtect); + flmAssert( dOldProtect == PAGE_READONLY); +#elif defined( FLM_UNIX) + mprotect( pSlab, m_uiSlabSize, PROT_READ | PROT_WRITE); +#endif +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMINT FLMAPI F_SlabManager::slabAddrCompareFunc( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2) +{ + void * pSlab1 = (((void **)pvBuffer)[ uiPos1]); + void * pSlab2 = (((void **)pvBuffer)[ uiPos2]); + + flmAssert( pSlab1 != pSlab2); + + if( pSlab1 < pSlab2) + { + return( -1); + } + + return( 1); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void FLMAPI F_SlabManager::slabAddrSwapFunc( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2) +{ + void ** ppSlab1 = &(((void **)pvBuffer)[ uiPos1]); + void ** ppSlab2 = &(((void **)pvBuffer)[ uiPos2]); + void * pTmp; + + pTmp = *ppSlab1; + *ppSlab1 = *ppSlab2; + *ppSlab2 = pTmp; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_SlabManager::sortSlabList( void) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiLoop; + void ** pSortBuf = NULL; + FLMUINT uiMaxSortEntries; + FLMUINT uiSortEntries = 0; +#define SMALL_SORT_BUF_SIZE 256 + void * smallSortBuf[ SMALL_SORT_BUF_SIZE]; + void * pCurSlab; + void * pPrevSib; + + if( m_uiAvailSlabs <= 1) + { + goto Exit; + } + + uiMaxSortEntries = m_uiAvailSlabs; + + // Sort the avail list according to the starting memory addresses of the + // slabs + + if( uiMaxSortEntries <= SMALL_SORT_BUF_SIZE) + { + pSortBuf = smallSortBuf; + } + else + { + if( RC_BAD( rc = f_alloc( uiMaxSortEntries * sizeof( void *), &pSortBuf))) + { + goto Exit; + } + } + + pCurSlab = m_pFirstInSlabList; + + while( pCurSlab) + { + flmAssert( uiSortEntries != uiMaxSortEntries); + pSortBuf[ uiSortEntries++] = pCurSlab; + pCurSlab = ((SLABHEADER *)pCurSlab)->pNext; + } + + flmAssert( uiSortEntries == uiMaxSortEntries); + + // Quick sort + + flmAssert( uiSortEntries); + + f_qsort( (FLMBYTE *)pSortBuf, 0, uiSortEntries - 1, + F_SlabManager::slabAddrCompareFunc, + F_SlabManager::slabAddrSwapFunc); + + // Re-link the items in the list according to the new + // sort order + + m_pFirstInSlabList = NULL; + m_pLastInSlabList = NULL; + + pCurSlab = NULL; + pPrevSib = NULL; + + for( uiLoop = 0; uiLoop < uiSortEntries; uiLoop++) + { + pCurSlab = pSortBuf[ uiLoop]; + ((SLABHEADER *)pCurSlab)->pNext = NULL; + ((SLABHEADER *)pCurSlab)->pPrev = NULL; + + if( pPrevSib) + { + ((SLABHEADER *)pCurSlab)->pPrev = pPrevSib; + ((SLABHEADER *)pPrevSib)->pNext = pCurSlab; + } + else + { + m_pFirstInSlabList = pCurSlab; + } + + pPrevSib = pCurSlab; + } + + m_pLastInSlabList = pCurSlab; + +Exit: + + if( pSortBuf && pSortBuf != smallSortBuf) + { + f_free( &pSortBuf); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_FixedAlloc::F_FixedAlloc() +{ + m_pSlabManager = NULL; + m_pFirstSlab = NULL; + m_pLastSlab = NULL; + m_pRelocator = NULL; + m_pFirstSlabWithAvailCells = NULL; + m_pLastSlabWithAvailCells = NULL; + m_uiSlabsWithAvailCells = 0; + m_bAvailListSorted = TRUE; + m_uiTotalFreeCells = 0; + m_uiSlabSize = 0; + m_pUsageStats = NULL; + +#ifdef FLM_MEM_PROTECT + m_bMemProtectionEnabled = FALSE; +#endif +} + +/**************************************************************************** +Desc: Destructor for F_FixedAlloc. checks for memory leaks, and + frees all memory in use. +****************************************************************************/ +F_FixedAlloc::~F_FixedAlloc() +{ +#ifdef FLM_DEBUG + testForLeaks(); +#endif + + freeAll(); + + if( m_pSlabManager) + { + m_pSlabManager->Release(); + } +} + +/**************************************************************************** +Desc: Setup method for any setup that can fail +****************************************************************************/ +RCODE F_FixedAlloc::setup( + IF_Relocator * pRelocator, + IF_SlabManager * pSlabManager, + FLMBOOL bMemProtect, + FLMUINT uiCellSize, + FLM_SLAB_USAGE * pUsageStats) +{ + RCODE rc = NE_FLM_OK; + + flmAssert( pSlabManager); + flmAssert( uiCellSize); + flmAssert( pUsageStats != NULL); + + m_pUsageStats = pUsageStats; + m_pSlabManager = pSlabManager; + m_pSlabManager->AddRef(); + m_pRelocator = pRelocator; + m_uiCellSize = uiCellSize; + m_uiSlabSize = m_pSlabManager->getSlabSize(); + + // Get the alloc-aligned versions of all the sizes + + m_uiSlabHeaderSize = getAllocAlignedSize( sizeof( SLAB)); + if (pRelocator) + { + m_uiCellHeaderSize = getAllocAlignedSize( sizeof( CELLHEADER)); + } + else + { + m_uiCellHeaderSize = getAllocAlignedSize( sizeof( CELLHEADER2)); + } + m_uiCellSize = getAllocAlignedSize( m_uiCellSize); + + // Ensure that there's enough space for our overhead + + flmAssert( m_uiCellSize >= sizeof( CELLAVAILNEXT)); + + m_uiSizeOfCellAndHeader = m_uiCellHeaderSize + m_uiCellSize; + + m_uiCellsPerSlab = + (m_uiSlabSize - m_uiSlabHeaderSize) / + m_uiSizeOfCellAndHeader; + + flmAssert( m_uiCellsPerSlab); + flmAssert( m_uiCellsPerSlab <= FLM_MAX_UINT16); + flmAssert( (m_uiCellsPerSlab * m_uiCellSize) < m_uiSlabSize); + +#ifdef FLM_MEM_PROTECT + m_bMemProtectionEnabled = bMemProtect; +#else + F_UNREFERENCED_PARM( bMemProtect); +#endif + + return( rc); +} + +/**************************************************************************** +Desc: Private, internal method to fetch a cell +****************************************************************************/ +void * F_FixedAlloc::getCell( + IF_Relocator * pRelocator) +{ + SLAB * pSlab = NULL; + FLMBYTE * pCell = NULL; + CELLHEADER * pHeader; + + // If there's a slab that has an avail cell, that one gets priority + + if( (pSlab = m_pFirstSlabWithAvailCells) != NULL) + { +#ifdef FLM_MEM_PROTECT + unprotectSlab( pSlab, TRUE); +#endif + + flmAssert( pSlab->ui16AvailCellCount <= m_uiTotalFreeCells); + flmAssert( m_uiTotalFreeCells); + flmAssert( pSlab->ui16AllocatedCells < m_uiCellsPerSlab); + + pCell = m_pFirstSlabWithAvailCells->pLocalAvailCellListHead; + flmAssert( pCell); + + pHeader = (CELLHEADER *)((FLMBYTE *)pCell - m_uiCellHeaderSize); + pSlab->ui16AllocatedCells++; + pSlab->ui16AvailCellCount--; + m_uiTotalFreeCells--; + + // An avail cell holds as its contents the next pointer in the avail chain. + // Avail chains do not span slabs. + + pSlab->pLocalAvailCellListHead = ((CELLAVAILNEXT *)pCell)->pNextInList; + + // If there are no other avail cells in this slab at this point, + // then we need to unlink the slab from the + // slabs-with-avail-cells list, headed by m_pFirstSlabWithAvailCells + + if( !pSlab->pLocalAvailCellListHead) + { + // Save a copy of the slab we're going to unlink + + SLAB * pSlabToUnlink = pSlab; + + // Need to keep the NULLNESS of the content of the cell consistent + // with the slab's ui16AvailCellCount being equal to 0 + + flmAssert( !pSlabToUnlink->ui16AvailCellCount); + + // There can't be a pPrevSlabWithAvailCells since + // we're positioned to the first one + + flmAssert( !pSlabToUnlink->pPrevSlabWithAvailCells); + + // Update m_pFirstSlabWithAvailCells to point to the next one + + if( (m_pFirstSlabWithAvailCells = + pSlabToUnlink->pNextSlabWithAvailCells) == NULL) + { + flmAssert( m_pLastSlabWithAvailCells == pSlabToUnlink); + m_pLastSlabWithAvailCells = NULL; + } + + // Unlink from slabs-with-avail-cells list + + if( pSlabToUnlink->pNextSlabWithAvailCells) + { +#ifdef FLM_MEM_PROTECT + unprotectSlab( pSlabToUnlink->pNextSlabWithAvailCells, TRUE); +#endif + + pSlabToUnlink-> + pNextSlabWithAvailCells->pPrevSlabWithAvailCells = + pSlabToUnlink->pPrevSlabWithAvailCells; + +#ifdef FLM_MEM_PROTECT + protectSlab( pSlabToUnlink->pNextSlabWithAvailCells, TRUE); +#endif + pSlabToUnlink->pNextSlabWithAvailCells = NULL; + } + + // Decrement the slab count + + flmAssert( m_uiSlabsWithAvailCells); + m_uiSlabsWithAvailCells--; + } + } + else + { + // If our m_pFirstSlab is completely full, or there is no + // m_pFirstSlab, it is time to allocate a new slab + + if( !m_pFirstSlab || + (m_pFirstSlab->ui16NextNeverUsedCell == m_uiCellsPerSlab)) + { + SLAB * pNewSlab; + + if( (pNewSlab = getAnotherSlab()) == NULL) + { + goto Exit; + } + + if( m_pFirstSlab) + { +#ifdef FLM_MEM_PROTECT + unprotectSlab( pNewSlab, TRUE); +#endif + pNewSlab->pNext = m_pFirstSlab; +#ifdef FLM_MEM_PROTECT + protectSlab( pNewSlab, TRUE); +#endif + +#ifdef FLM_MEM_PROTECT + unprotectSlab( m_pFirstSlab, TRUE); +#endif + m_pFirstSlab->pPrev = pNewSlab; +#ifdef FLM_MEM_PROTECT + protectSlab( m_pFirstSlab, TRUE); +#endif + } + else + { + m_pLastSlab = pNewSlab; + } + + m_pFirstSlab = pNewSlab; + } + + pSlab = m_pFirstSlab; + +#ifdef FLM_MEM_PROTECT + unprotectSlab( pSlab, TRUE); +#endif + pSlab->ui16AllocatedCells++; + +#ifdef FLM_MEM_PROTECT + flmAssert( pSlab->ui16AllocatedCells <= m_uiCellsPerSlab); +#endif + + pHeader = (CELLHEADER *) + ((FLMBYTE *)pSlab + m_uiSlabHeaderSize + + (m_uiSizeOfCellAndHeader * m_pFirstSlab->ui16NextNeverUsedCell)); + + pCell = ((FLMBYTE *)pHeader + m_uiCellHeaderSize); + m_pFirstSlab->ui16NextNeverUsedCell++; + } + + pHeader->pContainingSlab = pSlab; + +#ifdef FLM_DEBUG + if (gv_bTrackLeaks && gv_bStackWalk) + { + pHeader->puiStack = memWalkStack(); + } + else + { + pHeader->puiStack = NULL; + } +#endif + if (!m_pRelocator) + { + ((CELLHEADER2 *)pHeader)->pRelocator = pRelocator; + } + +#ifdef FLM_MEM_PROTECT + protectSlab( pSlab, TRUE); +#endif + + m_pUsageStats->ui64AllocatedCells++; + +Exit: + + return( pCell); +} + +/**************************************************************************** +Desc: Public method to free a cell of memory back to the system. +****************************************************************************/ +void F_FixedAlloc::freeCell( + void * pCell, + FLMBOOL bMutexLocked, + FLMBOOL bFreeIfEmpty, + FLMBOOL * pbFreedSlab) +{ + CELLAVAILNEXT * pCellContents; + CELLHEADER * pHeader; + SLAB * pSlab; + FLMBOOL bUnlockMutex = FALSE; +#ifdef FLM_MEM_PROTECT + FLMBOOL bProtectSlab = FALSE; +#endif + + if( pbFreedSlab) + { + *pbFreedSlab = FALSE; + } + + if( !pCell) + { + return; + } + + if( !bMutexLocked) + { + m_pSlabManager->lockMutex(); + bUnlockMutex = TRUE; + } + + pCellContents = (CELLAVAILNEXT *)pCell; + pHeader = (CELLHEADER *)(((FLMBYTE *)pCell) - m_uiCellHeaderSize); + pSlab = pHeader->pContainingSlab; + + // Memory corruption detected! + + if( !pSlab || pSlab->pvAllocator != (void *)this) + { + flmAssert( 0); + goto Exit; + } + +#ifdef FLM_MEM_PROTECT + unprotectSlab( pSlab, TRUE); + bProtectSlab = TRUE; +#endif + + pHeader->pContainingSlab = NULL; +#ifdef FLM_DEBUG + if( pHeader->puiStack) + { + free( pHeader->puiStack); + pHeader->puiStack = NULL; + } +#endif + + // Should always be set on a free + + flmAssert( m_pFirstSlab); + + // Add the cell to the pSlab's free list + + pCellContents->pNextInList = pSlab->pLocalAvailCellListHead; + +#ifdef FLM_DEBUG + // Write out a string that's easy to see in memory when debugging + + f_strcpy( (char *)pCellContents->szDebugPattern, "FREECELL"); +#endif + + flmAssert( pCell); + pSlab->pLocalAvailCellListHead = (FLMBYTE *)pCell; + pSlab->ui16AvailCellCount++; + + flmAssert( pSlab->ui16AllocatedCells); + pSlab->ui16AllocatedCells--; + + // If there's no chain, make this one the first + + if( !m_pFirstSlabWithAvailCells) + { + m_pFirstSlabWithAvailCells = pSlab; + m_pLastSlabWithAvailCells = pSlab; + flmAssert( !pSlab->pNextSlabWithAvailCells); + flmAssert( !pSlab->pPrevSlabWithAvailCells); + m_uiSlabsWithAvailCells++; + m_bAvailListSorted = TRUE; + } + else if( pSlab->ui16AvailCellCount == 1) + { + // This item is not linked in to the chain, so link it in + + if( m_bAvailListSorted && pSlab > m_pFirstSlabWithAvailCells) + { + m_bAvailListSorted = FALSE; + } + + pSlab->pNextSlabWithAvailCells = m_pFirstSlabWithAvailCells; + pSlab->pPrevSlabWithAvailCells = NULL; + +#ifdef FLM_MEM_PROTECT + unprotectSlab( m_pFirstSlabWithAvailCells, TRUE); +#endif + m_pFirstSlabWithAvailCells->pPrevSlabWithAvailCells = pSlab; +#ifdef FLM_MEM_PROTECT + protectSlab( m_pFirstSlabWithAvailCells, TRUE); +#endif + m_pFirstSlabWithAvailCells = pSlab; + m_uiSlabsWithAvailCells++; + } + + // Adjust counter, because the cell is now considered free + + m_uiTotalFreeCells++; + + // If this slab is now totally avail + + if( pSlab->ui16AvailCellCount == m_uiCellsPerSlab) + { + flmAssert( !pSlab->ui16AllocatedCells); + + // If we have met our threshold for being able to free a slab + + if( m_uiTotalFreeCells >= m_uiCellsPerSlab || bFreeIfEmpty) + { +#ifdef FLM_MEM_PROTECT + protectSlab( pSlab, TRUE); + bProtectSlab = FALSE; +#endif + + freeSlab( pSlab); + + if( pbFreedSlab) + { + *pbFreedSlab = TRUE; + } + } + else if( pSlab != m_pFirstSlabWithAvailCells) + { + // Link the slab to the front of the avail list so that + // it can be freed quickly at some point in the future + + if( pSlab->pPrevSlabWithAvailCells) + { + pSlab->pPrevSlabWithAvailCells->pNextSlabWithAvailCells = + pSlab->pNextSlabWithAvailCells; + } + + if( pSlab->pNextSlabWithAvailCells) + { + pSlab->pNextSlabWithAvailCells->pPrevSlabWithAvailCells = + pSlab->pPrevSlabWithAvailCells; + } + else + { + flmAssert( m_pLastSlabWithAvailCells == pSlab); + m_pLastSlabWithAvailCells = pSlab->pPrevSlabWithAvailCells; + } + + if( m_pFirstSlabWithAvailCells) + { + m_pFirstSlabWithAvailCells->pPrevSlabWithAvailCells = pSlab; + } + + pSlab->pPrevSlabWithAvailCells = NULL; + pSlab->pNextSlabWithAvailCells = m_pFirstSlabWithAvailCells; + m_pFirstSlabWithAvailCells = pSlab; + } + } + + m_pUsageStats->ui64AllocatedCells--; + +Exit: + +#ifdef FLM_MEM_PROTECT + if( bProtectSlab) + { + protectSlab( pSlab, TRUE); + } +#endif + + if( bUnlockMutex) + { + m_pSlabManager->unlockMutex(); + } + + return; +} + +/**************************************************************************** +Desc: Grabs another slab of memory from the operating system +****************************************************************************/ +F_FixedAlloc::SLAB * F_FixedAlloc::getAnotherSlab( void) +{ + SLAB * pSlab = NULL; + + if( RC_BAD( m_pSlabManager->allocSlab( (void **)&pSlab, TRUE))) + { + goto Exit; + } + + // Initialize the slab header fields + + f_memset( pSlab, 0, sizeof( SLAB)); + pSlab->pvAllocator = (void *)this; + m_pUsageStats->ui64Slabs++; + +#ifdef FLM_MEM_PROTECT + if( m_bMemProtectionEnabled) + { + m_pSlabManager->protectSlab( pSlab); + } +#endif + +Exit: + + return( pSlab); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_MEM_PROTECT +void F_FixedAlloc::protectSlab( + SLAB * pSlab, + FLMBOOL bMutexLocked) +{ + FLMBOOL bUnlockMutex = FALSE; + + if( !m_bMemProtectionEnabled) + { + return; + } + + if( !bMutexLocked) + { + m_pSlabManager->lockMutex(); + bUnlockMutex = TRUE; + } + + flmAssert( pSlab->pvAllocator == this); + flmAssert( pSlab->ui16UnprotectCount); + + pSlab->ui16UnprotectCount--; + + if( !pSlab->ui16UnprotectCount) + { + m_pSlabManager->protectSlab( pSlab); + } + + if( bUnlockMutex) + { + m_pSlabManager->unlockMutex(); + } +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_MEM_PROTECT +void F_FixedAlloc::unprotectSlab( + SLAB * pSlab, + FLMBOOL bMutexLocked) +{ + FLMBOOL bUnlockMutex = FALSE; + + if( !m_bMemProtectionEnabled) + { + return; + } + + flmAssert( pSlab->pvAllocator == this); + + if( !bMutexLocked) + { + m_pSlabManager->lockMutex(); + bUnlockMutex = TRUE; + } + + if( !pSlab->ui16UnprotectCount) + { + m_pSlabManager->unprotectSlab( pSlab); + } + + pSlab->ui16UnprotectCount++; + + if( bUnlockMutex) + { + m_pSlabManager->unlockMutex(); + } +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_MEM_PROTECT +void F_FixedAlloc::protectCell( + void * pvCell) +{ + CELLHEADER * pCellHeader; + + m_pSlabManager->lockMutex(); + pCellHeader = (CELLHEADER *)((FLMBYTE *)pvCell - m_uiCellHeaderSize); + protectSlab( pCellHeader->pContainingSlab, TRUE); + m_pSlabManager->unlockMutex(); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_MEM_PROTECT +void F_FixedAlloc::unprotectCell( + void * pvCell) +{ + CELLHEADER * pCellHeader; + + m_pSlabManager->lockMutex(); + pCellHeader = (CELLHEADER *)((FLMBYTE *)pvCell - m_uiCellHeaderSize); + unprotectSlab( pCellHeader->pContainingSlab, TRUE); + m_pSlabManager->unlockMutex(); +} +#endif + +/**************************************************************************** +Desc: Private internal method to free an unused empty slab back to the OS. +****************************************************************************/ +void F_FixedAlloc::freeSlab( + SLAB * pSlab) +{ +#ifdef FLM_DEBUG + CELLAVAILNEXT * pAvailNext = NULL; + FLMUINT32 ui32AvailCount = 0; +#endif + + flmAssert( pSlab); +#ifdef FLM_MEM_PROTECT + flmAssert( !pSlab->ui16UnprotectCount); +#endif + + // Memory corruption detected! + + if( pSlab->ui16AllocatedCells || pSlab->pvAllocator != this) + { + flmAssert( 0); + return; + } + +#ifdef FLM_DEBUG + // Walk the avail chain as a sanity check + + pAvailNext = (CELLAVAILNEXT *)pSlab->pLocalAvailCellListHead; + while( pAvailNext) + { + ui32AvailCount++; + pAvailNext = (CELLAVAILNEXT *)pAvailNext->pNextInList; + } + + flmAssert( pSlab->ui16AvailCellCount == ui32AvailCount); + flmAssert( pSlab->ui16NextNeverUsedCell >= ui32AvailCount); +#endif + + // Unlink from all-slabs-list + + if( pSlab->pNext) + { +#ifdef FLM_MEM_PROTECT + unprotectSlab( pSlab->pNext, TRUE); +#endif + pSlab->pNext->pPrev = pSlab->pPrev; +#ifdef FLM_MEM_PROTECT + protectSlab( pSlab->pNext, TRUE); +#endif + } + else + { + m_pLastSlab = pSlab->pPrev; + } + + if( pSlab->pPrev) + { +#ifdef FLM_MEM_PROTECT + unprotectSlab( pSlab->pPrev, TRUE); +#endif + pSlab->pPrev->pNext = pSlab->pNext; +#ifdef FLM_MEM_PROTECT + protectSlab( pSlab->pPrev, TRUE); +#endif + } + else + { + m_pFirstSlab = pSlab->pNext; + } + + // Unlink from slabs-with-avail-cells list + + if( pSlab->pNextSlabWithAvailCells) + { +#ifdef FLM_MEM_PROTECT + unprotectSlab( pSlab->pNextSlabWithAvailCells, TRUE); +#endif + pSlab->pNextSlabWithAvailCells->pPrevSlabWithAvailCells = + pSlab->pPrevSlabWithAvailCells; +#ifdef FLM_MEM_PROTECT + protectSlab( pSlab->pNextSlabWithAvailCells, TRUE); +#endif + } + else + { + m_pLastSlabWithAvailCells = pSlab->pPrevSlabWithAvailCells; + } + + if( pSlab->pPrevSlabWithAvailCells) + { +#ifdef FLM_MEM_PROTECT + unprotectSlab( pSlab->pPrevSlabWithAvailCells, TRUE); +#endif + pSlab->pPrevSlabWithAvailCells->pNextSlabWithAvailCells = + pSlab->pNextSlabWithAvailCells; +#ifdef FLM_MEM_PROTECT + protectSlab( pSlab->pPrevSlabWithAvailCells, TRUE); +#endif + } + else + { + m_pFirstSlabWithAvailCells = pSlab->pNextSlabWithAvailCells; + } + + flmAssert( m_uiSlabsWithAvailCells); + m_uiSlabsWithAvailCells--; + flmAssert( m_uiTotalFreeCells >= pSlab->ui16AvailCellCount); + m_uiTotalFreeCells -= pSlab->ui16AvailCellCount; + m_pUsageStats->ui64Slabs--; + +#ifdef FLM_MEM_PROTECT + unprotectSlab( pSlab, TRUE); +#endif + m_pSlabManager->freeSlab( (void **)&pSlab, TRUE); +} + +/**************************************************************************** +Desc: Public method to free all the memory in the system. +****************************************************************************/ +void F_FixedAlloc::freeAll( void) +{ + SLAB * pFreeMe; + + m_pSlabManager->lockMutex(); + + while( m_pFirstSlab) + { + pFreeMe = m_pFirstSlab; + m_pFirstSlab = m_pFirstSlab->pNext; + freeSlab( pFreeMe); + } + + flmAssert( !m_uiTotalFreeCells); + + m_pFirstSlab = NULL; + m_pLastSlab = NULL; + m_pFirstSlabWithAvailCells = NULL; + m_pLastSlabWithAvailCells = NULL; + m_uiSlabsWithAvailCells = 0; + m_bAvailListSorted = TRUE; + m_uiTotalFreeCells = 0; + f_memset( m_pUsageStats, 0, sizeof( FLM_SLAB_USAGE)); + + m_pSlabManager->unlockMutex(); +} + +/**************************************************************************** +Desc: If a relocation callback function has been registered, and memory + can be compressed, the avail list will be compressed +****************************************************************************/ +void F_FixedAlloc::defragmentMemory( void) +{ + SLAB * pCurSlab; + SLAB * pPrevSib; + CELLHEADER * pCellHeader; + FLMBOOL bSlabFreed; + FLMBYTE * pucOriginal; + FLMBYTE * pucReloc = NULL; + FLMUINT uiLoop; + SLAB ** pSortBuf = NULL; + FLMUINT uiMaxSortEntries; + FLMUINT uiSortEntries = 0; +#define SMALL_SORT_BUF_SIZE 256 + SLAB * smallSortBuf[ SMALL_SORT_BUF_SIZE]; + + m_pSlabManager->lockMutex(); + + if( m_uiTotalFreeCells < m_uiCellsPerSlab) + { + goto Exit; + } + + uiMaxSortEntries = m_uiSlabsWithAvailCells; + + // Re-sort the slabs in the avail list according to + // their memory addresses to help reduce logical fragmentation + + if( !m_bAvailListSorted && uiMaxSortEntries > 1) + { + if( uiMaxSortEntries <= SMALL_SORT_BUF_SIZE) + { + pSortBuf = smallSortBuf; + } + else + { + if( RC_BAD( f_alloc( uiMaxSortEntries * sizeof( SLAB *), &pSortBuf))) + { + goto Exit; + } + } + + pCurSlab = m_pFirstSlabWithAvailCells; + + while( pCurSlab) + { + flmAssert( uiSortEntries != uiMaxSortEntries); + pSortBuf[ uiSortEntries++] = pCurSlab; + pCurSlab = pCurSlab->pNextSlabWithAvailCells; + } + + // Quick sort + + flmAssert( uiSortEntries); + + f_qsort( (FLMBYTE *)pSortBuf, 0, uiSortEntries - 1, + F_FixedAlloc::slabAddrCompareFunc, + F_FixedAlloc::slabAddrSwapFunc); + + // Re-link the items in the list according to the new + // sort order + + m_pFirstSlabWithAvailCells = NULL; + m_pLastSlabWithAvailCells = NULL; + + pCurSlab = NULL; + pPrevSib = NULL; + + for( uiLoop = 0; uiLoop < uiSortEntries; uiLoop++) + { + pCurSlab = pSortBuf[ uiLoop]; +#ifdef FLM_MEM_PROTECT + unprotectSlab( pCurSlab, TRUE); +#endif + + pCurSlab->pNextSlabWithAvailCells = NULL; + pCurSlab->pPrevSlabWithAvailCells = NULL; + + if( pPrevSib) + { + pCurSlab->pPrevSlabWithAvailCells = pPrevSib; +#ifdef FLM_MEM_PROTECT + unprotectSlab( pPrevSib, TRUE); +#endif + pPrevSib->pNextSlabWithAvailCells = pCurSlab; +#ifdef FLM_MEM_PROTECT + protectSlab( pPrevSib, TRUE); +#endif + } + else + { + m_pFirstSlabWithAvailCells = pCurSlab; + } + +#ifdef FLM_MEM_PROTECT + protectSlab( pCurSlab, TRUE); +#endif + pPrevSib = pCurSlab; + } + + m_pLastSlabWithAvailCells = pCurSlab; + m_bAvailListSorted = TRUE; + } + + // Process the avail list (which should be sorted unless + // we are too low on memory) + + pCurSlab = m_pLastSlabWithAvailCells; + + while( pCurSlab) + { + if( m_uiTotalFreeCells < m_uiCellsPerSlab) + { + // No need to continue ... we aren't above the + // free cell threshold + + goto Exit; + } + + pPrevSib = pCurSlab->pPrevSlabWithAvailCells; + + if( pCurSlab == m_pFirstSlabWithAvailCells || + !pCurSlab->ui16AvailCellCount) + { + // We've either hit the beginning of the avail list or + // the slab that we are now positioned on has been + // removed from the avail list. In either case, + // we are done. + + break; + } + + if( pCurSlab->ui16AvailCellCount == m_uiCellsPerSlab || + pCurSlab->ui16NextNeverUsedCell == pCurSlab->ui16AvailCellCount) + { + freeSlab( pCurSlab); + pCurSlab = pPrevSib; + continue; + } + + for( uiLoop = 0; uiLoop < pCurSlab->ui16NextNeverUsedCell && + pCurSlab != m_pFirstSlabWithAvailCells && + m_uiTotalFreeCells >= m_uiCellsPerSlab; uiLoop++) + { + IF_Relocator * pRelocator; + + pCellHeader = (CELLHEADER *) + ((FLMBYTE *)pCurSlab + m_uiSlabHeaderSize + + (uiLoop * m_uiSizeOfCellAndHeader)); + if ((pRelocator = m_pRelocator) == NULL) + { + pRelocator = ((CELLHEADER2 *)pCellHeader)->pRelocator; + } + + if( pCellHeader->pContainingSlab) + { + + // If pContainingSlab is non-NULL, the cell is currently allocated + + flmAssert( pCellHeader->pContainingSlab == pCurSlab); + + pucOriginal = ((FLMBYTE *)pCellHeader + m_uiCellHeaderSize); + + if( pRelocator->canRelocate( pucOriginal)) + { + if( (pucReloc = (FLMBYTE *)getCell( pRelocator)) == NULL) + { + goto Exit; + } + +#ifdef FLM_MEM_PROTECT + unprotectSlab( ((CELLHEADER *)(pucReloc - + m_uiCellHeaderSize))->pContainingSlab, TRUE); +#endif + + f_memcpy( pucReloc, pucOriginal, m_uiCellSize); + pRelocator->relocate( pucOriginal, pucReloc); + +#ifdef FLM_MEM_PROTECT + protectSlab( ((CELLHEADER *)(pucReloc - + m_uiCellHeaderSize))->pContainingSlab, TRUE); +#endif + + freeCell( pucOriginal, TRUE, TRUE, &bSlabFreed); + + if( bSlabFreed) + { + break; + } + } + } + } + + pCurSlab = pPrevSib; + } + +Exit: + + m_pSlabManager->unlockMutex(); + + if( pSortBuf && pSortBuf != smallSortBuf) + { + f_free( &pSortBuf); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_FixedAlloc::freeUnused( void) +{ + SLAB * pSlab; + + m_pSlabManager->lockMutex(); + + if( (pSlab = m_pFirstSlabWithAvailCells) != NULL && + !pSlab->ui16AllocatedCells) + { + freeSlab( pSlab); + } + + if( (pSlab = m_pFirstSlab) != NULL && + !pSlab->ui16AllocatedCells) + { + freeSlab( pSlab); + } + + m_pSlabManager->unlockMutex(); +} + +/**************************************************************************** +Desc: Debug method to do mem leak testing. Any cells allocated via + allocCell but not freed via freeCell() will be triggered here. +****************************************************************************/ +#ifdef FLM_DEBUG +void F_FixedAlloc::testForLeaks( void) +{ + SLAB * pSlabRover = m_pFirstSlab; + CELLHEADER * pHeader; + FLMUINT uiLoop; + F_MEM_HDR memHeader; + + // Test for leaks + + while( pSlabRover) + { + for( uiLoop = 0; uiLoop < pSlabRover->ui16NextNeverUsedCell; uiLoop++) + { + pHeader = (CELLHEADER *) + ((FLMBYTE *)pSlabRover + m_uiSlabHeaderSize + + (uiLoop * m_uiSizeOfCellAndHeader)); + + // Nonzero here means we have a leak + + if( pHeader->pContainingSlab) + { + // We have a leak, so let's call logMemLeak with the + // appropriate header passed in + + f_memset( &memHeader, 0, sizeof( F_MEM_HDR)); + memHeader.uiDataSize = m_uiCellSize; + memHeader.puiStack = pHeader->puiStack; + logMemLeak( &memHeader); + } + } + + pSlabRover = pSlabRover->pNext; + } +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +F_BufferAlloc::~F_BufferAlloc() +{ + FLMUINT uiLoop; + + for (uiLoop = 0; uiLoop < NUM_BUF_ALLOCATORS; uiLoop++) + { + if( m_ppAllocators[ uiLoop]) + { + m_ppAllocators[ uiLoop]->Release(); + m_ppAllocators[ uiLoop] = NULL; + } + } + + if( m_pSlabManager) + { + m_pSlabManager->Release(); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_BufferAlloc::setup( + IF_SlabManager * pSlabManager, + FLMBOOL bMemProtect, + FLM_SLAB_USAGE * pUsageStats) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiLoop; + FLMUINT uiSize; + + flmAssert( pSlabManager); + m_pSlabManager = pSlabManager; + m_pSlabManager->AddRef(); + + for( uiLoop = 0; uiLoop < NUM_BUF_ALLOCATORS; uiLoop++) + { + if( (m_ppAllocators[ uiLoop] = f_new F_FixedAlloc) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + switch (uiLoop) + { + case 0: + uiSize = CELL_SIZE_0; + break; + case 1: + uiSize = CELL_SIZE_1; + break; + case 2: + uiSize = CELL_SIZE_2; + break; + case 3: + uiSize = CELL_SIZE_3; + break; + case 4: + uiSize = CELL_SIZE_4; + break; + case 5: + uiSize = CELL_SIZE_5; + break; + case 6: + uiSize = CELL_SIZE_6; + break; + case 7: + uiSize = CELL_SIZE_7; + break; + case 8: + uiSize = CELL_SIZE_8; + break; + case 9: + uiSize = CELL_SIZE_9; + break; + case 10: + uiSize = CELL_SIZE_10; + break; + case 11: + uiSize = CELL_SIZE_11; + break; + case 12: + uiSize = CELL_SIZE_12; + break; + case 13: + uiSize = CELL_SIZE_13; + break; + case 14: + uiSize = CELL_SIZE_14; + break; + case 15: + uiSize = CELL_SIZE_15; + break; + case 16: + uiSize = CELL_SIZE_16; + break; + case 17: + uiSize = CELL_SIZE_17; + break; + case 18: + uiSize = CELL_SIZE_18; + break; + case 19: + uiSize = CELL_SIZE_19; + break; + case 20: + uiSize = CELL_SIZE_20; + break; + case 21: + uiSize = CELL_SIZE_21; + break; + default: + uiSize = 0; + rc = RC_SET_AND_ASSERT( NE_FLM_NOT_IMPLEMENTED); + goto Exit; + } + + if (RC_BAD( rc = m_ppAllocators[ uiLoop]->setup( NULL, + pSlabManager, bMemProtect, uiSize, pUsageStats))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_BufferAlloc::allocBuf( + IF_Relocator * pRelocator, + FLMUINT uiSize, + void * pvInitialData, + FLMUINT uiDataSize, + FLMBYTE ** ppucBuffer, + FLMBOOL * pbAllocatedOnHeap) +{ + RCODE rc = NE_FLM_OK; + IF_FixedAlloc * pAllocator = getAllocator( uiSize); + + if( pbAllocatedOnHeap) + { + *pbAllocatedOnHeap = FALSE; + } + + if( pAllocator) + { + flmAssert( pAllocator->getCellSize() >= uiSize); + + if( (*ppucBuffer = (FLMBYTE *)pAllocator->allocCell( pRelocator, + pvInitialData, uiDataSize)) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + } + else + { + if( RC_BAD( rc = f_alloc( uiSize, ppucBuffer))) + { + goto Exit; + } + + m_pSlabManager->incrementTotalBytesAllocated( + f_msize( *ppucBuffer), FALSE); + + if( pvInitialData) + { + f_memcpy( *ppucBuffer, pvInitialData, uiDataSize); + } + + if( pbAllocatedOnHeap) + { + *pbAllocatedOnHeap = TRUE; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_BufferAlloc::reallocBuf( + IF_Relocator * pRelocator, + FLMUINT uiOldSize, + FLMUINT uiNewSize, + void * pvInitialData, + FLMUINT uiDataSize, + FLMBYTE ** ppucBuffer, + FLMBOOL * pbAllocatedOnHeap) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucTmp; + IF_FixedAlloc * pOldAllocator; + IF_FixedAlloc * pNewAllocator; + FLMBOOL bLockedMutex = FALSE; + + flmAssert( uiNewSize); + + if( !uiOldSize) + { + rc = allocBuf( pRelocator, uiNewSize, pvInitialData, uiDataSize, + ppucBuffer, pbAllocatedOnHeap); + goto Exit; + } + + pOldAllocator = getAllocator( uiOldSize); + pNewAllocator = getAllocator( uiNewSize); + + if( pOldAllocator && pOldAllocator == pNewAllocator) + { + // The allocation will still fit in the same cell + + goto Exit; + } + + m_pSlabManager->lockMutex(); + bLockedMutex = TRUE; + + if( pbAllocatedOnHeap) + { + *pbAllocatedOnHeap = FALSE; + } + + if( pOldAllocator) + { + if( pNewAllocator) + { + flmAssert( pOldAllocator != pNewAllocator); + + if( (pucTmp = (FLMBYTE *)pNewAllocator->allocCell( pRelocator, + NULL, 0, TRUE)) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + } + else + { + if( RC_BAD( rc = f_alloc( uiNewSize, &pucTmp))) + { + goto Exit; + } + + m_pSlabManager->incrementTotalBytesAllocated( + f_msize( pucTmp), FALSE); + + if( pbAllocatedOnHeap) + { + *pbAllocatedOnHeap = TRUE; + } + } + + f_memcpy( pucTmp, *ppucBuffer, f_min( uiOldSize, uiNewSize)); + pOldAllocator->freeCell( *ppucBuffer, TRUE); + *ppucBuffer = pucTmp; + } + else + { + if( pNewAllocator) + { + if( (pucTmp = (FLMBYTE *)pNewAllocator->allocCell( pRelocator, + *ppucBuffer, f_min( uiOldSize, uiNewSize))) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + m_pSlabManager->decrementTotalBytesAllocated( + f_msize( *ppucBuffer), TRUE); + f_free( ppucBuffer); + *ppucBuffer = pucTmp; + } + else + { + FLMUINT uiOldAllocSize = f_msize( *ppucBuffer); + + flmAssert( uiOldSize > m_ppAllocators[ NUM_BUF_ALLOCATORS - 1]->getCellSize()); + flmAssert( uiNewSize > m_ppAllocators[ NUM_BUF_ALLOCATORS - 1]->getCellSize()); + + if( RC_BAD( rc = f_realloc( uiNewSize, ppucBuffer))) + { + goto Exit; + } + + m_pSlabManager->decrementTotalBytesAllocated( + uiOldAllocSize, TRUE); + m_pSlabManager->incrementTotalBytesAllocated( + f_msize( *ppucBuffer), TRUE); + + if( pbAllocatedOnHeap) + { + *pbAllocatedOnHeap = TRUE; + } + } + } + +Exit: + + if( bLockedMutex) + { + m_pSlabManager->unlockMutex(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_BufferAlloc::freeBuf( + FLMUINT uiSize, + FLMBYTE ** ppucBuffer) +{ + IF_FixedAlloc * pAllocator = getAllocator( uiSize); + + if( pAllocator) + { + pAllocator->freeCell( *ppucBuffer, FALSE); + *ppucBuffer = NULL; + } + else + { + m_pSlabManager->decrementTotalBytesAllocated( + f_msize( *ppucBuffer), FALSE); + f_free( ppucBuffer); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_BufferAlloc::defragmentMemory( void) +{ + FLMUINT uiLoop; + + for( uiLoop = 0; uiLoop < NUM_BUF_ALLOCATORS; uiLoop++) + { + if( m_ppAllocators[ uiLoop]) + { + m_ppAllocators[ uiLoop]->defragmentMemory(); + m_ppAllocators[ uiLoop]->freeUnused(); + } + + uiLoop++; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT F_BufferAlloc::getTrueSize( + FLMUINT uiSize, + FLMBYTE * pucBuffer) +{ + FLMUINT uiTrueSize; + IF_FixedAlloc * pAllocator; + + if( !uiSize) + { + uiTrueSize = 0; + } + else if( (pAllocator = getAllocator( uiSize)) != NULL) + { + uiTrueSize = pAllocator->getCellSize(); + } + else + { + uiTrueSize = f_msize( pucBuffer); + } + + return( uiTrueSize); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +IF_FixedAlloc * F_BufferAlloc::getAllocator( + FLMUINT uiSize) +{ + IF_FixedAlloc * pAllocator; + + flmAssert( uiSize); + + if( uiSize <= CELL_SIZE_10) + { + if( uiSize <= CELL_SIZE_4) + { + if( uiSize <= CELL_SIZE_2) + { + if( uiSize <= CELL_SIZE_0) + { + pAllocator = m_ppAllocators [0]; + } + else + { + pAllocator = (uiSize <= CELL_SIZE_1 + ? m_ppAllocators [1] + : m_ppAllocators [2]); + } + } + else + { + pAllocator = (uiSize <= CELL_SIZE_3 + ? m_ppAllocators [3] + : m_ppAllocators [4]); + } + } + else if( uiSize <= CELL_SIZE_7) + { + if( uiSize <= CELL_SIZE_5) + { + pAllocator = m_ppAllocators [5]; + } + else + { + pAllocator = (uiSize <= CELL_SIZE_6 + ? m_ppAllocators [6] + : m_ppAllocators [7]); + } + } + else + { + if( uiSize <= CELL_SIZE_8) + { + pAllocator = m_ppAllocators [8]; + } + else + { + pAllocator = (uiSize <= CELL_SIZE_9 + ? m_ppAllocators [9] + : m_ppAllocators [10]); + } + } + } + else if( uiSize <= CELL_SIZE_16) + { + if( uiSize <= CELL_SIZE_13) + { + if( uiSize <= CELL_SIZE_11) + { + pAllocator = m_ppAllocators [11]; + } + else + { + pAllocator = (uiSize <= CELL_SIZE_12 + ? m_ppAllocators [12] + : m_ppAllocators [13]); + } + } + else + { + if( uiSize <= CELL_SIZE_14) + { + pAllocator = m_ppAllocators [14]; + } + else + { + pAllocator = (uiSize <= CELL_SIZE_15 + ? m_ppAllocators [15] + : m_ppAllocators [16]); + } + } + } + else if( uiSize <= CELL_SIZE_19) + { + if( uiSize <= CELL_SIZE_17) + { + pAllocator = m_ppAllocators [17]; + } + else + { + pAllocator = (uiSize <= CELL_SIZE_18 + ? m_ppAllocators [18] + : m_ppAllocators [19]); + } + } + else if( uiSize <= CELL_SIZE_21) + { + pAllocator = (uiSize <= CELL_SIZE_20 + ? m_ppAllocators [20] + : m_ppAllocators [21]); + } + else + { + pAllocator = NULL; + } + + return( pAllocator); +} +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_MultiAlloc::setup( + F_SlabManager * pSlabManager, + FLMBOOL bMemProtect, + FLMUINT * puiCellSizes, + FLM_SLAB_USAGE * pUsageStats) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiLoop; + FLMUINT uiCellCount; + + m_pSlabManager = pSlabManager; + m_pSlabManager->AddRef(); + + uiCellCount = 0; + while( puiCellSizes[ uiCellCount]) + { + uiCellCount++; + } + + if( !uiCellCount) + { + rc = RC_SET_AND_ASSERT( NE_FLM_INVALID_PARM); + goto Exit; + } + + f_qsort( puiCellSizes, 0, uiCellCount - 1, + f_qsortUINTCompare, f_qsortUINTSwap); + + if( RC_BAD( rc = f_alloc( + sizeof( FLMUINT *) * (uiCellCount + 1), &m_puiCellSizes))) + { + goto Exit; + } + + m_pSlabManager->incrementTotalBytesAllocated( + f_msize( m_puiCellSizes), FALSE); + + f_memcpy( m_puiCellSizes, puiCellSizes, + (uiCellCount + 1) * sizeof( FLMUINT)); + + // Set up the allocators + + if( RC_BAD( rc = f_calloc( + sizeof( F_FixedAlloc *) * (uiCellCount + 1), &m_ppAllocators))) + { + goto Exit; + } + + m_pSlabManager->incrementTotalBytesAllocated( + f_msize( m_ppAllocators), FALSE); + + uiLoop = 0; + while( m_puiCellSizes[ uiLoop]) + { + if( (m_ppAllocators[ uiLoop] = f_new F_FixedAlloc) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_ppAllocators[ uiLoop]->setup( NULL, + pSlabManager, bMemProtect, m_puiCellSizes[ uiLoop], pUsageStats))) + { + goto Exit; + } + + uiLoop++; + } + +Exit: + + if( RC_BAD( rc)) + { + cleanup(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_MultiAlloc::cleanup( void) +{ + FLMUINT uiLoop = 0; + + if( !m_puiCellSizes || !m_ppAllocators) + { + goto Exit; + } + + while( m_puiCellSizes[ uiLoop]) + { + if( m_ppAllocators[ uiLoop]) + { + m_ppAllocators[ uiLoop]->Release(); + m_ppAllocators[ uiLoop] = NULL; + } + + uiLoop++; + } + +Exit: + + if( m_puiCellSizes) + { + m_pSlabManager->decrementTotalBytesAllocated( + f_msize( m_puiCellSizes), FALSE); + f_free( &m_puiCellSizes); + } + + if( m_ppAllocators) + { + m_pSlabManager->decrementTotalBytesAllocated( + f_msize( m_ppAllocators), FALSE); + f_free( &m_ppAllocators); + } + + if( m_pSlabManager) + { + m_pSlabManager->Release(); + m_pSlabManager = NULL; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_MultiAlloc::allocBuf( + IF_Relocator * pRelocator, + FLMUINT uiSize, + FLMBYTE ** ppucBuffer, + FLMBOOL bMutexLocked) +{ + RCODE rc = NE_FLM_OK; + IF_FixedAlloc * pAllocator = getAllocator( uiSize); + + flmAssert( pAllocator); + flmAssert( pAllocator->getCellSize() >= uiSize); + + if( (*ppucBuffer = (FLMBYTE *)pAllocator->allocCell( pRelocator, + NULL, 0, bMutexLocked)) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_MultiAlloc::reallocBuf( + IF_Relocator * pRelocator, + FLMUINT uiNewSize, + FLMBYTE ** ppucBuffer, + FLMBOOL bMutexLocked) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucTmp; + IF_FixedAlloc * pOldAllocator; + IF_FixedAlloc * pNewAllocator; + FLMBOOL bLockedMutex = FALSE; + + flmAssert( uiNewSize); + + if( !(*ppucBuffer)) + { + rc = allocBuf( pRelocator, uiNewSize, ppucBuffer); + goto Exit; + } + + pOldAllocator = getAllocator( *ppucBuffer); + pNewAllocator = getAllocator( uiNewSize); + + if( pOldAllocator == pNewAllocator) + { + // The allocation will still fit in the same cell + + goto Exit; + } + + if( !bMutexLocked) + { + m_pSlabManager->lockMutex(); + bLockedMutex = TRUE; + } + + if( (pucTmp = (FLMBYTE *)pNewAllocator->allocCell( pRelocator, *ppucBuffer, + f_min( uiNewSize, pOldAllocator->getCellSize()), TRUE)) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + pOldAllocator->freeCell( *ppucBuffer, TRUE); + *ppucBuffer = pucTmp; + +Exit: + + if( bLockedMutex) + { + m_pSlabManager->unlockMutex(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_MultiAlloc::defragmentMemory( void) +{ + FLMUINT uiLoop = 0; + + while( m_puiCellSizes[ uiLoop]) + { + if( m_ppAllocators[ uiLoop]) + { + m_ppAllocators[ uiLoop]->defragmentMemory(); + m_ppAllocators[ uiLoop]->freeUnused(); + } + + uiLoop++; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +IF_FixedAlloc * F_MultiAlloc::getAllocator( + FLMUINT uiSize) +{ + IF_FixedAlloc * pAllocator = NULL; + FLMUINT uiLoop; + + flmAssert( uiSize); + + for( uiLoop = 0; m_puiCellSizes[ uiLoop]; uiLoop++) + { + if( m_puiCellSizes[ uiLoop] >= uiSize) + { + pAllocator = m_ppAllocators[ uiLoop]; + break; + } + } + + return( pAllocator); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +IF_FixedAlloc * F_MultiAlloc::getAllocator( + FLMBYTE * pucBuffer) +{ + F_FixedAlloc::CELLHEADER * pHeader; + F_FixedAlloc::SLAB * pSlab; + IF_FixedAlloc * pAllocator = NULL; + + m_pSlabManager->lockMutex(); + + pHeader = (F_FixedAlloc::CELLHEADER *)(pucBuffer - + F_FixedAlloc::getAllocAlignedSize( + sizeof( F_FixedAlloc::CELLHEADER2))); + pSlab = pHeader->pContainingSlab; + pAllocator = (IF_FixedAlloc *)pSlab->pvAllocator; + + m_pSlabManager->unlockMutex(); + return( pAllocator); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_MEM_PROTECT +void F_MultiAlloc::protectBuffer( + void * pvBuffer, + FLMBOOL bMutexLocked) +{ + F_FixedAlloc::CELLHEADER * pHeader; + F_FixedAlloc::SLAB * pSlab; + IF_FixedAlloc * pAllocator = NULL; + FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; + + if( !bMutexLocked) + { + m_pSlabManager->lockMutex(); + } + + pHeader = (F_FixedAlloc::CELLHEADER *)(pucBuffer - + F_FixedAlloc::getAllocAlignedSize( + sizeof( F_FixedAlloc::CELLHEADER2))); + pSlab = pHeader->pContainingSlab; + pAllocator = (IF_FixedAlloc *)pSlab->pvAllocator; + pAllocator->protectSlab( pSlab, TRUE); + + if( !bMutexLocked) + { + m_pSlabManager->unlockMutex(); + } +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_MEM_PROTECT +void F_MultiAlloc::unprotectBuffer( + void * pvBuffer, + FLMBOOL bMutexLocked) +{ + F_FixedAlloc::CELLHEADER * pHeader; + F_FixedAlloc::SLAB * pSlab; + IF_FixedAlloc * pAllocator = NULL; + FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; + + if( !bMutexLocked) + { + m_pSlabManager->lockMutex(); + } + + pHeader = (F_FixedAlloc::CELLHEADER *)(pucBuffer - + F_FixedAlloc::getAllocAlignedSize( + sizeof( F_FixedAlloc::CELLHEADER2))); + pSlab = pHeader->pContainingSlab; + pAllocator = (IF_FixedAlloc *)pSlab->pvAllocator; + pAllocator->unprotectSlab( pSlab, TRUE); + + if( !bMutexLocked) + { + m_pSlabManager->unlockMutex(); + } +} +#endif + +#undef new +#undef delete +/**************************************************************************** +Desc: +****************************************************************************/ +void * F_Base::operator new( + FLMSIZET uiSize, + const char * pszFile, + int iLine) +{ + void * pvReturnPtr = NULL; + +#ifdef FLM_DEBUG + f_allocImp( uiSize, &pvReturnPtr, TRUE, pszFile, iLine); +#else + F_UNREFERENCED_PARM( pszFile); + F_UNREFERENCED_PARM( iLine); + + f_allocImp( uiSize, &pvReturnPtr); +#endif + + return( pvReturnPtr); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void * F_Base::operator new[]( + FLMSIZET uiSize, + const char * pszFile, + int iLine) +{ + void * pvReturnPtr = NULL; + +#ifdef FLM_DEBUG + f_allocImp( uiSize, &pvReturnPtr, TRUE, pszFile, iLine); +#else + F_UNREFERENCED_PARM( pszFile); + F_UNREFERENCED_PARM( iLine); + + f_allocImp( uiSize, &pvReturnPtr); +#endif + + return( pvReturnPtr); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_Base::operator delete( + void * ptr, + const char *, // file + int) // line +{ + if( !ptr) + { + return; + } + + f_freeImp( &ptr, TRUE); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_Base::operator delete[]( + void * ptr, + const char *, // file + int) // line +{ + if( !ptr) + { + return; + } + + f_freeImp( &ptr, TRUE); +} + +/************************************************************************ +Desc: +*************************************************************************/ +void * F_OSBase::operator new( + FLMSIZET uiSize) +{ + return( malloc( uiSize)); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_DEBUG +void * F_OSBase::operator new( + FLMSIZET uiSize, + const char *, // pszFile, + int) // iLine) +{ + return( malloc( uiSize)); +} +#endif + +/************************************************************************ +Desc: +*************************************************************************/ +void F_OSBase::operator delete( + void * ptr) +{ + free( ptr); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_OSBase::operator delete[]( + void * ptr) +{ + free( &ptr); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_DEBUG) && !defined( FLM_WATCOM_NLM) && !defined( FLM_SOLARIS) +void F_OSBase::operator delete( + void * ptr, + const char *, // file + int) // line +{ + free( &ptr); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_DEBUG) && !defined( FLM_WATCOM_NLM) && !defined( FLM_SOLARIS) +void F_OSBase::operator delete[]( + void * ptr, + const char *, // file + int) // line +{ + free( &ptr); +} +#endif diff --git a/ftk/src/ftkmfh.cpp b/ftk/src/ftkmfh.cpp new file mode 100644 index 0000000..f5e5602 --- /dev/null +++ b/ftk/src/ftkmfh.cpp @@ -0,0 +1,915 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains the F_64BitFile class +// +// Tabs: 3 +// +// Copyright (c) 2001,2003-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: f64bitfh.cpp 3111 2006-01-19 13:10:50 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +/**************************************************************************** +Desc: +****************************************************************************/ +F_MultiFileHdl::F_MultiFileHdl( + FLMUINT uiMaxFileSize) +{ + m_bOpen = FALSE; + m_szPath[ 0] = 0; + m_ui64EOF = 0; + m_pLockFileHdl = NULL; + f_memset( m_pFileHdlList, 0, sizeof( FH_INFO) * F_MULTI_FHDL_LIST_SIZE); + m_uiMaxFileSize = uiMaxFileSize; + if( !m_uiMaxFileSize) + { + m_uiMaxFileSize = F_MULTI_FHDL_DEFAULT_MAX_FILE_SIZE; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_MultiFileHdl::~F_MultiFileHdl() +{ + if( m_bOpen) + { + close(); + } + + flmAssert( !m_pLockFileHdl); +} + +/**************************************************************************** +Desc: Closes all data files associated with the object +****************************************************************************/ +void F_MultiFileHdl::close( + FLMBOOL bDelete) +{ + FLMUINT uiLoop; + IF_DirHdl * pDir = NULL; + char szTmpPath[ F_PATH_MAX_SIZE]; + RCODE rc = NE_FLM_OK; + + if( !m_bOpen) + { + return; + } + + for( uiLoop = 0; uiLoop < F_MULTI_FHDL_LIST_SIZE; uiLoop++) + { + if( m_pFileHdlList[ uiLoop].pFileHdl) + { + if( m_pFileHdlList[ uiLoop].bDirty) + { + (void)m_pFileHdlList[ uiLoop].pFileHdl->flush(); + } + m_pFileHdlList[ uiLoop].pFileHdl->close(); + m_pFileHdlList[ uiLoop].pFileHdl->Release(); + f_memset( &m_pFileHdlList[ uiLoop], 0, sizeof( FH_INFO)); + } + } + + m_ui64EOF = 0; + m_bOpen = FALSE; + + if( bDelete) + { + if( RC_OK( gv_pFileSystem->openDir( + m_szPath, "*.64", &pDir))) + { + /* + Remove all data files + */ + + for( rc = pDir->next(); !RC_BAD( rc) ; rc = pDir->next()) + { + pDir->currentItemPath( szTmpPath); + flmAssert( f_strstr( szTmpPath, ".64") != 0); + (void)gv_pFileSystem->deleteFile( szTmpPath); + } + + pDir->Release(); + pDir = NULL; + } + + /* + Release and delete the lock file + */ + + (void)releaseLockFile( m_szPath, TRUE); + + /* + Remove the directory + */ + + (void)gv_pFileSystem->removeDir( m_szPath); + } + else + { + (void)releaseLockFile( m_szPath, FALSE); + } +} + +/**************************************************************************** +Desc: Removes a 64-bit file +****************************************************************************/ +RCODE F_MultiFileHdl::deleteMultiFile( + const char * pszPath) +{ + IF_DirHdl * pDir = NULL; + char szTmpPath[ F_PATH_MAX_SIZE]; + RCODE rc = NE_FLM_OK; + + // Can't use this handle to delete something if we already + // have a file open. + + if( m_bOpen) + { + + // Can't jump to exit, because it calls releaseLockFile + + return( RC_SET_AND_ASSERT( NE_FLM_FAILURE)); + } + + if( RC_BAD( rc = gv_pFileSystem->doesFileExist( pszPath))) + { + goto Exit; + } + + if( !gv_pFileSystem->isDir( pszPath)) + { + /* + If the path specifies a single file rather than a + 64-bit directory, just go ahead and delete the file. + */ + + rc = gv_pFileSystem->deleteFile( pszPath); + goto Exit; + } + + if( RC_BAD( rc = createLockFile( pszPath))) + { + goto Exit; + } + + if( RC_OK( gv_pFileSystem->openDir( pszPath, "*.64", &pDir))) + { + /* + Remove all data files + */ + + for( rc = pDir->next(); !RC_BAD( rc) ; rc = pDir->next()) + { + pDir->currentItemPath( szTmpPath); + flmAssert( f_strstr( szTmpPath, ".64") != 0); + (void)gv_pFileSystem->deleteFile( szTmpPath); + } + + pDir->Release(); + pDir = NULL; + rc = NE_FLM_OK; + } + + /* + Release and delete the lock file + */ + + (void)releaseLockFile( pszPath, TRUE); + + /* + Remove the directory + */ + + (void)gv_pFileSystem->removeDir( pszPath); + +Exit: + + (void)releaseLockFile( pszPath, FALSE); + + return( rc); +} + +/**************************************************************************** +Desc: Creates a new 64-bit "file" +****************************************************************************/ +RCODE F_MultiFileHdl::create( + const char * pszPath) +{ + FLMBOOL bCreatedDir = FALSE; + RCODE rc = NE_FLM_OK; + + if( m_bOpen) + { + rc = RC_SET_AND_ASSERT( NE_FLM_FAILURE); + goto Exit; + } + + if( RC_BAD( rc = gv_pFileSystem->createDir( pszPath))) + { + goto Exit; + } + + f_strcpy( m_szPath, pszPath); + bCreatedDir = TRUE; + + /* + Create the lock file + */ + + if( RC_BAD( rc = createLockFile( m_szPath))) + { + goto Exit; + } + + /* + Initialize the EOF to 0 and set the state to open + */ + + m_ui64EOF = 0; + m_bOpen = TRUE; + +Exit: + + /* + Release the lock file + */ + + if( RC_BAD( rc)) + { + (void)releaseLockFile( m_szPath, TRUE); + if( bCreatedDir) + { + (void)gv_pFileSystem->removeDir( m_szPath); + } + } + + return( rc); +} + +/**************************************************************************** +Desc: Creates a new 64-bit file with a unique, generated name +****************************************************************************/ +RCODE F_MultiFileHdl::createUnique( + const char * pszPath, // Directory where the file is to be created + const char * pszFileExtension) // Extension to be used on the new file. +{ + FLMUINT uiCount; + FLMBOOL bModext = TRUE; + FLMBOOL bCreatedDir = FALSE; + FLMUINT uiBaseTime = 0; + FLMBYTE ucHighByte = 0; + char szDirName[ F_FILENAME_SIZE]; + char szTmpPath[ F_PATH_MAX_SIZE]; + char szBasePath[ F_PATH_MAX_SIZE]; + RCODE rc = NE_FLM_OK; + + if( m_bOpen) + { + rc = RC_SET_AND_ASSERT( NE_FLM_FAILURE); + goto Exit; + } + + if( !pszPath || pszPath[ 0] == '\0') + { +#if defined( FLM_UNIX) + f_strcpy( szBasePath, "./"); +#elif defined( FLM_NLM) + f_strcpy( szBasePath, "SYS:_NETWARE"); +#else + szBasePath[ 0] = '\0'; +#endif + } + else + { + f_strcpy( szBasePath, pszPath); + } + + if ((pszFileExtension) && (f_strlen( pszFileExtension) >= 3)) + { + bModext = FALSE; + } + + uiCount = 0; + szDirName[ 0] = '\0'; + do + { + gv_pFileSystem->pathCreateUniqueName( &uiBaseTime, szDirName, pszFileExtension, + &ucHighByte, bModext); + + f_strcpy( szTmpPath, szBasePath); + gv_pFileSystem->pathAppend( szTmpPath, szDirName); + rc = gv_pFileSystem->createDir( szTmpPath); + } while ((rc != NE_FLM_OK) && (uiCount++ < 20)); + + if( RC_BAD( rc)) + { + goto Exit; + } + + f_strcpy( m_szPath, szTmpPath); + bCreatedDir = TRUE; + + /* + Create the lock file + */ + + if( RC_BAD( rc = createLockFile( m_szPath))) + { + goto Exit; + } + + /* + Initialize the EOF to 0 and set the state to open + */ + + m_ui64EOF = 0; + m_bOpen = TRUE; + +Exit: + + /* + Release the lock file + */ + + if( RC_BAD( rc)) + { + releaseLockFile( m_szPath, TRUE); + + if( bCreatedDir) + { + (void)gv_pFileSystem->removeDir( m_szPath); + } + } + + return( rc); +} + +/**************************************************************************** +Desc: Opens an existing 64-bit file +****************************************************************************/ +RCODE F_MultiFileHdl::open( + const char * pszPath) +{ + IF_DirHdl * pDir = NULL; + FLMUINT uiTmp; + FLMUINT uiHighFileNum = 0; + FLMUINT64 ui64HighOffset = 0; + RCODE rc = NE_FLM_OK; + + if( m_bOpen) + { + rc = RC_SET_AND_ASSERT( NE_FLM_FAILURE); + goto Exit; + } + + if( RC_BAD( gv_pFileSystem->doesFileExist( pszPath)) || + !gv_pFileSystem->isDir( pszPath)) + { + rc = RC_SET( NE_FLM_IO_PATH_NOT_FOUND); + goto Exit; + } + + f_strcpy( m_szPath, pszPath); + + /* + Create the lock file + */ + + if( RC_BAD( rc = createLockFile( m_szPath))) + { + goto Exit; + } + + /* + Need to determine the current EOF + */ + + if( RC_BAD( rc = gv_pFileSystem->openDir( + m_szPath, (char *)"*.64", &pDir))) + { + goto Exit; + } + + /* + Find all data files to determine the EOF + */ + + for( rc = pDir->next(); !RC_BAD( rc) ; rc = pDir->next()) + { + if( RC_OK( getFileNum( pDir->currentItemName(), &uiTmp))) + { + if( uiTmp >= uiHighFileNum) + { + uiHighFileNum = uiTmp; + ui64HighOffset = pDir->currentItemSize(); + } + } + } + rc = NE_FLM_OK; + + m_ui64EOF = (((FLMUINT64)uiHighFileNum) * m_uiMaxFileSize) + ui64HighOffset; + m_bOpen = TRUE; + +Exit: + + if( pDir) + { + pDir->Release(); + } + + /* + Release the lock file + */ + + if( RC_BAD( rc)) + { + releaseLockFile( m_szPath, FALSE); + } + + return( rc); +} + +/**************************************************************************** +Desc: Flushes cached data to the data file(s) +****************************************************************************/ +RCODE F_MultiFileHdl::flush( void) +{ + FLMUINT uiLoop; + RCODE rc = NE_FLM_OK; + + if( !m_bOpen) + { + rc = RC_SET_AND_ASSERT( NE_FLM_FAILURE); + goto Exit; + } + + for( uiLoop = 0; uiLoop < F_MULTI_FHDL_LIST_SIZE; uiLoop++) + { + if( m_pFileHdlList[ uiLoop].bDirty) + { + if( RC_BAD( rc = m_pFileHdlList[ uiLoop].pFileHdl->flush())) + { + goto Exit; + } + m_pFileHdlList[ uiLoop].bDirty = FALSE; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Reads data from the file +****************************************************************************/ +RCODE F_MultiFileHdl::read( + FLMUINT64 ui64Offset, // Offset to begin reading + FLMUINT uiLength, // Number of bytes to read + void * pvBuffer, // Buffer + FLMUINT * puiBytesRead) // [out] Number of bytes read +{ + FLMUINT uiFileNum = getFileNum( ui64Offset); + FLMUINT uiFileOffset = getFileOffset( ui64Offset); + FLMUINT uiTmp; + FLMUINT uiTotalBytesRead = 0; + FLMUINT uiBytesToRead; + FLMUINT uiMaxReadLen; + IF_FileHdl * pFileHdl; + RCODE rc = NE_FLM_OK; + + /* + Handle the case of a 0-byte read + */ + + if( !uiLength) + { + if( ui64Offset >= m_ui64EOF) + { + rc = RC_SET( NE_FLM_IO_END_OF_FILE); + } + goto Exit; + } + + /* + Read the data file(s), moving to new files as needed. + */ + + for( ;;) + { + if( ui64Offset >= m_ui64EOF) + { + rc = RC_SET( NE_FLM_IO_END_OF_FILE); + goto Exit; + } + + uiMaxReadLen = m_uiMaxFileSize - uiFileOffset; + flmAssert( uiMaxReadLen != 0); + uiTmp = (uiLength >= uiMaxReadLen ? uiMaxReadLen : uiLength); + uiBytesToRead = (((FLMUINT64)uiTmp > (FLMUINT64)(m_ui64EOF - ui64Offset)) + ? (FLMUINT)(m_ui64EOF - ui64Offset) + : uiTmp); + + if( RC_BAD( rc = getFileHdl( uiFileNum, FALSE, &pFileHdl))) + { + if( rc == NE_FLM_IO_PATH_NOT_FOUND) + { + /* + Handle the case of a sparse file by filling the unread + portion of the buffer with zeros. + */ + + f_memset( pvBuffer, 0, uiBytesToRead); + uiTmp = uiBytesToRead; + rc = NE_FLM_OK; + } + else + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pFileHdl->read( uiFileOffset, uiBytesToRead, + pvBuffer, &uiTmp))) + { + if( rc == NE_FLM_IO_END_OF_FILE) + { + /* + Handle the case of a sparse file by filling the unread + portion of the buffer with zeros. + */ + + f_memset( &(((FLMBYTE *)(pvBuffer))[ uiTmp]), + 0, (FLMUINT)(uiBytesToRead - uiTmp)); + uiTmp = uiBytesToRead; + rc = NE_FLM_OK; + } + else + { + goto Exit; + } + } + } + + uiTotalBytesRead += uiTmp; + uiLength -= uiTmp; + if( !uiLength) + { + break; + } + + /* + Set up for next read + */ + + pvBuffer = ((FLMBYTE *)pvBuffer) + uiTmp; + ui64Offset += uiTmp; + uiFileNum = getFileNum( ui64Offset); + uiFileOffset = getFileOffset( ui64Offset); + } + +Exit: + + *puiBytesRead = uiTotalBytesRead; + return( rc); +} + +/**************************************************************************** +Desc: Writes data to the file +****************************************************************************/ +RCODE F_MultiFileHdl::write( + FLMUINT64 ui64Offset, // Offset + FLMUINT uiLength, // Number of bytes to write. + void * pvBuffer, // Buffer that contains bytes to be written + FLMUINT * puiBytesWritten) // Number of bytes written. +{ + FLMUINT uiFileNum = getFileNum( ui64Offset); + FLMUINT uiFileOffset = getFileOffset( ui64Offset); + FLMUINT uiTmp; + FLMUINT uiTotalBytesWritten = 0; + FLMUINT uiBytesToWrite; + FLMUINT uiMaxWriteLen; + IF_FileHdl * pFileHdl; + RCODE rc = NE_FLM_OK; + + /* + Don't allow zero-length writes + */ + + flmAssert( uiLength); + + /* + Write to the data file(s), moving to new files as needed. + */ + + for( ;;) + { + if( RC_BAD( rc = getFileHdl( uiFileNum, TRUE, &pFileHdl))) + { + goto Exit; + } + + uiMaxWriteLen = m_uiMaxFileSize - uiFileOffset; + flmAssert( uiMaxWriteLen != 0); + uiBytesToWrite = uiLength >= uiMaxWriteLen ? uiMaxWriteLen : uiLength; + + uiTmp = 0; + rc = pFileHdl->write( uiFileOffset, uiBytesToWrite, pvBuffer, &uiTmp); + + uiTotalBytesWritten += uiTmp; + uiLength -= uiTmp; + ui64Offset += uiTmp; + + if( RC_BAD( rc)) + { + goto Exit; + } + + if( !uiLength) + { + break; + } + + /* + Set up for next write + */ + + pvBuffer = ((FLMBYTE *)pvBuffer) + uiTmp; + uiFileNum = getFileNum( ui64Offset); + uiFileOffset = getFileOffset( ui64Offset); + } + +Exit: + + if( ui64Offset > m_ui64EOF) + { + m_ui64EOF = ui64Offset; + } + + *puiBytesWritten = uiTotalBytesWritten; + return( rc); +} + +/**************************************************************************** +Desc: Returns the requested file handle +****************************************************************************/ +RCODE F_MultiFileHdl::getFileHdl( + FLMUINT uiFileNum, + FLMBOOL bGetForWrite, + IF_FileHdl ** ppFileHdl) +{ + FLMUINT uiSlot; + IF_FileHdl * pTmpHdl; + char szPath[ F_PATH_MAX_SIZE]; + RCODE rc = NE_FLM_OK; + + flmAssert( m_bOpen); + + *ppFileHdl = NULL; + + uiSlot = uiFileNum % F_MULTI_FHDL_LIST_SIZE; + pTmpHdl = m_pFileHdlList[ uiSlot].pFileHdl; + + if( pTmpHdl && m_pFileHdlList[ uiSlot].uiFileNum != uiFileNum) + { + if( RC_BAD( rc = pTmpHdl->flush())) + { + goto Exit; + } + + pTmpHdl->close(); + pTmpHdl->Release(); + pTmpHdl = NULL; + + f_memset( &m_pFileHdlList[ uiSlot], 0, sizeof( FH_INFO)); + } + + if( !pTmpHdl) + { + dataFilePath( uiFileNum, szPath); + if( RC_BAD( rc = gv_pFileSystem->openFile( szPath, + FLM_IO_RDWR, &pTmpHdl))) + { + if( rc == NE_FLM_IO_PATH_NOT_FOUND && bGetForWrite) + { + if( RC_BAD( rc = gv_pFileSystem->createFile( szPath, + FLM_IO_RDWR, &pTmpHdl))) + { + goto Exit; + } + } + else + { + goto Exit; + } + } + + m_pFileHdlList[ uiSlot].pFileHdl = pTmpHdl; + m_pFileHdlList[ uiSlot].uiFileNum = uiFileNum; + flmAssert( !m_pFileHdlList[ uiSlot].bDirty); + } + + *ppFileHdl = m_pFileHdlList[ uiSlot].pFileHdl; + if( bGetForWrite) + { + m_pFileHdlList[ uiSlot].bDirty = TRUE; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Given a data file name, returns the file's number +****************************************************************************/ +RCODE F_MultiFileHdl::getFileNum( + const char * pszFileName, + FLMUINT * puiFileNum) +{ + FLMUINT uiCnt = 0; + FLMUINT uiDigit; + FLMUINT uiFileNum = 0; + RCODE rc = NE_FLM_OK; + + if( f_strlen( pszFileName) != 11) // XXXXXXXX.64 + { + rc = RC_SET( NE_FLM_IO_INVALID_FILENAME); + goto Exit; + } + + if( f_strcmp( &pszFileName[ 8], ".64") != 0) + { + rc = RC_SET( NE_FLM_IO_INVALID_FILENAME); + goto Exit; + } + + while( uiCnt < 8) + { + uiDigit = pszFileName[ uiCnt]; + if( uiDigit >= NATIVE_LOWER_A && uiDigit <= NATIVE_LOWER_F) + { + uiDigit = (FLMUINT)(uiDigit - NATIVE_LOWER_A) + 10; + } + else if( uiDigit >= NATIVE_UPPER_A && uiDigit <= NATIVE_UPPER_F) + { + uiDigit = (FLMUINT)(uiDigit - NATIVE_UPPER_A) + 10; + } + else if( uiDigit >= NATIVE_ZERO && uiDigit <= NATIVE_NINE) + { + uiDigit -= NATIVE_ZERO; + } + else + { + /* + Invalid character found in the file name + */ + + rc = RC_SET( NE_FLM_IO_INVALID_FILENAME); + goto Exit; + } + + uiFileNum <<= 4; + uiFileNum += uiDigit; + uiCnt++; + } + + *puiFileNum = uiFileNum; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine obtains exclusive access to a 64-bit file by creating + a .lck file. The object holds the .lck file open as long as the + 64-bit file is open. +****************************************************************************/ +RCODE F_MultiFileHdl::createLockFile( + const char * pszBasePath) +{ + RCODE rc = NE_FLM_OK; + char szLockPath [F_PATH_MAX_SIZE]; + F_FileHdl * pLockFileHdl = NULL; + + f_strcpy( szLockPath, pszBasePath); + gv_pFileSystem->pathAppend( szLockPath, "64.LCK"); + + /* + Attempt to create the lock file. If it fails, the lock file + may have been left because of a crash. Hence, we first try + to delete the file. If that succeeds, we then attempt to + create the file again. If it, or the 2nd create fail, we simply + return an access denied error. + */ + + if ((pLockFileHdl = f_new F_FileHdl) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + +#ifndef FLM_UNIX + pLockFileHdl->setupFileHdl( 0, TRUE); +#else + + // On Unix, we do not want to delete the file because it + // will succeed even if someone else has the file open. + + pLockFileHdl->setupFileHdl( 0, FALSE); +#endif + + if( RC_BAD( pLockFileHdl->create( szLockPath, + FLM_IO_RDWR | FLM_IO_EXCL | FLM_IO_SH_DENYRW))) + { +#ifndef FLM_UNIX + if (RC_BAD( gv_pFileSystem->deleteFile( szLockPath))) + { + rc = RC_SET( NE_FLM_IO_ACCESS_DENIED); + goto Exit; + } + else if (RC_BAD( pLockFileHdl->create( szLockPath, + FLM_IO_RDWR | FLM_IO_EXCL | + FLM_IO_SH_DENYRW))) + { + rc = RC_SET( NE_FLM_IO_ACCESS_DENIED); + goto Exit; + } +#else + + if( RC_BAD( pLockFileHdl->open( szLockPath, + FLM_IO_RDWR | FLM_IO_SH_DENYRW))) + { + rc = RC_SET( NE_FLM_IO_ACCESS_DENIED); + goto Exit; + } +#endif + } + +#ifdef FLM_UNIX + if( RC_BAD( pLockFileHdl->lock())) + { + rc = RC_SET( NE_FLM_IO_ACCESS_DENIED); + goto Exit; + } +#endif + + m_pLockFileHdl = pLockFileHdl; + pLockFileHdl = NULL; + +Exit: + + if (pLockFileHdl) + { + (void)pLockFileHdl->close(); + pLockFileHdl->Release(); + pLockFileHdl = NULL; + } + return( rc); +} + +/**************************************************************************** +Desc: This is a private method that will truncate the spill file back to + the specified size. +****************************************************************************/ +RCODE F_MultiFileHdl::truncate( + FLMUINT64 ui64NewSize) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiFileNum = getFileNum( ui64NewSize); + IF_FileHdl * pFileHdl; + + if( RC_BAD( rc = getFileHdl( uiFileNum, TRUE, &pFileHdl))) + { + goto Exit; + } + + if (RC_BAD( rc = pFileHdl->truncate( getFileOffset( ui64NewSize)))) + { + goto Exit; + } + +Exit: + + return rc; + +} diff --git a/ftk/src/ftkmisc.cpp b/ftk/src/ftkmisc.cpp new file mode 100644 index 0000000..1455af6 --- /dev/null +++ b/ftk/src/ftkmisc.cpp @@ -0,0 +1,571 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains misc toolkit functions +// +// Tabs: 3 +// +// Copyright (c) 2000-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: ftkmisc.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +// Global data + +FLMUINT gv_uiSerialInitCount = 0; +F_MUTEX gv_hSerialMutex = F_MUTEX_NULL; + +#ifdef FLM_UNIX + F_RandomGenerator gv_SerialRandom; +#endif + +#ifdef FLM_NLM + void f_sleep( + FLMUINT uiMilliseconds) + { + if( ! uiMilliseconds ) + { + kYieldThread(); + } + else + { + kDelayThread( uiMilliseconds); + } + } + +#endif + +#if defined( FLM_UNIX) + + #ifdef FLM_AIX + #ifndef nsleep + extern "C" + { + extern int nsleep( struct timestruc_t *, struct timestruc_t *); + } + #endif + #endif + +/**************************************************************************** +Desc: This routine causes the calling process to delay the given number + of milliseconds. Due to the nature of the call, the actual sleep + time is almost guaranteed to be different from requested sleep time. +In: milliseconds - the number of milliseconds to delay +****************************************************************************/ +void f_sleep( + FLMUINT uiMilliseconds) +{ +#ifdef FLM_AIX + struct timestruc_t timeout; + struct timestruc_t remain; +#else + struct timespec timeout; +#endif + + timeout.tv_sec = (uiMilliseconds / 1000); + timeout.tv_nsec = (uiMilliseconds % 1000) * 1000000; + +#ifdef FLM_AIX + nsleep(&timeout, &remain); +#else + nanosleep(&timeout, 0); +#endif +} +#endif + +/*************************************************************************** +Desc: Map POSIX errno to Flaim IO errors. +***************************************************************************/ +#if defined( FLM_UNIX) || defined( FLM_NLM) +RCODE MapPlatformError( + FLMINT iError, + RCODE defaultRc) +{ + switch (err) + { + case 0: + { + return( NE_FLM_OK); + } + + case ENOENT: + { + return( RC_SET( NE_FLM_IO_PATH_NOT_FOUND)); + } + + case EACCES: + case EEXIST: + { + return( RC_SET( NE_FLM_IO_ACCESS_DENIED)); + } + + case EINVAL: + { + return( RC_SET( NE_FLM_IO_PATH_TOO_LONG)); + } + + case EIO: + { + return( RC_SET( NE_FLM_IO_DISK_FULL)); + } + + case ENOTDIR: + { + return( RC_SET( NE_FLM_IO_DIRECTORY_ERR)); + } + +#ifdef EBADFD + case EBADFD: + { + return( RC_SET( NE_FLM_IO_BAD_FILE_HANDLE)); + } +#endif + +#ifdef EOF + case EOF: + { + return( RC_SET( NE_FLM_IO_END_OF_FILE)); + } +#endif + + case EMFILE: + { + return( RC_SET( NE_FLM_IO_NO_MORE_FILES)); + } + + default: + { + return( RC_SET( defaultRc)); + } + } +} +#endif + +/**************************************************************************** +Desc: This routine initializes the serial number generator. If the O/S + does not provide support for GUID generation or if the GUID + routines fail for some reason, a pseudo-GUID will be generated. +Notes: This routine should only be called once by the process. +****************************************************************************/ +RCODE f_initSerialNumberGenerator( void) +{ + FLMUINT uiTime; + RCODE rc = NE_FLM_OK; + + if (++gv_uiSerialInitCount > 1) + { + goto Exit; + } + + if( RC_BAD( rc = f_mutexCreate( &gv_hSerialMutex))) + { + goto Exit; + } + + f_timeGetSeconds( &uiTime ); + +#if defined( FLM_UNIX) + gv_SerialRandom.randomSetSeed( (FLMUINT32)(uiTime ^ (FLMUINT)getpid())); +#endif + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine will use the operating system calls to generate a + "globally unique" identifier. Typically, this is based on the + MAC address of an ethernet card installed in the machine. If the + machine does not have an ethernet card, or if the OS does not + support generating GUIDs, this routine will generate a pseudo-GUID + using a random number generator. A serial number is 16-bytes. +****************************************************************************/ +RCODE FLMAPI f_createSerialNumber( + FLMBYTE * pszSerialNum) +{ + RCODE rc = NE_FLM_OK; + +#if defined( FLM_WIN) + + UUID uuidVal; + RPC_STATUS err = UuidCreate( &uuidVal); + + if (err == RPC_S_OK || err == RPC_S_UUID_LOCAL_ONLY) + { + UD2FBA( (FLMUINT32)uuidVal.Data1, &pszSerialNum[ 0]); + UW2FBA( (FLMUINT16)uuidVal.Data2, &pszSerialNum[ 4]); + UW2FBA( (FLMUINT16)uuidVal.Data3, &pszSerialNum[ 6]); + f_memcpy( &pszSerialNum[ 8], (FLMBYTE *)uuidVal.Data4, 8); + goto Exit; + } + +#elif defined( FLM_NLM) + + NWGUID guidVal; + int err = SGUIDCreate( &guidVal); + + if( !err || err == 1) // NOTE: 1 == SGUID_WARN_RANDOM_NODE + { + UD2FBA( guidVal.time_low, &pszSerialNum[ 0]); + UW2FBA( guidVal.time_mid, &pszSerialNum[ 4]); + UW2FBA( guidVal.time_hi_and_version, &pszSerialNum[ 6]); + pszSerialNum[ 8] = guidVal.clk_seq_hi_res; + pszSerialNum[ 9] = guidVal.clk_seq_low; + f_memcpy( &pszSerialNum[ 10], (FLMBYTE *)guidVal.node, 6); + goto Exit; + } + +#elif defined( FLM_UNIX) + + // Generate a pseudo GUID value + + flmAssert( gv_hSerialMutex != F_MUTEX_NULL); + + f_mutexLock( gv_hSerialMutex); + + UD2FBA( (FLMUINT32)gv_SerialRandom.randomLong(), &pszSerialNum[ 0]); + UD2FBA( (FLMUINT32)gv_SerialRandom.randomLong(), &pszSerialNum[ 4]); + UD2FBA( (FLMUINT32)gv_SerialRandom.randomLong(), &pszSerialNum[ 8]); + UD2FBA( (FLMUINT32)gv_SerialRandom.randomLong(), &pszSerialNum[ 12]); + + f_mutexUnlock( gv_hSerialMutex); + +#endif + +#if defined( FLM_WIN) || defined( FLM_NLM) +Exit: +#endif + + return( rc); +} + +/**************************************************************************** +Notes: This routine should only be called once by the process. +****************************************************************************/ +void f_freeSerialNumberGenerator( void) +{ + if( (--gv_uiSerialInitCount) > 0) + { + return; + } + + if( gv_hSerialMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &gv_hSerialMutex); + } +} + +/**************************************************************************** +Desc: Generates a table of remainders for each 8-bit byte. The resulting + table is used by flmUpdateCRC to calculate a CRC value. The table + must be freed via a call to f_freeCRCTable. +*****************************************************************************/ +RCODE f_initCRCTable( + FLMUINT32 ** ppui32CRCTbl) +{ + RCODE rc = NE_FLM_OK; + FLMUINT32 * pTable; + FLMUINT32 ui32Val; + FLMUINT32 ui32Loop; + FLMUINT32 ui32SubLoop; + + // Use the standard degree-32 polynomial used by + // Ethernet, PKZIP, etc. for computing the CRC of + // a data stream. This is the little-endian + // representation of the polynomial. The big-endian + // representation is 0x04C11DB7. + +#define CRC_POLYNOMIAL ((FLMUINT32)0xEDB88320) + + *ppui32CRCTbl = NULL; + + if( RC_BAD( rc = f_alloc( 256 * sizeof( FLMUINT32), &pTable))) + { + goto Exit; + } + + for( ui32Loop = 0; ui32Loop < 256; ui32Loop++) + { + ui32Val = ui32Loop; + for( ui32SubLoop = 0; ui32SubLoop < 8; ui32SubLoop++) + { + if( ui32Val & 0x00000001) + { + ui32Val = CRC_POLYNOMIAL ^ (ui32Val >> 1); + } + else + { + ui32Val >>= 1; + } + } + + pTable[ ui32Loop] = ui32Val; + } + + *ppui32CRCTbl = pTable; + pTable = NULL; + +Exit: + + if( pTable) + { + f_free( &pTable); + } + + return( rc); +} + +/**************************************************************************** +Desc: Computes the CRC of the passed-in data buffer. Multiple calls can + be made to this routine to build a CRC over multiple data buffers. + On the first call, *pui32CRC must be initialized to something + (0, etc.). For generating CRCs that are compatible with PKZIP, + *pui32CRC should be initialized to 0xFFFFFFFF and the ones complement + of the resulting CRC should be computed. +*****************************************************************************/ +void f_updateCRC( + FLMUINT32 * pui32CRCTbl, + FLMBYTE * pucBlk, + FLMUINT uiBlkSize, + FLMUINT32 * pui32CRC) +{ + FLMUINT32 ui32CRC = *pui32CRC; + FLMUINT uiLoop; + + for( uiLoop = 0; uiLoop < uiBlkSize; uiLoop++) + { + ui32CRC = (ui32CRC >> 8) ^ pui32CRCTbl[ + ((FLMBYTE)(ui32CRC & 0x000000FF)) ^ pucBlk[ uiLoop]]; + } + + *pui32CRC = ui32CRC; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT f_breakpoint( + FLMUINT uiBreakFlag) +{ + if( uiBreakFlag) + { +#ifdef FLM_NLM + EnterDebugger(); +#else + flmAssert( 0); +#endif + } + + return( 0); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void FLMAPI f_getenv( + const char * pszKey, + FLMBYTE * pszBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiValueLen) +{ + FLMUINT uiValueLen = 0; + + if( !uiBufferSize) + { + goto Exit; + } + + pszBuffer[ 0] = 0; + +#if defined( FLM_WIN) || defined( FLM_UNIX) + char * pszValue; + + if( (pszValue = getenv( pszKey)) != NULL && + (uiValueLen = f_strlen( pszValue)) < uiBufferSize) + { + f_strcpy( (char *)pszBuffer, pszValue); + } +#else + F_UNREFERENCED_PARM( pszKey); +#endif + +Exit: + + if( puiValueLen) + { + *puiValueLen = uiValueLen; + } + + return; +} + +/*************************************************************************** +Desc: Sort an array of items +****************************************************************************/ +void FLMAPI f_qsort( + void * pvBuffer, + FLMUINT uiLowerBounds, + FLMUINT uiUpperBounds, + F_SORT_COMPARE_FUNC fnCompare, + F_SORT_SWAP_FUNC fnSwap) +{ + FLMUINT uiLBPos; + FLMUINT uiUBPos; + FLMUINT uiMIDPos; + FLMUINT uiCurrentPos; + FLMUINT uiLeftItems; + FLMUINT uiRightItems; + FLMINT iCompare; + +Iterate_Larger_Half: + + uiUBPos = uiUpperBounds; + uiLBPos = uiLowerBounds; + uiMIDPos = (uiUpperBounds + uiLowerBounds + 1) / 2; + uiCurrentPos = uiMIDPos; + + for (;;) + { + while (uiLBPos == uiMIDPos || + ((iCompare = + fnCompare( pvBuffer, uiLBPos, uiCurrentPos)) < 0)) + { + if( uiLBPos >= uiUpperBounds) + { + break; + } + uiLBPos++; + } + + while( uiUBPos == uiMIDPos || + (((iCompare = + fnCompare( pvBuffer, uiCurrentPos, uiUBPos)) < 0))) + { + if (!uiUBPos) + { + break; + } + uiUBPos--; + } + + if( uiLBPos < uiUBPos) + { + // Exchange [uiLBPos] with [uiUBPos]. + + fnSwap( pvBuffer, uiLBPos, uiUBPos); + uiLBPos++; + uiUBPos--; + } + else + { + break; + } + } + + // Check for swap( LB, MID ) - cases 3 and 4 + + if( uiLBPos < uiMIDPos ) + { + + // Exchange [uiLBPos] with [uiMIDPos] + + fnSwap( pvBuffer, uiMIDPos, uiLBPos); + uiMIDPos = uiLBPos; + } + else if( uiMIDPos < uiUBPos ) + { + // Exchange [uUBPos] with [uiMIDPos] + + fnSwap( pvBuffer, uiMIDPos, uiUBPos); + uiMIDPos = uiUBPos; + } + + // Check the left piece. + + uiLeftItems = (uiLowerBounds + 1 < uiMIDPos) + ? uiMIDPos - uiLowerBounds + : 0; + + uiRightItems = (uiMIDPos + 1 < uiUpperBounds) + ? uiUpperBounds - uiMIDPos + : 0; + + if( uiLeftItems < uiRightItems) + { + // Recurse on the LEFT side and goto the top on the RIGHT side. + + if( uiLeftItems) + { + f_qsort( pvBuffer, uiLowerBounds, uiMIDPos - 1, fnCompare, fnSwap); + } + + uiLowerBounds = uiMIDPos + 1; + goto Iterate_Larger_Half; + } + else if( uiLeftItems) + { + // Recurse on the RIGHT side and goto the top for the LEFT side. + + if (uiRightItems ) + { + f_qsort( pvBuffer, uiMIDPos + 1, uiUpperBounds, fnCompare, fnSwap); + } + + uiUpperBounds = uiMIDPos - 1; + goto Iterate_Larger_Half; + } +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FLMINT flmQSortUINTCompare( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2) +{ + FLMUINT uiLeft = *(((FLMUINT *)pvBuffer) + uiPos1); + FLMUINT uiRight = *(((FLMUINT *)pvBuffer) + uiPos2); + + if( uiLeft < uiRight) + { + return( -1); + } + else if( uiLeft > uiRight) + { + return( 1); + } + + return( 0); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +void flmQSortUINTSwap( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2) +{ + FLMUINT * puiArray = (FLMUINT *)pvBuffer; + FLMUINT uiTmp = puiArray[ uiPos1]; + + puiArray[ uiPos1] = puiArray[ uiPos2]; + puiArray[ uiPos2] = uiTmp; +} diff --git a/ftk/src/ftkntab.cpp b/ftk/src/ftkntab.cpp new file mode 100644 index 0000000..6e11292 --- /dev/null +++ b/ftk/src/ftkntab.cpp @@ -0,0 +1,1799 @@ +//------------------------------------------------------------------------------ +// Desc: Class for managing a name table. +// +// Tabs: 3 +// +// Copyright (c) 1992, 1994-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: fntable.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +#define MAX_ELEMENTS_TO_LOAD 0xFFFF +#define MAX_ATTRIBUTES_TO_LOAD 0xFFFF + +typedef FLMINT (* TAG_COMPARE_FUNC)( + FLM_TAG_INFO * pTagInfo1, + FLM_TAG_INFO * pTagInfo2); + +FSTATIC FLMINT tagNameCompare( + const FLMUNICODE * puzName1, + const char * pszName1, + const FLMUNICODE * puzName2); + +FSTATIC FLMINT compareTagTypeAndName( + FLM_TAG_INFO * pTagInfo1, + FLM_TAG_INFO * pTagInfo2); + +FSTATIC FLMINT compareTagTypeAndNum( + FLM_TAG_INFO * pTagInfo1, + FLM_TAG_INFO * pTagInfo2); + +FSTATIC void sortTagTbl( + FLM_TAG_INFO ** ppTagInfoTbl, + FLMUINT uiLowerBounds, + FLMUINT uiUpperBounds, + TAG_COMPARE_FUNC fnTagCompare); + +/**************************************************************************** +Desc: +****************************************************************************/ +F_NameTable::F_NameTable() +{ + m_pool.poolInit( 1024); + m_uiMemoryAllocated = 0; + m_ppSortedByTagTypeAndName = NULL; + m_ppSortedByTagTypeAndNum = NULL; + m_uiTblSize = 0; + m_uiNumTags = 0; + m_bTablesSorted = FALSE; + m_ppuzNamespaces = NULL; + m_uiNamespaceTblSize = 0; + m_uiNumNamespaces = 0; + m_hRefMutex = F_MUTEX_NULL; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_NameTable::~F_NameTable() +{ + clearTable( 0); + + if( m_hRefMutex) + { + f_mutexDestroy( &m_hRefMutex); + } +} + +/**************************************************************************** +Desc: Setup name table. This routine should be called immediately after + the object is allocated. +****************************************************************************/ +RCODE FLMAPI F_NameTable::setupNameTable( void) +{ +#ifndef FLM_HAVE_ATOMICS + RCODE rc = NE_FLM_OK; + + if( RC_BAD( rc = f_mutexCreate( &m_hRefMutex))) + { + goto Exit; + } + +Exit: + + return( rc); +#else + return( NE_FLM_OK); +#endif +} + +/**************************************************************************** +Desc: Free everything in the table +****************************************************************************/ +void FLMAPI F_NameTable::clearTable( + FLMUINT uiPoolBlkSize) +{ + m_pool.poolFree(); + + if (uiPoolBlkSize) + { + m_pool.poolInit( uiPoolBlkSize); + } + + m_uiMemoryAllocated = 0; + + // NOTE: Only one allocation is used for m_ppSortedByTagTypeAndName and + // m_ppSortedByTagTypeAndNum - there is no + // need to free m_ppSortedByTagTypeAndNum + + if (m_ppSortedByTagTypeAndName) + { + f_free( &m_ppSortedByTagTypeAndName); + m_ppSortedByTagTypeAndNum = NULL; + m_uiTblSize = 0; + m_uiNumTags = 0; + } + + if (m_ppuzNamespaces) + { + f_free( &m_ppuzNamespaces); + m_ppuzNamespaces = NULL; + m_uiNamespaceTblSize = 0; + m_uiNumNamespaces = 0; + } + + m_bTablesSorted = FALSE; +} + +/**************************************************************************** +Desc: Compare two tag names. Name1 can be NATIVE or UNICODE. If a non-NULL + UNICODE string is passed, it will be used. Otherwise, the NATIVE + string will be used. +Note: Comparison is case sensitive. Either or both names may be NULL. + Empty strings and NULL pointers are considered to be equal. +****************************************************************************/ +FSTATIC FLMINT tagNameCompare( + const FLMUNICODE * puzName1, // If NULL, use pszName1 for comparison + const char * pszName1, + const FLMUNICODE * puzName2) +{ + FLMUNICODE uzChar1; + FLMUNICODE uzChar2; + FLMUNICODE uzLowerChar1; + FLMUNICODE uzLowerChar2; + + if (puzName1) + { + if (!puzName2) + { + if (*puzName1) + { + return( 1); + } + else + { + return( 0); + } + } + + for (;;) + { + uzChar1 = *puzName1; + uzChar2 = *puzName2; + + if (!uzChar1) + { + if (!uzChar2) + { + return( 0); + } + else + { + return( -1); + } + } + else if (!uzChar2) + { + return( 1); + } + else if (uzChar1 != uzChar2) + { +Test_Case: + uzLowerChar1 = f_unitolower( uzChar1); + uzLowerChar2 = f_unitolower( uzChar2); + + if (uzLowerChar1 < uzLowerChar2) + { + return( -1); + } + else if (uzLowerChar1 > uzLowerChar2) + { + return( 1); + } + else if (uzLowerChar1 != uzChar1) + { + + // Char1 is uppercase, char2 is lowercase + // Uppercase sorts before lowercase + + return( -1); + } + else + { + + // Char1 is lowercase, char2 is uppercase + // Lowercase sorts after uppercase + + return( 1); + } + } + puzName1++; + puzName2++; + } + } + else if (pszName1) + { + if (!puzName2) + { + if (*pszName1) + { + return( 1); + } + else + { + return( 0); + } + } + for (;;) + { + uzChar1 = (FLMUNICODE)*pszName1; + uzChar2 = *puzName2; + if (!uzChar1) + { + if (!uzChar2) + { + return( 0); + } + else + { + return( -1); + } + } + else if (!uzChar2) + { + return( 1); + } + else if (uzChar1 != uzChar2) + { + goto Test_Case; + } + pszName1++; + puzName2++; + } + } + else if (puzName2) + { + + // Both name1's are NULL. + + if (*puzName2) + { + return( -1); + } + else + { + return( 0); + } + } + else + { + + // Both name1 and name2 are NULL. + + return( 0); + } +} + +/**************************************************************************** +Desc: Lookup a tag by type and tag number. +****************************************************************************/ +FLM_TAG_INFO * F_NameTable::findTagByTypeAndNum( + FLMUINT uiType, + FLMUINT uiTagNum, + FLMUINT * puiInsertPos) +{ + FLM_TAG_INFO * pTagInfo = NULL; + FLMUINT uiTblSize; + FLMUINT uiLow; + FLMUINT uiMid; + FLMUINT uiHigh; + FLMUINT uiTblTagNum; + FLMUINT uiTblType; + + if (!m_bTablesSorted) + { + sortTags(); + } + + // Do binary search in the table + + if ((uiTblSize = m_uiNumTags) == 0) + { + if (puiInsertPos) + { + *puiInsertPos = 0; + } + goto Exit; + } + + uiHigh = --uiTblSize; + uiLow = 0; + for (;;) + { + uiMid = (uiLow + uiHigh) / 2; + + uiTblType = m_ppSortedByTagTypeAndNum [uiMid]->uiType; + uiTblTagNum = m_ppSortedByTagTypeAndNum [uiMid]->uiTagNum; + if (uiTblType == uiType && uiTagNum == uiTblTagNum) + { + + // Found Match + + pTagInfo = m_ppSortedByTagTypeAndNum [uiMid]; + if (puiInsertPos) + { + *puiInsertPos = uiMid; + } + goto Exit; + } + + // Check if we are done + + if (uiLow >= uiHigh) + { + + // Done, item not found + + if (puiInsertPos) + { + *puiInsertPos = (uiType < uiTblType || + (uiType == uiTblType && uiTagNum < uiTblTagNum)) + ? uiMid + : uiMid + 1; + } + goto Exit; + } + + if (uiType < uiTblType || + (uiType == uiTblType && uiTagNum < uiTblTagNum)) + { + if (uiMid == 0) + { + if (puiInsertPos) + { + *puiInsertPos = 0; + } + goto Exit; + } + uiHigh = uiMid - 1; + } + else + { + if (uiMid == uiTblSize) + { + if (puiInsertPos) + { + *puiInsertPos = uiMid + 1; + } + goto Exit; + } + uiLow = uiMid + 1; + } + } + +Exit: + + return( pTagInfo); +} + +/**************************************************************************** +Desc: Lookup a tag by tag type and tag name. Tag name is passed in as a + UNICODE string or a NATIVE string. If a non-NULL UNICODE string is + passed in, it will be used. Otherwise, the NATIVE string will be used. +****************************************************************************/ +FLM_TAG_INFO * F_NameTable::findTagByTypeAndName( + FLMUINT uiType, + const FLMUNICODE * puzTagName, + const char * pszTagName, + FLMBOOL bMatchNamespace, + const FLMUNICODE * puzNamespace, + FLMBOOL * pbAmbiguous, + FLMUINT * puiInsertPos) +{ + FLM_TAG_INFO * pTagInfo = NULL; + FLMUINT uiTblType; + FLMUINT uiTblSize; + FLMUINT uiLow; + FLMUINT uiMid; + FLMUINT uiHigh; + FLMINT iCmp; + + if (!m_bTablesSorted) + { + sortTags(); + } + + // Do binary search in the table + + *pbAmbiguous = FALSE; + if ((uiTblSize = m_uiNumTags) == 0) + { + if (puiInsertPos) + { + *puiInsertPos = 0; + } + goto Exit; + } + uiHigh = --uiTblSize; + uiLow = 0; + for (;;) + { + uiMid = (uiLow + uiHigh) / 2; + uiTblType = m_ppSortedByTagTypeAndName [uiMid]->uiType; + if (uiType < uiTblType) + { + iCmp = -1; + } + else if (uiType > uiTblType) + { + iCmp = 1; + } + else if ((iCmp = tagNameCompare( puzTagName, pszTagName, + m_ppSortedByTagTypeAndName [uiMid]->puzTagName)) == 0) + { + if (!bMatchNamespace) + { + + // Better not be trying to insert a new one in this case! + + flmAssert( puiInsertPos == NULL); + + if ((uiMid && + tagNameCompare( puzTagName, pszTagName, + m_ppSortedByTagTypeAndName [uiMid-1]->puzTagName) == 0) || + (uiMid < m_uiNumTags - 1 && + tagNameCompare( puzTagName, pszTagName, + m_ppSortedByTagTypeAndName [uiMid+1]->puzTagName) == 0)) + { + *pbAmbiguous = TRUE; + } + } + else + { + iCmp = tagNameCompare( puzNamespace, NULL, + m_ppSortedByTagTypeAndName [uiMid]->puzNamespace); + } + + if (iCmp == 0) + { + // Found Match + + pTagInfo = m_ppSortedByTagTypeAndName [uiMid]; + if (puiInsertPos) + { + *puiInsertPos = uiMid; + } + + goto Exit; + } + } + + // Check if we are done + + if (uiLow >= uiHigh) + { + + // Done, item not found + + if (puiInsertPos) + { + *puiInsertPos = (iCmp < 0) + ? uiMid + : uiMid + 1; + } + goto Exit; + } + + if (iCmp < 0) + { + if (uiMid == 0) + { + if (puiInsertPos) + { + *puiInsertPos = 0; + } + goto Exit; + } + uiHigh = uiMid - 1; + } + else + { + if (uiMid == uiTblSize) + { + if (puiInsertPos) + { + *puiInsertPos = uiMid + 1; + } + goto Exit; + } + uiLow = uiMid + 1; + } + } + +Exit: + + return( pTagInfo); +} + +/**************************************************************************** +Desc: Copy a tag name to a UNICODE or NATIVE buffer. Truncate if necessary. + If a non-NULL UNICODE string is passed in, it will be populated. + Otherwise, the NATIVE string will be populated. +****************************************************************************/ +RCODE F_NameTable::copyTagName( + FLMUNICODE * puzDestTagName, + char * pszDestTagName, + FLMUINT * puiDestBufSize, // Bytes, must be enough for null terminator + FLMUNICODE * puzSrcTagName, // May be NULL + FLMBOOL bTruncatedNamesOk) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiDestCharCnt = *puiDestBufSize; + FLMUINT uiCharCnt; + + if (puzDestTagName) + { + + // Decrement name buffer size by sizeof( FLMUNICODE) to allow for a + // terminating NULL character. uiDestChars better be at least big + // enough for a null terminating character. + + flmAssert( uiDestCharCnt >= sizeof( FLMUNICODE)); + uiDestCharCnt /= sizeof( FLMUNICODE); + uiDestCharCnt--; + + if (puzSrcTagName) + { + + // Copy the name to the UNICODE buffer. + + uiCharCnt = 0; + while (uiCharCnt < uiDestCharCnt && *puzSrcTagName) + { + *puzDestTagName++ = *puzSrcTagName; + uiCharCnt++; + puzSrcTagName++; + } + *puzDestTagName = 0; + *puiDestBufSize = uiCharCnt; + if (!bTruncatedNamesOk && *puzSrcTagName) + { + rc = RC_SET( NE_FLM_CONV_DEST_OVERFLOW); + goto Exit; + } + } + else + { + *puzDestTagName = 0; + *puiDestBufSize = 0; + } + } + else + { + + // Decrement name buffer size by one to allow for a terminating + // 0 character. uiDestCharCnt better be at list big + // enough for a 0 terminating character. + + flmAssert( uiDestCharCnt); + uiDestCharCnt--; + + if (puzSrcTagName) + { + // Copy the name to the NATIVE buffer. Non-Ascii UNICODE characters + // will cause NE_FLM_CONV_ILLEGAL to be returned. + + uiCharCnt = 0; + while (uiCharCnt < uiDestCharCnt && *puzSrcTagName) + { + if (*puzSrcTagName <= 0xFF) + { + *pszDestTagName++ = + (char)f_tonative( (FLMBYTE)*puzSrcTagName); + uiCharCnt++; + puzSrcTagName++; + } + else + { + rc = RC_SET( NE_FLM_CONV_ILLEGAL); + goto Exit; + } + + } + *pszDestTagName = 0; + *puiDestBufSize = uiCharCnt; + if (!bTruncatedNamesOk && *puzSrcTagName) + { + rc = RC_SET( NE_FLM_CONV_DEST_OVERFLOW); + goto Exit; + } + } + else + { + *pszDestTagName = 0; + *puiDestBufSize = 0; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Swap two entries in tag info table during sort. +*****************************************************************************/ +FINLINE void tagInfoSwap( + FLM_TAG_INFO * * ppTagInfoTbl, + FLMUINT uiPos1, + FLMUINT uiPos2 + ) +{ + FLM_TAG_INFO * pTmpTagInfo = ppTagInfoTbl [uiPos1]; + ppTagInfoTbl [uiPos1] = ppTagInfoTbl [uiPos2]; + ppTagInfoTbl [uiPos2] = pTmpTagInfo; +} + +/*************************************************************************** +Desc: Comparison function for sorting by tag type and name. +****************************************************************************/ +FSTATIC FLMINT compareTagTypeAndName( + FLM_TAG_INFO * pTagInfo1, + FLM_TAG_INFO * pTagInfo2) +{ + FLMINT iCmp; + + if (pTagInfo1->uiType < pTagInfo2->uiType) + { + return( -1); + } + else if (pTagInfo1->uiType > pTagInfo2->uiType) + { + return( 1); + } + else if ((iCmp = tagNameCompare( pTagInfo1->puzTagName, NULL, + pTagInfo2->puzTagName)) != 0) + { + return( iCmp); + } + else + { + return( tagNameCompare( pTagInfo1->puzNamespace, NULL, + pTagInfo2->puzNamespace)); + } +} + +/*************************************************************************** +Desc: Comparison function for sorting by tag type and number. +****************************************************************************/ +FSTATIC FLMINT compareTagTypeAndNum( + FLM_TAG_INFO * pTagInfo1, + FLM_TAG_INFO * pTagInfo2 + ) +{ + if (pTagInfo1->uiType < pTagInfo2->uiType) + { + return( -1); + } + else if (pTagInfo1->uiType > pTagInfo2->uiType) + { + return( 1); + } + else if (pTagInfo1->uiTagNum < pTagInfo2->uiTagNum) + { + return( -1); + } + else if (pTagInfo1->uiTagNum > pTagInfo2->uiTagNum) + { + return( 1); + } + else + { + return( 0); + } +} + +/*************************************************************************** +Desc: Sort an array of SCACHE pointers by their block address. +****************************************************************************/ +FSTATIC void sortTagTbl( + FLM_TAG_INFO * * ppTagInfoTbl, + FLMUINT uiLowerBounds, + FLMUINT uiUpperBounds, + TAG_COMPARE_FUNC fnTagCompare) +{ + FLMUINT uiLBPos; + FLMUINT uiUBPos; + FLMUINT uiMIDPos; + FLMUINT uiLeftItems; + FLMUINT uiRightItems; + FLM_TAG_INFO * pCurTagInfo; + FLMINT iCompare; + +Iterate_Larger_Half: + + uiUBPos = uiUpperBounds; + uiLBPos = uiLowerBounds; + uiMIDPos = (uiUpperBounds + uiLowerBounds + 1) / 2; + pCurTagInfo = ppTagInfoTbl [uiMIDPos ]; + for (;;) + { + while (uiLBPos == uiMIDPos || // Don't compare with target + ((iCompare = + fnTagCompare( ppTagInfoTbl [uiLBPos], pCurTagInfo)) < 0)) + { + if (uiLBPos >= uiUpperBounds) + { + break; + } + uiLBPos++; + } + + while (uiUBPos == uiMIDPos || // Don't compare with target + (((iCompare = + fnTagCompare( pCurTagInfo, ppTagInfoTbl [uiUBPos])) < 0))) + { + if (!uiUBPos) + { + break; + } + uiUBPos--; + } + + if (uiLBPos < uiUBPos ) // Interchange and continue loop. + { + + // Exchange [uiLBPos] with [uiUBPos]. + + tagInfoSwap( ppTagInfoTbl, uiLBPos, uiUBPos); + uiLBPos++; // Scan from left to right. + uiUBPos--; // Scan from right to left. + } + else // Past each other - done + { + break; + } + } + + // Check for swap( LB, MID ) - cases 3 and 4 + + if( uiLBPos < uiMIDPos ) + { + + // Exchange [uiLBPos] with [uiMIDPos] + + tagInfoSwap( ppTagInfoTbl, uiMIDPos, uiLBPos); + uiMIDPos = uiLBPos; + } + else if( uiMIDPos < uiUBPos ) + { + + // Exchange [uUBPos] with [uiMIDPos] + + tagInfoSwap( ppTagInfoTbl, uiMIDPos, uiUBPos); + uiMIDPos = uiUBPos; + } + + // Check the left piece. + + uiLeftItems = (uiLowerBounds + 1 < uiMIDPos ) + ? uiMIDPos - uiLowerBounds // 2 or more + : 0; + uiRightItems = (uiMIDPos + 1 < uiUpperBounds ) + ? uiUpperBounds - uiMIDPos // 2 or more + : 0; + + if( uiLeftItems < uiRightItems ) + { + + // Recurse on the LEFT side and goto the top on the RIGHT side. + + if (uiLeftItems ) + { + sortTagTbl( ppTagInfoTbl, uiLowerBounds, uiMIDPos - 1, fnTagCompare); + } + uiLowerBounds = uiMIDPos + 1; + goto Iterate_Larger_Half; + } + else if (uiLeftItems ) // Compute a truth table to figure out this check. + { + + // Recurse on the RIGHT side and goto the top for the LEFT side. + + if (uiRightItems ) + { + sortTagTbl( ppTagInfoTbl, uiMIDPos + 1, uiUpperBounds, fnTagCompare); + } + uiUpperBounds = uiMIDPos - 1; + goto Iterate_Larger_Half; + } +} + +/**************************************************************************** +Desc: Lookup a namespace - so we can reuse the memory. +****************************************************************************/ +FLMUNICODE * F_NameTable::findNamespace( + FLMUNICODE * puzNamespace, + FLMUINT * puiInsertPos) +{ + FLMUNICODE * puzFoundNamespace = NULL; + FLMUINT uiTblSize; + FLMUINT uiLow; + FLMUINT uiMid; + FLMUINT uiHigh; + FLMINT iCmp; + + // Do binary search in the table + + if ((uiTblSize = m_uiNumNamespaces) == 0) + { + if (puiInsertPos) + { + *puiInsertPos = 0; + } + goto Exit; + } + + uiHigh = --uiTblSize; + uiLow = 0; + for (;;) + { + uiMid = (uiLow + uiHigh) / 2; + + if ((iCmp = tagNameCompare( puzNamespace, NULL, + m_ppuzNamespaces [uiMid])) == 0) + { + + // Found Match + + puzFoundNamespace = m_ppuzNamespaces [uiMid]; + if (puiInsertPos) + { + *puiInsertPos = uiMid; + } + goto Exit; + } + + // Check if we are done + + if (uiLow >= uiHigh) + { + + // Done, item not found + + if (puiInsertPos) + { + *puiInsertPos = (iCmp < 0) + ? uiMid + : uiMid + 1; + } + goto Exit; + } + + if (iCmp < 0) + { + if (uiMid == 0) + { + if (puiInsertPos) + { + *puiInsertPos = 0; + } + goto Exit; + } + uiHigh = uiMid - 1; + } + else + { + if (uiMid == uiTblSize) + { + if (puiInsertPos) + { + *puiInsertPos = uiMid + 1; + } + goto Exit; + } + uiLow = uiMid + 1; + } + } + +Exit: + + return( puzFoundNamespace); +} + +/**************************************************************************** +Desc: Insert a tag info structure into the sorted tables at the specified + positions. +****************************************************************************/ +RCODE F_NameTable::insertNamespace( + FLMUNICODE * puzNamespace, + FLMUINT uiInsertPos) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiLoop; + + // See if we need to resize the table. Add 32 each time. There + // should not be that many different namespaces. + + if (m_uiNumNamespaces == m_uiNamespaceTblSize) + { + FLMUINT uiNewSize = m_uiNamespaceTblSize + 32; + FLMUNICODE ** ppNewTbl; + + if( RC_BAD( rc = f_alloc( sizeof( FLMUNICODE *) * uiNewSize, &ppNewTbl))) + { + goto Exit; + } + + // Copy the old tables into the new. + + if (m_uiNumNamespaces) + { + f_memcpy( ppNewTbl, m_ppuzNamespaces, + sizeof( FLMUNICODE *) * m_uiNumNamespaces); + f_free( &m_ppuzNamespaces); + } + m_ppuzNamespaces = ppNewTbl; + m_uiNamespaceTblSize = uiNewSize; + } + + // Insert the new namespace into the table. + + uiLoop = m_uiNumNamespaces; + while (uiLoop > uiInsertPos) + { + m_ppuzNamespaces [uiLoop] = m_ppuzNamespaces [uiLoop - 1]; + uiLoop--; + } + m_ppuzNamespaces [uiInsertPos] = puzNamespace; + + // Increment the total number of namespaces + + m_uiNumNamespaces++; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Allocate a new tag info structure and set it up. +****************************************************************************/ +RCODE F_NameTable::allocTag( + FLMUINT uiType, + FLMUNICODE * puzTagName, + const char * pszTagName, + FLMUINT uiTagNum, + FLMUINT uiDataType, + FLMUNICODE * puzNamespace, + FLM_TAG_INFO ** ppTagInfo) +{ + RCODE rc = NE_FLM_OK; + void * pvMark; + FLMUINT uiSaveMemoryAllocated; + FLM_TAG_INFO * pTagInfo; + FLMUINT uiNameSize; + FLMUNICODE * puzTmp; + FLMUNICODE * puzTblNamespace; + FLMUINT uiNamespaceInsertPos; + + // Create a new tag info structure. + + pvMark = m_pool.poolMark(); + uiSaveMemoryAllocated = m_uiMemoryAllocated; + if (RC_BAD( rc = m_pool.poolCalloc( sizeof( FLM_TAG_INFO), + (void **)&pTagInfo))) + { + goto Exit; + } + m_uiMemoryAllocated += sizeof( FLM_TAG_INFO); + + // Allocate the space for the tag name. + + if (puzTagName) + { + uiNameSize = (f_unilen( puzTagName) + 1) * sizeof( FLMUNICODE); + if (RC_BAD( rc = m_pool.poolAlloc( uiNameSize, + (void **)&pTagInfo->puzTagName))) + { + goto Exit; + } + m_uiMemoryAllocated += uiNameSize; + f_memcpy( pTagInfo->puzTagName, puzTagName, uiNameSize); + } + else + { + uiNameSize = (f_strlen( pszTagName) + 1) * sizeof( FLMUNICODE); + if (RC_BAD( rc = m_pool.poolAlloc( uiNameSize, + (void **)&pTagInfo->puzTagName))) + { + goto Exit; + } + m_uiMemoryAllocated += uiNameSize; + puzTmp = pTagInfo->puzTagName; + while (*pszTagName) + { + *puzTmp++ = (FLMUNICODE)*pszTagName; + pszTagName++; + } + *puzTmp = 0; + } + pTagInfo->uiType = uiType; + pTagInfo->uiTagNum = uiTagNum; + pTagInfo->uiDataType = uiDataType; + + if (puzNamespace && *puzNamespace) + { + + // See if we have already allocated the namespace. If so, just + // point to it. Otherwise, allocate it and add it to the + // namespace table and then point to it. + + if ((puzTblNamespace = findNamespace( puzNamespace, + &uiNamespaceInsertPos)) == NULL) + { + uiNameSize = (f_unilen( puzNamespace) + 1) * sizeof( FLMUNICODE); + if (RC_BAD( rc = m_pool.poolAlloc( uiNameSize, + (void **)&puzTblNamespace))) + { + goto Exit; + } + m_uiMemoryAllocated += uiNameSize; + f_memcpy( puzTblNamespace, puzNamespace, uiNameSize); + if (RC_BAD( rc = insertNamespace( puzTblNamespace, + uiNamespaceInsertPos))) + { + goto Exit; + } + + // Need to re-mark the pool after this point, because + // we can now not afford to lose the namespace that was + // allocated if the pool is reset at Exit due to a later + // error. + + pvMark = m_pool.poolMark(); + uiSaveMemoryAllocated = m_uiMemoryAllocated; + } + + pTagInfo->puzNamespace = puzTblNamespace; + } + +Exit: + + if (RC_BAD( rc)) + { + m_pool.poolReset( pvMark); + m_uiMemoryAllocated = uiSaveMemoryAllocated; + pTagInfo = NULL; + } + *ppTagInfo = pTagInfo; + + return( rc); +} + +/**************************************************************************** +Desc: Allocate the sort tables. +****************************************************************************/ +RCODE F_NameTable::reallocSortTables( + FLMUINT uiNewTblSize) +{ + RCODE rc = NE_FLM_OK; + FLM_TAG_INFO ** ppNewTbl; + + if( RC_BAD( f_alloc( + sizeof( FLM_TAG_INFO *) * uiNewTblSize * 2, &ppNewTbl))) + { + goto Exit; + } + + // Copy the old tables into the new. + + if (m_uiNumTags) + { + f_memcpy( ppNewTbl, m_ppSortedByTagTypeAndName, + sizeof( FLM_TAG_INFO *) * m_uiNumTags); + f_memcpy( &ppNewTbl [uiNewTblSize], + m_ppSortedByTagTypeAndNum, + sizeof( FLM_TAG_INFO *) * m_uiNumTags); + f_free( &m_ppSortedByTagTypeAndName); + } + m_ppSortedByTagTypeAndName = ppNewTbl; + m_ppSortedByTagTypeAndNum = &ppNewTbl [uiNewTblSize]; + + m_uiTblSize = uiNewTblSize; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Get a tag name, number, etc. using type + tag number ordering. + Tag name is returned as a UNICODE string or NATIVE string. If a + non-NULL UNICODE string is passed in, it will be used. + Otherwise, the NATIVE string will be used. +****************************************************************************/ +RCODE FLMAPI F_NameTable::getNextTagTypeAndNumOrder( + FLMUINT uiType, + FLMUINT * puiNextPos, + FLMUNICODE * puzTagName, // May be NULL + char * pszTagName, // May be NULL + FLMUINT uiNameBufSize, + FLMUINT * puiTagNum, // May be NULL + FLMUINT * puiDataType, // May be NULL + FLMUNICODE * puzNamespace, // May be NULL + FLMUINT uiNamespaceBufSize, + FLMBOOL bTruncatedNamesOk) +{ + RCODE rc = NE_FLM_OK; + FLM_TAG_INFO * pTagInfo = NULL; + FLMBOOL bFound = FALSE; + + if (!m_bTablesSorted) + { + sortTags(); + } + + while (*puiNextPos < m_uiNumTags) + { + pTagInfo = m_ppSortedByTagTypeAndNum [*puiNextPos]; + if (pTagInfo->uiType == uiType) + { + bFound = TRUE; + if (puiTagNum) + { + *puiTagNum = pTagInfo->uiTagNum; + } + if( puzTagName || pszTagName) + { + if (RC_BAD( rc = copyTagName( puzTagName, pszTagName, + &uiNameBufSize, + pTagInfo->puzTagName, bTruncatedNamesOk))) + { + goto Exit; + } + } + + if (puiDataType) + { + *puiDataType = pTagInfo->uiDataType; + } + + if (puzNamespace) + { + if (RC_BAD( rc = copyTagName( puzNamespace, NULL, + &uiNamespaceBufSize, + pTagInfo->puzNamespace, + bTruncatedNamesOk))) + { + goto Exit; + } + } + + // Returned *puiNextPos should be the next one to retrieve. + + (*puiNextPos)++; + break; + } + else if (pTagInfo->uiType > uiType) + { + break; + } + else + { + (*puiNextPos)++; + } + } + + if (!bFound) + { + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Get a tag name, number, etc. using type + tag name ordering. + Tag name is returned as a UNICODE string or NATIVE string. If a + non-NULL UNICODE string is passed in, it will be used. + Otherwise, the NATIVE string will be used. +****************************************************************************/ +RCODE FLMAPI F_NameTable::getNextTagTypeAndNameOrder( + FLMUINT uiType, + FLMUINT * puiNextPos, + FLMUNICODE * puzTagName, // May be NULL + char * pszTagName, // May be NULL + FLMUINT uiNameBufSize, + FLMUINT * puiTagNum, // May be NULL + FLMUINT * puiDataType, // May be NULL + FLMUNICODE * puzNamespace, // May be NULL + FLMUINT uiNamespaceBufSize, + FLMBOOL bTruncatedNamesOk) +{ + RCODE rc = NE_FLM_OK; + FLM_TAG_INFO * pTagInfo = NULL; + FLMBOOL bFound = FALSE; + + if (!m_bTablesSorted) + { + sortTags(); + } + + while (*puiNextPos < m_uiNumTags) + { + pTagInfo = m_ppSortedByTagTypeAndName [*puiNextPos]; + + if (pTagInfo->uiType == uiType) + { + bFound = TRUE; + if (puiTagNum) + { + *puiTagNum = pTagInfo->uiTagNum; + } + if (puzTagName || pszTagName) + { + if (RC_BAD( rc = copyTagName( puzTagName, pszTagName, + &uiNameBufSize, pTagInfo->puzTagName, + bTruncatedNamesOk))) + { + goto Exit; + } + } + + if (puiDataType) + { + *puiDataType = pTagInfo->uiDataType; + } + + if (puzNamespace) + { + if (RC_BAD( rc = copyTagName( puzNamespace, NULL, + &uiNamespaceBufSize, + pTagInfo->puzNamespace, + bTruncatedNamesOk))) + { + goto Exit; + } + } + + // Returned *puiNextPos should be the next one to retrieve. + + (*puiNextPos)++; + break; + } + else if (pTagInfo->uiType > uiType) + { + break; + } + else + { + (*puiNextPos)++; + } + } + if (!bFound) + { + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Get a tag name from its tag type and number. Tag name is returned as a + UNICODE string or NATIVE string. If a non-NULL UNICODE string is passed + in, it will be used. Otherwise, the NATIVE string will be used. +****************************************************************************/ +RCODE FLMAPI F_NameTable::getFromTagTypeAndNum( + FLMUINT uiType, + FLMUINT uiTagNum, + FLMUNICODE * puzTagName, // May be NULL + char * pszTagName, // May be NULL + FLMUINT * puiNameBufSize, // May be NULL, returns # characters + FLMUINT * puiDataType, // May be NULL + FLMUNICODE * puzNamespace, // May be NULL + char * pszNamespace, // May be NULL + FLMUINT * puiNamespaceBufSize, // May be NULL, returns # characters + FLMBOOL bTruncatedNamesOk) +{ + RCODE rc = NE_FLM_OK; + FLM_TAG_INFO * pTagInfo; + FLMUNICODE * puzTmpNamespace = NULL; + FLMUNICODE * puzTmpName = NULL; + + if ((pTagInfo = findTagByTypeAndNum( uiType, uiTagNum)) != NULL) + { + if( puzTagName || pszTagName) + { + if (RC_BAD( rc = copyTagName( puzTagName, pszTagName, + puiNameBufSize, + pTagInfo->puzTagName, bTruncatedNamesOk))) + { + goto Exit; + } + } + else if (puiNameBufSize) + { + *puiNameBufSize = f_unilen( pTagInfo->puzTagName); + } + + if (puiDataType) + { + *puiDataType = pTagInfo->uiDataType; + } + + if (puzNamespace || pszNamespace) + { + if (RC_BAD( rc = copyTagName( puzNamespace, pszNamespace, + puiNamespaceBufSize, + pTagInfo->puzNamespace, + bTruncatedNamesOk))) + { + goto Exit; + } + } + else if (puiNamespaceBufSize) + { + *puiNamespaceBufSize = f_unilen( pTagInfo->puzNamespace); + } + } + else + { + rc = RC_SET( NE_FLM_NOT_FOUND); + goto Exit; + } + +Exit: + + if (puzTmpName) + { + f_free( &puzTmpName); + } + + if (puzTmpNamespace) + { + f_free( &puzTmpNamespace); + } + + return( rc); +} + +/**************************************************************************** +Desc: Get a tag number from its tag name and type. Tag name is passed + in as a UNICODE or NATIVE string. If a non-NULL UNICODE string is + passed in, it will be used. Otherwise, the NATIVE string will + be used. +****************************************************************************/ +RCODE FLMAPI F_NameTable::getFromTagTypeAndName( + FLMUINT uiType, + const FLMUNICODE * puzTagName, + const char * pszTagName, + FLMBOOL bMatchNamespace, + const FLMUNICODE * puzNamespace, + FLMUINT * puiTagNum, // May be NULL + FLMUINT * puiDataType) // May be NULL +{ + RCODE rc = NE_FLM_OK; + FLM_TAG_INFO * pTagInfo; + FLMBOOL bAmbiguous; + + if ((pTagInfo = findTagByTypeAndName( uiType, + puzTagName, pszTagName, bMatchNamespace, + puzNamespace, + &bAmbiguous)) != NULL) + { + if (puiTagNum) + { + *puiTagNum = pTagInfo->uiTagNum; + } + + if (puiDataType) + { + *puiDataType = pTagInfo->uiDataType; + } + + if (bAmbiguous) + { + rc = RC_SET( NE_FLM_MULTIPLE_MATCHES); + goto Exit; + } + } + else + { + rc = RC_SET( NE_FLM_NOT_FOUND); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Insert a tag info structure into the sorted tables at the specified + positions. +****************************************************************************/ +RCODE F_NameTable::insertTagInTables( + FLM_TAG_INFO * pTagInfo, + FLMUINT uiTagTypeAndNameTblInsertPos, + FLMUINT uiTagTypeAndNumTblInsertPos) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiLoop; + + // See if we need to resize the tables. Start at 256. Double each + // time up to 2048. Then just add 2048 at a time. + + if (m_uiNumTags == m_uiTblSize) + { + FLMUINT uiNewSize; + + if (!m_uiTblSize) + { + uiNewSize = 256; + } + else if (m_uiTblSize < 2048) + { + uiNewSize = m_uiTblSize * 2; + } + else + { + uiNewSize = m_uiTblSize + 2048; + } + + if (RC_BAD( rc = reallocSortTables( uiNewSize))) + { + goto Exit; + } + } + + // Insert into the sorted-by-tag-type-and-name table + + uiLoop = m_uiNumTags; + while (uiLoop > uiTagTypeAndNameTblInsertPos) + { + m_ppSortedByTagTypeAndName [uiLoop] = + m_ppSortedByTagTypeAndName [uiLoop - 1]; + uiLoop--; + } + m_ppSortedByTagTypeAndName [uiTagTypeAndNameTblInsertPos] = pTagInfo; + + // Insert into the sorted-by-tag-type-and-num table + + uiLoop = m_uiNumTags; + while (uiLoop > uiTagTypeAndNumTblInsertPos) + { + m_ppSortedByTagTypeAndNum [uiLoop] = + m_ppSortedByTagTypeAndNum [uiLoop - 1]; + uiLoop--; + } + m_ppSortedByTagTypeAndNum [uiTagTypeAndNumTblInsertPos] = pTagInfo; + + // Increment the total number of tags + + m_uiNumTags++; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Add a tag to the table. Tag name is passed in as a UNICODE string or + NATIVE string. If a non-NULL UNICODE string is passed in, it will + be used. Otherwise, the NATIVE string will be used. +****************************************************************************/ +RCODE FLMAPI F_NameTable::addTag( + FLMUINT uiType, + FLMUNICODE * puzTagName, + const char * pszTagName, + FLMUINT uiTagNum, + FLMUINT uiDataType, + FLMUNICODE * puzNamespace, + FLMBOOL bCheckDuplicates) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiTagTypeAndNameTblInsertPos; + FLMUINT uiTagTypeAndNumTblInsertPos; + FLM_TAG_INFO * pTagInfo; + FLMBOOL bAmbiguous; + + // Must have a non-NULL tag name. Use UNICODE string if it is + // non-NULL. Otherwise, use NATIVE string. + + if (puzTagName && *puzTagName) + { + pszTagName = NULL; + } + else if (pszTagName && *pszTagName) + { + puzTagName = NULL; + } + else + { + rc = RC_SET_AND_ASSERT( NE_FLM_INVALID_PARM); + goto Exit; + } + + // Tag number of zero not allowed. + + if (!uiTagNum) + { + rc = RC_SET_AND_ASSERT( NE_FLM_INVALID_PARM); + goto Exit; + } + + // Tables must be sorted in order for this to work + + if (bCheckDuplicates) + { + + // Make sure that the tag type + name is not already used. + + if (findTagByTypeAndName( uiType, puzTagName, pszTagName, + TRUE, puzNamespace, + &bAmbiguous, &uiTagTypeAndNameTblInsertPos)) + { + rc = RC_SET( NE_FLM_EXISTS); + goto Exit; + } + + // Make sure that the tag type + number is not already used. + + if (findTagByTypeAndNum( uiType, uiTagNum, + &uiTagTypeAndNumTblInsertPos)) + { + rc = RC_SET( NE_FLM_EXISTS); + goto Exit; + } + } + else + { + uiTagTypeAndNameTblInsertPos = uiTagTypeAndNumTblInsertPos = m_uiNumTags; + m_bTablesSorted = FALSE; + } + + // Create a new tag info structure. + + if (RC_BAD( rc = allocTag( uiType, puzTagName, pszTagName, uiTagNum, + uiDataType, puzNamespace, &pTagInfo))) + { + goto Exit; + } + + // Insert the tag structure into the appropriate places in the + // sorted tables. + + if (RC_BAD( rc = insertTagInTables( pTagInfo, + uiTagTypeAndNameTblInsertPos, + uiTagTypeAndNumTblInsertPos))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Sort the tag tables according to their respective criteria. +****************************************************************************/ +void F_NameTable::sortTags( void) +{ + if (!m_bTablesSorted && m_uiNumTags > 1) + { + sortTagTbl( m_ppSortedByTagTypeAndName, 0, m_uiNumTags - 1, + compareTagTypeAndName); + sortTagTbl( m_ppSortedByTagTypeAndNum, 0, m_uiNumTags - 1, + compareTagTypeAndNum); + } + m_bTablesSorted = TRUE; +} + +/**************************************************************************** +Desc: Remove a tag from the table +****************************************************************************/ +void FLMAPI F_NameTable::removeTag( + FLMUINT uiType, + FLMUINT uiTagNum) +{ + FLM_TAG_INFO * pTagInfo; + FLMUINT uiTagTypeAndNameTblPos; + FLMUINT uiTagTypeAndNumTblPos; + FLMBOOL bAmbiguous; + FLMBOOL bMatchNamespace; + FLMUNICODE * puzNamespace; + + if ((pTagInfo = findTagByTypeAndNum( uiType, uiTagNum, + &uiTagTypeAndNumTblPos)) != NULL) + { + puzNamespace = pTagInfo->puzNamespace; + bMatchNamespace = TRUE; + + if (findTagByTypeAndName( uiType, pTagInfo->puzTagName, + NULL, bMatchNamespace, + puzNamespace, &bAmbiguous, + &uiTagTypeAndNameTblPos) == NULL) + { + + // It should have been in the name table too! + + flmAssert( 0); + } + + // Shift everything in the sorted number table that is above + // the found position down one. + + if (uiTagTypeAndNumTblPos < m_uiNumTags - 1) + { + f_memmove( &m_ppSortedByTagTypeAndNum [uiTagTypeAndNumTblPos], + &m_ppSortedByTagTypeAndNum [uiTagTypeAndNumTblPos + 1], + sizeof( FLM_TAG_INFO *) * + (m_uiNumTags - 1 - uiTagTypeAndNumTblPos)); + } + + // Shift everything in the sorted name table that is above + // the found position down one. + + if (uiTagTypeAndNameTblPos < m_uiNumTags - 1) + { + f_memmove( &m_ppSortedByTagTypeAndName [uiTagTypeAndNameTblPos], + &m_ppSortedByTagTypeAndName [uiTagTypeAndNameTblPos + 1], + sizeof( FLM_TAG_INFO *) * + (m_uiNumTags - 1 - uiTagTypeAndNameTblPos)); + } + m_uiNumTags--; + } +} + +/**************************************************************************** +Desc: Clone a name table from another one +****************************************************************************/ +#if 0 +RCODE FLMAPI F_NameTable::cloneNameTable( + IF_NameTable * pSrcNameTable) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiLoop; + FLM_TAG_INFO * pTagInfo; + FLMUINT uiPoolBlkSize; + + // Set the pool size to be as optimal as possible. + + uiPoolBlkSize = pSrcNameTable->m_uiMemoryAllocated / 8; + if (uiPoolBlkSize < 1024) + { + uiPoolBlkSize = 1024; + } + else if (uiPoolBlkSize > 65536) + { + uiPoolBlkSize = 65536; + } + clearTable( uiPoolBlkSize); + + // Pre-allocate exactly enough table space + + if (RC_BAD( rc = reallocSortTables( pSrcNameTable->m_uiNumTags))) + { + goto Exit; + } + + // Add all of the tags + + for (uiLoop = 0; uiLoop < pSrcNameTable->m_uiNumTags; uiLoop++) + { + pTagInfo = pSrcNameTable->m_ppSortedByTagTypeAndNum [uiLoop]; + if (RC_BAD( rc = addTag( pTagInfo->uiType, pTagInfo->puzTagName, NULL, + pTagInfo->uiTagNum, + pTagInfo->uiDataType, + pTagInfo->puzNamespace, FALSE))) + { + goto Exit; + } + } + + sortTags(); + +Exit: + + return( rc); +} +#endif + +/**************************************************************************** +Desc: Copy a name table from another one. This differs from cloneNameTable + in that it doesn't clear the name table, it just copies the names. + Thus, the destination name table may already have names in it, and the + names from the source table will just be added. +****************************************************************************/ +#if 0 +RCODE FLMAPI F_NameTable::importFromNameTable( + IF_NameTable * pSrcNameTable) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiLoop; + FLM_TAG_INFO * pTagInfo; + + // Pre-allocate exactly enough table space + + if (RC_BAD( rc = reallocSortTables( m_uiNumTags + + pSrcNameTable->m_uiNumTags))) + { + goto Exit; + } + + // Add all of the tags from the source table + + for (uiLoop = 0; uiLoop < pSrcNameTable->m_uiNumTags; uiLoop++) + { + pTagInfo = pSrcNameTable->m_ppSortedByTagTypeAndNum [uiLoop]; + if (RC_BAD( rc = addTag( pTagInfo->uiType, pTagInfo->puzTagName, NULL, + pTagInfo->uiTagNum, + pTagInfo->uiDataType, + pTagInfo->puzNamespace, FALSE))) + { + if (rc != NE_FLM_EXISTS) + { + goto Exit; + } + else + { + rc = NE_FLM_OK; + } + } + } + + sortTags(); + +Exit: + + return( rc); +} +#endif + +/**************************************************************************** +Desc: Increment use count on this object. +****************************************************************************/ +FLMINT FLMAPI F_NameTable::AddRef( void) +{ + return( f_atomicInc( &m_refCnt)); +} + +/**************************************************************************** +Desc: Decrement the use count and delete if use count goes to zero. +****************************************************************************/ +FLMINT FLMAPI F_NameTable::Release( void) +{ + FLMINT iRefCnt; + + if ((iRefCnt = f_atomicDec( &m_refCnt)) == 0) + { + delete this; + } + + return( iRefCnt); +} diff --git a/ftk/src/ftkpath.cpp b/ftk/src/ftkpath.cpp new file mode 100644 index 0000000..f358dfa --- /dev/null +++ b/ftk/src/ftkpath.cpp @@ -0,0 +1,775 @@ +//------------------------------------------------------------------------------ +// Desc: Contains functions for file name/path manipulation +// +// Tabs: 3 +// +// Copyright (c) 1998-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: ftkpath.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +FSTATIC FLMBOOL f_canReducePath( + const char * pszSource); + +FSTATIC const char * f_findFileNameStart( + const char * pszPath); + +FSTATIC char * f_getPathComponent( + char ** ppszPath, + FLMUINT * puiEndChar); + +/**************************************************************************** +Desc: Returns TRUE if character is a "slash" separator +****************************************************************************/ +FINLINE FLMBOOL f_isSlashSeparator( + char cChar) +{ +#ifdef FLM_UNIX + return( cChar == '/' ? TRUE : FALSE); +#else + return( cChar == '/' || cChar == '\\' ? TRUE : FALSE); +#endif +} + +/**************************************************************************** +Desc: Return a pointer to the next path component in ppszPath. +****************************************************************************/ +FSTATIC char * f_getPathComponent( + char ** ppszPath, + FLMUINT * puiEndChar) +{ + char * pszComponent; + char * pszEnd; + + pszComponent = pszEnd = *ppszPath; + if (f_isSlashSeparator( *pszEnd)) + { + // handle the condition of sys:\system the colon would have terminated + // the previous token, to pComponent would now be pointing at the '\'. + // We need to move past the '\' to find the next token. + + pszEnd++; + } + + // Find the end of the path component + + while (*pszEnd) + { + if (f_isSlashSeparator( *pszEnd) +#ifndef FLM_UNIX + || *pszEnd == ':' +#endif + ) + { + break; + } + pszEnd++; + } + + if (*pszEnd) + { + + // A delimiter was found, assume that there is another path component + // after this one. + // Return a pointer to the beginning of the next path component + + *ppszPath = pszEnd + 1; + + *puiEndChar = *pszEnd; + + // NULL terminate the path component + + *pszEnd = 0; + } + else + { + + // There is no "next path component" so return a pointer to the + // NULL-terminator + + *ppszPath = pszEnd; + *puiEndChar = 0; + } + + // Return the path component + + return( pszComponent); +} + +/**************************************************************************** +Desc: Split the path into its components +Output: + pServer - pointer to a buffer to hold the server name + pVolume - pointer to a buffer to hold the volume name + pDirPath - pointer to a buffer to hold the path + pFileName pointer to a buffer to hold the filename + + All of the output parameters are optional. If you do not want one + of the components, simply give a NULL pointer. + +Note: if the input path has no file name, d:\dir_1 for example, then + pass a NULL pointer for pFileName. Otherwise dir_1 will be returned + as pFileName. + + The server name may be ommitted in the input path: + sys:\system\autoexec.ncf + + UNC paths of the form: + \\server-name\volume-name\dir_1\dir_2\file.ext + are supported. + + DOS paths of the form: + d:\dir_1\dir_2\file.ext + are also supported. + +Example: + Given this input: orm-prod48/sys:\system\autoexec.ncf + The output would be: + pServer = "orm-prod48" + pVolume = "sys:" + pDirPath = "\system" + pFileName "autoexec.ncf" +****************************************************************************/ +void FLMAPI F_FileSystem::pathParse( + const char * pszInputPath, + char * pszServer, + char * pszVolume, + char * pszDirPath, + char * pszFileName) +{ + char szInput[ F_PATH_MAX_SIZE]; + char * pszNext; + char * pszColon; + char * pszComponent; + FLMUINT uiEndChar; + FLMBOOL bUNC = FALSE; + + // Initialize return buffers + + if (pszServer) + { + *pszServer = 0; + } + if (pszVolume) + { + *pszVolume = 0; + } + if (pszDirPath) + { + *pszDirPath = 0; + } + if (pszFileName) + { + + // Get the file name + + *pszFileName = 0; + gv_pFileSystem->pathReduce( pszInputPath, szInput, pszFileName); + } + else + { + f_strcpy( szInput, pszInputPath); + } + + // Split out the rest of the components + + pszComponent = &szInput [0]; + + // Is this a UNC path? + + if (szInput[0] == '\\' && szInput[1] == '\\') + { + + // Yes, assume a UNC path + + pszComponent += 2; + bUNC = TRUE; + } + + pszNext = pszColon = pszComponent; + + // Is there a ':' in the szInput path? + + while (*pszColon && *pszColon != ':') + { + pszColon++; + } + if (*pszColon || bUNC) + { + + // Yes, assume there is a volume in the path + + pszComponent = f_getPathComponent( &pszNext, &uiEndChar); + if (uiEndChar != ':') + { + // Assume that this component is the server + + if (pszServer) + { + f_strcpy( pszServer, pszComponent); + } + + // Get the next component + + pszComponent = f_getPathComponent( &pszNext, &uiEndChar); + } + + // Assume that this component is the volume + + if (pszVolume) + { + char * pszSrc = pszComponent; + char * pszDst = pszVolume; + + while (*pszSrc) + { + *pszDst++ = *pszSrc++; + } + *pszDst++ = ':'; + *pszDst = 0; + } + + // For UNC paths, the leading '\' of the path is set to 0 by + // f_getPathComponent. This code restores the leading '\'. + + if (f_isSlashSeparator( (char)uiEndChar)) + { + *(--pszNext) = (char)uiEndChar; + } + } + + // Assume that all that is left of the input is the path + + if (pszDirPath) + { + f_strcpy( pszDirPath, pszNext); + } +} + +/**************************************************************************** +Desc: Will determine whether any format of (UNC, drive based, NetWare + UNC) path can be reduced any further. +****************************************************************************/ +FSTATIC FLMBOOL f_canReducePath( + const char * pszSource) +{ +#if defined FLM_UNIX + F_UNREFERENCED_PARM( pszSource); + return( TRUE); +#else + FLMBOOL bCanReduce; + const char * pszTemp = pszSource; + + // Determine whether the passed path is UNC or not + // (UNC format is: \\FileServer\Volume\Path). + + if (f_strncmp( "\\\\", pszSource, 2 ) == 0) + { + FLMUINT uiSlashCount = 0; + + pszTemp += 2; + + // Search forward for at least two slash separators + // If we find at least two, the path can be reduced. + + bCanReduce = FALSE; + while (*pszTemp) + { + pszTemp++; + if (f_isSlashSeparator( *pszTemp)) + { + ++uiSlashCount; + if (uiSlashCount == 2) + { + bCanReduce = TRUE; + break; + } + } + } + } + else + { + bCanReduce = TRUE; + + // Search forward for the colon. + + while (*pszTemp) + { + if (*pszTemp == ':') + { + + // If nothing comes after the colon, + // we can't reduce any more. + + if (*(pszTemp + 1) == 0) + { + bCanReduce = FALSE; + } + break; + } + pszTemp++; + } + } + + return( bCanReduce); +#endif +} + +/**************************************************************************** +Desc: Return pointer to start of filename part of path. + Search for the last slash separator. +****************************************************************************/ +FSTATIC const char * f_findFileNameStart( + const char * pszPath) +{ + const char * pszFileNameStart; + + pszFileNameStart = pszPath; + while (*pszPath) + { + if (f_isSlashSeparator( *pszPath)) + { + pszFileNameStart = pszPath + 1; + } + pszPath++; + } + return( pszFileNameStart); +} + +/**************************************************************************** +Desc: This function will strip off the filename or trailing + directory of a path. The stripped component of the path will + be placed into the area pointed at by string. The source + path will not be modified. The dest path will contain the + remainder of the stripped path. A stripped path can be processed + repeatedly by this function until there is no more path to reduce. + If the string is set to NULL, the copying of the stripped portion of + the path will be bypassed by the function. + +Notes: This function handles drive based, UNC, Netware, and UNIX type + paths. +****************************************************************************/ +RCODE FLMAPI F_FileSystem::pathReduce( + const char * pszPath, + char * pszDir, + char * pszPathComponent) +{ + RCODE rc = NE_FLM_OK; + const char * pszFileNameStart; + char szLocalPath[ F_PATH_MAX_SIZE]; + FLMUINT uiLen; + + // Check for valid path pointers + + if( !pszPath || !pszDir) + { + rc = RC_SET( NE_FLM_INVALID_PARM); + goto Exit; + } + + if ((uiLen = f_strlen( pszPath)) == 0) + { + rc = RC_SET( NE_FLM_IO_CANNOT_REDUCE_PATH); + goto Exit; + } + + // Trim out any trailing slash separators + + if( f_isSlashSeparator( pszPath [uiLen - 1])) + { + f_strcpy( szLocalPath, pszPath); + + while( f_isSlashSeparator( szLocalPath[ uiLen - 1])) + { + szLocalPath[ --uiLen] = 0; + if( !uiLen) + { + rc = RC_SET( NE_FLM_IO_CANNOT_REDUCE_PATH); + goto Exit; + } + } + + pszPath = szLocalPath; + } + + if( f_canReducePath( pszPath)) + { + // Search for a slash or beginning of path + + pszFileNameStart = f_findFileNameStart( pszPath); + + // Copy the sliced portion of the path if requested by caller + + if( pszPathComponent) + { + f_strcpy( pszPathComponent, pszFileNameStart); + } + + // Copy the reduced source path to the dir path + + if (pszFileNameStart > pszPath) + { + uiLen = (FLMUINT)(pszFileNameStart - pszPath); + f_memcpy( pszDir, pszPath, uiLen); + + if (uiLen >= 2 && f_isSlashSeparator( pszDir [uiLen - 1]) +#ifndef FLM_UNIX + && pszDir [uiLen - 2] != ':' +#endif + ) + { + // Trim off the trailing path separator + + pszDir [uiLen - 1] = 0; + } + else + { + pszDir [uiLen] = 0; + } + } + else + { + *pszDir = 0; + } + } + else + { + // We've found the drive id or server\volume specifier. + + if (pszPathComponent) + { + f_strcpy( pszPathComponent, pszPath); + } + + *pszDir = 0; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Internal function for WpioPathBuild() and WpioPathModify(). + Appends string the path & adds a path delimiter if necessary. +In: *path = pointer to an IO_PATH + *string = pointer to a NULL terminated string + *end_ptr = pointer to the end of the IO_PATH which is being built. +****************************************************************************/ +RCODE FLMAPI F_FileSystem::pathAppend( + char * pszPath, + const char * pszPathComponent) +{ + + // Don't put a slash separator if pszPath is empty + + if (*pszPath) + { + FLMUINT uiStrLen = f_strlen( pszPath); + char * pszEnd = pszPath + uiStrLen - 1; + + if (!f_isSlashSeparator( *pszEnd)) + { + + // Check for maximum path size - 2 is for slash separator + // and null byte. + + if (uiStrLen + 2 + f_strlen( pszPathComponent) > F_PATH_MAX_SIZE) + { + return RC_SET( NE_FLM_IO_PATH_TOO_LONG); + } + + pszEnd++; +#if defined( FLM_UNIX) + *pszEnd = '/'; +#else + *pszEnd = '\\'; +#endif + } + else + { + + // Check for maximum path size +1 is for null byte. + + if (uiStrLen + 1 + f_strlen( pszPathComponent) > F_PATH_MAX_SIZE) + { + return RC_SET( NE_FLM_IO_PATH_TOO_LONG); + } + } + + f_strcpy( pszEnd + 1, pszPathComponent); + } + else + { + f_strcpy( pszPath, pszPathComponent); + } + + return( NE_FLM_OK); +} + +/**************************************************************************** +Desc: Convert an PATH into a fully qualified, storable C string + reference to a file or directory. +In: pszPath - the path to convert. + pszStorageString - a pointer to a string that is atleast + F_PATH_MAX_SIZE in size +****************************************************************************/ +RCODE FLMAPI F_FileSystem::pathToStorageString( + const char * pszPath, + char * pszStorageString) +{ +#ifdef FLM_WIN + char * pszNamePart; + + if (GetFullPathName( (LPCSTR)pszPath, + (DWORD)F_PATH_MAX_SIZE - 1, + (LPSTR)pszStorageString, + (LPSTR *)&pszNamePart) != 0) + { + + } + else + { + // Convert to upper case. + + while (*pszPath) + { + *pszStorageString++ = *pszPath; + pszPath++; + } + *pszStorageString = 0; + } + return NE_FLM_OK; +#else + + char szFile[ F_PATH_MAX_SIZE]; + char szDir[ F_PATH_MAX_SIZE]; + char * pszRealPath = NULL; + RCODE rc = NE_FLM_OK; + + if (RC_BAD( rc = pathReduce( pszPath, szDir, szFile))) + { + goto Exit; + } + + if (!szDir [0]) + { + szDir [0] = '.'; + szDir [1] = '\0'; + } + + if (RC_BAD( rc = f_alloc( (FLMUINT)PATH_MAX, &pszRealPath))) + { + goto Exit; + } + + if (!realpath( (char *)szDir, (char *)pszRealPath)) + { + rc = MapErrnoToFlaimErr( errno, NE_FLM_PARSING_FILE_NAME); + goto Exit; + } + + if (f_strlen( pszRealPath) >= F_PATH_MAX_SIZE) + { + rc = RC_SET( NE_FLM_IO_PATH_TOO_LONG); + goto Exit; + } + + f_strcpy( pszStorageString, pszRealPath); + + if (RC_BAD( rc = pathAppend( pszStorageString, szFile))) + { + goto Exit; + } + +Exit: + + if (pszRealPath) + { + f_free( &pszRealPath); + } + + return( rc); +#endif +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE void HexToNative( + FLMBYTE ucHexVal, + char * pszNativeChar) +{ + *pszNativeChar = (char)(ucHexVal < 10 + ? ucHexVal + NATIVE_ZERO + : (ucHexVal - 10) + NATIVE_LOWER_A); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE void SetUpTime( + FLMUINT * puiBaseTime, + FLMBYTE * pbyHighByte) +{ + FLMUINT uiSdTime = 0; + f_timeGetSeconds( &uiSdTime); + *pbyHighByte = (FLMBYTE)(uiSdTime >> 24); + uiSdTime = uiSdTime << 5; + if( *puiBaseTime < uiSdTime) + *puiBaseTime = uiSdTime; +} + +/**************************************************************************** +Desc: Generates a file name given a seed and some modifiers, it is built + to be called in a loop until the file can be sucessfully written or + created with the increment being changed every time. +In: bModext -> if TRUE then we will use the extension for collisions. +In\Out: puiTime -> a modified time stamp which is used as the base + filename. To properly set up this value, make sure + the puiTime points to a 0 the first time this routine + is called and it will be set up for you. Thereafter, + do not change it between calls. + pHighChars-> these are the 8 bits that were shifted off the top of + the time struct. It will be set up for you the first + time you call this routine if puiTime points to a 0 + the first time this routine is called. Do not change + this value between calls. + pszFileName -> should be pointing to a null string on the way in. + going out it will be the complete filename. + pszFileExt -> the last char of the ext will be used for collisions, + depending on the bModext flag. If null then + the extension will be .00x where x is the collision + counter. +Notes: The counter on the collision is 0-9, a-z. +****************************************************************************/ +void FLMAPI F_FileSystem::pathCreateUniqueName( + FLMUINT * puiTime, + char * pszFileName, + const char * pszFileExt, + FLMBYTE * pHighChars, + FLMBOOL bModext) +{ + FLMINT iCount, iLength; + FLMUINT uiSdTmp = 0; + FLMUINT uiIncVal = 1; + + SetUpTime( puiTime, pHighChars); + uiSdTmp = *puiTime; + + /* Add on the filename extension if passed from the caller */ + *(pszFileName + 8) = NATIVE_DOT; + f_memset( (pszFileName + 9), NATIVE_ZERO, 3 ); + if ( ( pszFileExt != NULL )) + { + if ((iLength = f_strlen(pszFileExt)) > 3) + { + iLength = 3; + } + f_memmove( (pszFileName + 9), pszFileExt, iLength); + } + + if( bModext == TRUE) + { + HexToNative((FLMBYTE)(uiSdTmp & 0x0000001F), pszFileName+(11)); + } + else + { + uiIncVal = 32; + } + uiSdTmp = uiSdTmp >> 5; + for( iCount = 0; iCount < 6; iCount++) /* set pos 2-7 of filename */ + { + HexToNative((FLMBYTE)(uiSdTmp & 0x0000000F), pszFileName+(7-iCount)); + uiSdTmp = uiSdTmp >> 4; + } /* End for() */ + + for( iCount = 0; iCount < 2; iCount++) /* set pos 0-1 of filename */ + { + HexToNative((FLMBYTE)(*pHighChars & 0x0000000F), pszFileName+(1-iCount)); + *pHighChars = *pHighChars >> 4; + } /* End for() */ + + /* Append on a NULL terminator */ + *(pszFileName + 12) = '\0'; + *puiTime += uiIncVal; + + return; +} + +/**************************************************************************** +Desc: Compares the current file against a pattern template +****************************************************************************/ +FLMBOOL FLMAPI f_doesFileMatch( + const char * pszFileName, + const char * pszTemplate) +{ + FLMUINT uiPattern; + FLMUINT uiChar; + + if( !*pszTemplate) + { + return( TRUE); + } + + while( *pszTemplate) + { + uiPattern = *pszTemplate++; + switch( uiPattern) + { + case NATIVE_WILDCARD: + /* if the match_template ends in an asterisk, then we match the*/ + /* remaining string by default, return a match. */ + + if( *pszTemplate == 0) + { + return( TRUE); + } + + /* Found an asterisk somewhere in the match_template, now let's + see if we match anywhere on the remaining input string. */ + + while( *pszFileName) + { + if( f_doesFileMatch( pszFileName, pszTemplate)) + { + return( TRUE); /* found a match, return */ + } + pszFileName++; + } + return( FALSE); /* did not find match, return */ + case NATIVE_QUESTIONMARK: + if( *pszFileName++ == 0) /* skip one character for '?' */ + { + return( FALSE); + } + break; + default: + uiChar = *pszFileName++; + if( f_toupper( uiPattern) != f_toupper( uiChar)) + { + return( FALSE); + } + break; + } + } + + return( (*pszFileName != 0) ? FALSE : TRUE ); +} diff --git a/ftk/src/ftkprntf.cpp b/ftk/src/ftkprntf.cpp new file mode 100644 index 0000000..128d41b --- /dev/null +++ b/ftk/src/ftkprntf.cpp @@ -0,0 +1,769 @@ +//------------------------------------------------------------------------------ +// Desc: sprintf and vsprintf +// +// Tabs: 3 +// +// Copyright (c) 2002-2003,2005-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: flprintf.cpp 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +FSTATIC void f_sprintfParseArgs( + FLMBYTE * pszFormat, + F_SPRINTF_INFO * pInfo, + f_va_list * args); + +FSTATIC void f_processFormatString( + FLMUINT uiLen, + F_SPRINTF_INFO * pInfo, + ...); + +FSTATIC FLMUINT f_printNumber( + FLMUINT64 ui64Val, + FLMUINT uiBase, + FLMBOOL bUpperCase, + FLMBOOL bCommas, + FLMBYTE * pszBuf); + +/**************************************************************************** +Desc: Parameter 'format' points to text following a '%' sign. Process + legal field information. Leave 'format' pointing at the format + specifier char. +****************************************************************************/ +void f_sprintfProcessFieldInfo( + FLMBYTE ** ppszFormat, + FLMUINT * puiWidth, + FLMUINT * puiPrecision, + FLMUINT * puiFlags, + f_va_list * args) +{ + FLMBYTE * pszFormat = *ppszFormat; + + // Process flags + + *puiFlags = 0; + for( ;;) + { + switch( *pszFormat) + { + case '-': + *puiFlags |= FLM_PRINTF_MINUS_FLAG; + break; + + case '+': + *puiFlags |= FLM_PRINTF_PLUS_FLAG; + break; + + case ' ': + *puiFlags |= FLM_PRINTF_SPACE_FLAG; + break; + + case '#': + *puiFlags |= FLM_PRINTF_POUND_FLAG; + break; + + case '0': + *puiFlags |= FLM_PRINTF_ZERO_FLAG; + break; + + case ',': + *puiFlags |= FLM_PRINTF_COMMA_FLAG; + break; + + default: + goto NoMoreFlags; + } + + pszFormat++; + } + +NoMoreFlags: + + // Process width + + *puiWidth = 0; + if( *pszFormat == '*') + { + *puiWidth = f_va_arg( *args, unsigned int); + pszFormat++; + } + else + { + while( *pszFormat >= '0' && *pszFormat <= '9') + { + *puiWidth = (*puiWidth * 10) + (*pszFormat - '0'); + pszFormat++; + } + } + + // Process precision + + *puiPrecision = 0; + if( *pszFormat == '.') + { + pszFormat++; + if( *pszFormat == '*') + { + *puiPrecision = f_va_arg( *args, unsigned int); + pszFormat++; + } + else while( *pszFormat >= '0' && *pszFormat <= '9') + { + *puiPrecision = (*puiPrecision * 10) + (*pszFormat - '0'); + pszFormat++; + } + } + + // Size modifiers + switch( *pszFormat) + { + case 'I': + if( pszFormat[ 1] == '6' && pszFormat[ 2] == '4') + { + *puiFlags |= FLM_PRINTF_INT64_FLAG; + pszFormat += 3; + } + else + { + flmAssert( 0); + } + break; + + case 'L': + *puiFlags |= FLM_PRINTF_DOUBLE_FLAG; + pszFormat++; + break; + + case 'l': + *puiFlags |= FLM_PRINTF_LONG_FLAG; + pszFormat++; + break; + + case 'h': + *puiFlags |= FLM_PRINTF_SHORT_FLAG; + pszFormat++; + break; + } + + *ppszFormat = pszFormat; + return; +} + +/**************************************************************************** +Desc: Handle text portions of the format string +****************************************************************************/ +FSTATIC void f_processFormatString( + FLMUINT uiLen, + F_SPRINTF_INFO * pInfo, + ...) +{ + f_va_list args; + + f_va_start( args, pInfo); + if( uiLen) + { + f_sprintfStringFormatter( 0, uiLen, uiLen, 0, pInfo, &args); + } + f_va_end( args); +} + +/**************************************************************************** +Desc: Parse arguments in format string, calling appropriate handlers +****************************************************************************/ +FSTATIC void f_sprintfParseArgs( + FLMBYTE * pszFormat, + F_SPRINTF_INFO * pInfo, + f_va_list * args) +{ + FLMBYTE ucFormatChar; + FLMUINT uiFlags; + FLMUINT uiWidth; + FLMUINT uiPrecision; + FLMBYTE * pszTextStart = pszFormat; + + while( (ucFormatChar = (FLMBYTE)*pszFormat++) != 0) + { + if( ucFormatChar != '%') + { + continue; + } + + uiWidth = (FLMUINT)(pszFormat - pszTextStart - 1); + f_processFormatString( uiWidth, pInfo, pszTextStart); + + f_sprintfProcessFieldInfo( &pszFormat, &uiWidth, + &uiPrecision, &uiFlags, args); + + ucFormatChar = (unsigned char)*pszFormat++; + switch( ucFormatChar) + { + case '%': + case 'c': + f_sprintfCharFormatter( ucFormatChar, uiWidth, + uiPrecision, uiFlags, pInfo, args); + break; + + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + case 'p': + if( ucFormatChar == 'i') + { + ucFormatChar = 'd'; + } + + f_sprintfNumberFormatter( ucFormatChar, uiWidth, + uiPrecision, uiFlags, pInfo, args); + break; + + case 's': + case 'S': + case 'U': + f_sprintfStringFormatter( ucFormatChar, uiWidth, + uiPrecision, uiFlags, pInfo, args); + break; + + case 'e': + case 'E': + f_sprintfErrorFormatter( ucFormatChar, uiWidth, + uiPrecision, uiFlags, pInfo, args); + break; + + default: + f_sprintfNotHandledFormatter( ucFormatChar, uiWidth, + uiPrecision, uiFlags, pInfo, args); + break; + } + pszTextStart = pszFormat; + } + + f_processFormatString( (FLMUINT)(pszFormat - pszTextStart - 1), + pInfo, pszTextStart); +} + +/**************************************************************************** +Desc: Default string formatter. +****************************************************************************/ +void f_sprintfStringFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + F_SPRINTF_INFO * pInfo, + f_va_list * args) +{ + static const char pszNullPointerStr[] = ""; + FLMUINT uiOutputLen; + FLMUINT uiCount; + FLMUNICODE * pUnicode; + const char * pszStr = f_va_arg( *args, char *); + FLMBYTE * pszDest = pInfo->pszDestStr; + + if( !pszStr) + { + uiOutputLen = f_strlen( pszNullPointerStr); + } + else if( ucFormatChar == 'S') + { + uiOutputLen = *pszStr++; + } + else + { + if( ucFormatChar == 'U') + { + uiOutputLen = 0; + pUnicode = (FLMUNICODE *)pszStr; + while( *pUnicode) + { + if( *pUnicode >= 32 && *pUnicode <= 127) + { + uiOutputLen++; + } + else + { + uiOutputLen += 7; + } + pUnicode++; + } + } + else + { + uiOutputLen = f_strlen( pszStr); + } + } + + if( uiPrecision > 0 && uiOutputLen > uiPrecision) + { + uiOutputLen = uiPrecision; + } + + uiCount = uiWidth - uiOutputLen; + + if( uiOutputLen < uiWidth && !(uiFlags & FLM_PRINTF_MINUS_FLAG)) + { + // Right justify + + f_memset( pszDest, ' ', uiCount); + pszDest += uiCount; + } + + if( !pszStr) + { + f_memcpy( pszDest, pszNullPointerStr, uiOutputLen); + pszDest += uiOutputLen; + } + else if( ucFormatChar == 'U') + { + FLMUINT uiBytesOutput = 0; + + for( pUnicode = (FLMUNICODE *)pszStr; + uiBytesOutput < uiOutputLen && *pUnicode; pUnicode++) + { + if( *pUnicode >= 32 && *pUnicode <= 127) + { + *pszDest++ = (FLMBYTE)(*pUnicode); + uiBytesOutput++; + } + else + { + FLMBYTE szTmpBuf[ 8]; + FLMUINT uiTmpLen; + FLMBYTE * pszTmp; + + szTmpBuf[ 0] = '~'; + szTmpBuf[ 1] = '['; + uiTmpLen = f_printNumber( (FLMUINT64)(*pUnicode), + 16, TRUE, FALSE, &szTmpBuf[ 2]); + uiTmpLen += 2; + szTmpBuf[ uiTmpLen] = ']'; + szTmpBuf[ uiTmpLen + 1] = 0; + + pszTmp = szTmpBuf; + while( *pszTmp && uiBytesOutput < uiOutputLen) + { + *pszDest++ = *pszTmp; + pszTmp++; + uiBytesOutput++; + } + } + } + } + else + { + f_memcpy( pszDest, pszStr, uiOutputLen); + pszDest += uiOutputLen; + } + + if( uiOutputLen < uiWidth && (uiFlags & FLM_PRINTF_MINUS_FLAG)) + { + // Left justify + + f_memset( pszDest, ' ', uiCount); + pszDest += uiCount; + } + + *pszDest = 0; + uiCount = (FLMUINT)(pszDest - pInfo->pszDestStr); + pInfo->pszDestStr = pszDest; +} + +/**************************************************************************** +Desc: Converts a number to a string +****************************************************************************/ +FSTATIC FLMUINT f_printNumber( + FLMUINT64 ui64Val, + FLMUINT uiBase, + FLMBOOL bUpperCase, + FLMBOOL bCommas, + FLMBYTE * pszBuf) +{ + FLMBYTE ucChar; + FLMUINT uiOffset = 0; + FLMUINT uiDigitCount = 0; + FLMUINT uiLoop; + + // We don't support commas on bases other than 10 + + if( uiBase != 10) + { + bCommas = FALSE; + } + + // Build the number string from the value + + for( ;;) + { + ucChar = (FLMBYTE)(ui64Val % uiBase); + pszBuf[ uiOffset++] = (FLMBYTE)(ucChar > 9 + ? ucChar + (bUpperCase ? 'A' : 'a') - 10 + : ucChar + '0'); + uiDigitCount++; + + if( (ui64Val = (ui64Val / uiBase)) == 0) + { + break; + } + + if( bCommas && (uiDigitCount % 3) == 0) + { + pszBuf[ uiOffset++] = (FLMBYTE)','; + } + } + + // Reverse the string + + for( uiLoop = 0; uiLoop < uiOffset / 2; uiLoop++) + { + ucChar = pszBuf[ uiLoop]; + pszBuf[ uiLoop] = pszBuf[ uiOffset - uiLoop - 1]; + pszBuf[ uiOffset - uiLoop - 1] = ucChar; + } + + pszBuf[ uiOffset] = 0; + return( uiOffset); +} + +/**************************************************************************** +Desc: Default number formatter. + Format: %[flags][width][.prec]'E' +****************************************************************************/ +void f_sprintfNumberFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + F_SPRINTF_INFO * pInfo, + f_va_list * args) +{ + FLMUINT uiCount; + FLMUINT uiPrefix = FLM_PREFIX_NONE; + FLMUINT uiLength; + FLMUINT uiBase = 10; + FLMBYTE ucNumberBuffer[ 64]; + FLMBOOL bUpperCase = FALSE; + FLMBOOL bCommas = FALSE; + FLMBYTE * pszTmp; + FLMBYTE * pszDest = pInfo->pszDestStr; + FLMUINT64 ui64Val; + + if( ucFormatChar == 'p') + { + ui64Val = (FLMUINT64)((FLMUINT)f_va_arg( *args, void *)); + uiFlags |= FLM_PRINTF_POUND_FLAG; + } + else if( ucFormatChar != 'd') + { + // Unsigned number + + if( uiFlags & FLM_PRINTF_SHORT_FLAG) + { + ui64Val = (FLMUINT64)((unsigned int)f_va_arg( *args, int)); + } + else if( uiFlags & (FLM_PRINTF_LONG_FLAG | FLM_PRINTF_DOUBLE_FLAG)) + { + ui64Val = (FLMUINT64)((unsigned long int)f_va_arg( *args, long int)); + } + else if( uiFlags & FLM_PRINTF_INT64_FLAG) + { + ui64Val = f_va_arg( *args, FLMUINT64); + } + else + { + ui64Val = (FLMUINT64)((unsigned int)f_va_arg( *args, int)); + } + } + else + { + // Signed number + + if( uiFlags & FLM_PRINTF_SHORT_FLAG) + { + ui64Val = (FLMUINT64)((FLMINT64)f_va_arg( *args, int)); + } + else if( uiFlags & (FLM_PRINTF_LONG_FLAG | FLM_PRINTF_DOUBLE_FLAG)) + { + ui64Val = (FLMUINT64)((FLMINT64)f_va_arg( *args, long int)); + } + else if( uiFlags & FLM_PRINTF_INT64_FLAG) + { + ui64Val = (FLMUINT64)f_va_arg( *args, FLMINT64); + } + else + { + ui64Val = (FLMUINT64)((FLMINT64)f_va_arg( *args, int)); + } + } + + switch( ucFormatChar) + { + case 'd': + { + if( ((FLMINT64)ui64Val) < 0) + { + uiPrefix = FLM_PREFIX_MINUS; + if( uiWidth > 0) + { + uiWidth--; + } + ui64Val = (FLMUINT64)(-(FLMINT64)ui64Val); + } + else if( uiFlags & FLM_PRINTF_PLUS_FLAG) + { + uiPrefix = FLM_PREFIX_PLUS; + if( uiWidth > 0) + { + uiWidth--; + } + } + break; + } + + case 'o': + { + uiBase = 8; + break; + } + + case 'x': + case 'X': + case 'p': + { + if( (uiFlags & FLM_PRINTF_POUND_FLAG) && ui64Val) + { + uiPrefix = FLM_PREFIX_POUND; + if( uiWidth > 1) + { + uiWidth -= 2; + } + } + uiBase = 16; + break; + } + } + + if( ucFormatChar == 'X') + { + bUpperCase = TRUE; + } + + if( (uiFlags & FLM_PRINTF_COMMA_FLAG) && uiBase == 10) + { + bCommas = TRUE; + } + + uiLength = f_printNumber( ui64Val, uiBase, bUpperCase, + bCommas, ucNumberBuffer); + + if( uiWidth < uiLength) + { + uiWidth = uiLength; + } + + if( uiFlags & FLM_PRINTF_ZERO_FLAG) + { + // Zero fill + + uiPrecision = uiWidth; + } + else if( !(uiFlags & FLM_PRINTF_MINUS_FLAG)) + { + // Right justify + + while( uiWidth > uiLength && uiWidth > uiPrecision) + { + *pszDest++ = ' '; + uiWidth--; + } + } + + // Handle the prefix (if any) + + switch( uiPrefix) + { + case FLM_PREFIX_NONE: + break; + + case FLM_PREFIX_MINUS: + *pszDest++ = '-'; + break; + + case FLM_PREFIX_PLUS: + *pszDest++ = '+'; + break; + + case FLM_PREFIX_POUND: + { + *pszDest++ = '0'; + *pszDest++ = 'x'; + break; + } + + default: + flmAssert( 0); + break; + } + + // Handle the precision + + if( bCommas && uiPrecision && (uiPrecision % 4) == 0) + { + uiPrecision--; + } + + while( uiLength < uiPrecision) + { + if( bCommas && (uiPrecision % 4) == 0) + { + *pszDest++ = ','; + uiPrecision--; + uiWidth--; + continue; + } + + *pszDest++ = '0'; + uiPrecision--; + uiWidth--; + } + + // Output the number + + for( uiCount = uiLength, pszTmp = &ucNumberBuffer[ 0]; + uiCount > 0; uiCount--) + { + *pszDest++ = *pszTmp++; + } + + if( uiFlags & FLM_PRINTF_MINUS_FLAG) + { + // Left justify + while( uiLength < uiWidth) + { + *pszDest++ = ' '; + uiWidth--; + } + } + + *pszDest = 0; + pInfo->pszDestStr = pszDest; +} + +/**************************************************************************** +Desc: Default character formatter. + Prints the character specified by VALUE in 'c', or the '%' character. + Format: %[flags][width][.prec]'c' + flags = + width = + prec = +****************************************************************************/ +void f_sprintfCharFormatter( + FLMBYTE ucFormatChar, + FLMUINT , // uiWidth, + FLMUINT , // uiPrecision, + FLMUINT , // uiFlags, + F_SPRINTF_INFO * pInfo, + f_va_list * args) +{ + *pInfo->pszDestStr++ = (FLMBYTE)((ucFormatChar == '%') + ? '%' + : f_va_arg( *args, int)); + *pInfo->pszDestStr = 0; +} + +/**************************************************************************** +Desc: Default error formatter. +****************************************************************************/ +void f_sprintfErrorFormatter( + FLMBYTE ucFormatChar, + FLMUINT , // uiWidth, + FLMUINT , // uiPrecision, + FLMUINT , // uiFlags, + F_SPRINTF_INFO * pInfo, + f_va_list * args) +{ + FLMUINT uiErrorCode = (FLMUINT)f_va_arg( *args, unsigned); + + if( ucFormatChar == 'e') + { + pInfo->pszDestStr += + f_sprintf( (char *)pInfo->pszDestStr, "%s (%04X)", + (char *)f_errorString( (RCODE)uiErrorCode), + (unsigned)uiErrorCode); + } + else + { + pInfo->pszDestStr += + f_sprintf( (char *)pInfo->pszDestStr, "%04X", + (unsigned)uiErrorCode); + } +} + +/**************************************************************************** +Desc: Unknown format handler +****************************************************************************/ +void f_sprintfNotHandledFormatter( + FLMBYTE , // ucFormatChar, + FLMUINT , // uiWidth, + FLMUINT , // uiPrecision, + FLMUINT , // uiFlags, + F_SPRINTF_INFO * pInfo, + f_va_list * ) // args) +{ + flmAssert( 0); + *pInfo->pszDestStr++ = '?'; + *pInfo->pszDestStr = 0; +} + +/**************************************************************************** +Desc: FLAIM's vsprintf +****************************************************************************/ +FLMINT FLMAPI f_vsprintf( + char * pszDestStr, + const char * pszFormat, + f_va_list * args) +{ + F_SPRINTF_INFO info; + + info.pszDestStr = (FLMBYTE *)pszDestStr; + f_sprintfParseArgs( (FLMBYTE *)pszFormat, &info, args); + *info.pszDestStr = 0; + + return( (FLMINT)(info.pszDestStr - (FLMBYTE *)pszDestStr)); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMINT FLMAPI f_sprintf( + char * pszDestStr, + const char * pszFormat, + ...) +{ + FLMINT iLen; + f_va_list args; + + f_va_start(args, pszFormat); + iLen = f_vsprintf( pszDestStr, pszFormat, &args); + f_va_end(args); + + return( iLen); +} diff --git a/ftk/src/ftkrand.cpp b/ftk/src/ftkrand.cpp new file mode 100644 index 0000000..646d7d0 --- /dev/null +++ b/ftk/src/ftkrand.cpp @@ -0,0 +1,170 @@ +//------------------------------------------------------------------------------ +// Desc: Random number routines +// +// Tabs: 3 +// +// Copyright (c) 1995-1998, 2000, 2003,2005-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: ftkrand.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +/**************************************************************************** +This random number generator is based on Park & Miller's "suggested +minimal standard" for random number generation, pp 1192-1201 of the Oct 1988 +issue of _Communications_of_the_ACM_ (vol 31 number 10). It is a Lehmer +generator, which are of the form x[n] = A * x[n-1] % M, with A and M being +judiciously chosen constants. More formally, this is a "prime modulus +multiplicative linear congruential generator," or PMMLCG. + +Park & Miller originally suggested A==16807 and M==2**31-1, but an update +in CACM Vol 36, No. 7 pp 108-110 (July 1993) indicates that they have found +a better multiplier (A == 48271) to use with the same modulus (2**31-1). +This implementation uses the updated multiplier. + +To quote Park & Miller 1988, "We believe that this is the generator that +should always be used--unless one has access to a random number generator +KNOWN to be better." + +This algorithm produces a full-period generator; that is, starting from +any seed between 1 and 2**31-2, it generates all other values between 1 +and 2**31-2 before it returns to the starting point -- whereupon it repeats +the same sequence of 31-bit values. This is true for either choice of A +(16807 or 48271). + +The July 1993 article includes criticism by George Marsaglia of the Park +and Miller generator. Marsaglia feels that longer periods are needed. For +a description of his "subtract-with-borrow" (SWB) generators, see "A New +Class of Random Number Generators", The Annals of Applied Probability, +(1991) Vol. 1, No. 3, pp. 462-480. These generators require more state +information (~48 longwords) but produce generators with periods on the +order of 10**445. They also pass more stringent tests than the congruential +generators, and so might be considered 'a random number generator KNOWN to +be better.' However, Marsaglia does not spell out all the details needed to +implement SWB, nor does he give any simple test to determine whether an SWB +implementation is correct. +****************************************************************************/ + +/************************************************************************* +Desc: Set the seed from the date and time +*************************************************************************/ +void F_RandomGenerator::randomize( void) +{ + FLMUINT uiTime; + + f_timeGetSeconds( &uiTime ); + randomSetSeed( (FLMUINT32)(((FLMUINT32)uiTime % MAX_RANDOM) + 1)); +} + +/************************************************************************* +Desc: initialize the seed to a known value +*************************************************************************/ +void F_RandomGenerator::setSeed( + FLMINT32 i32Seed) +{ + if( i32Seed > 0 && i32Seed <= MAX_RANDOM) + { + m_i32Seed = i32Seed; + } + else + { + randomSetSeed( (FLMUINT32) + (i32Seed < 1 + ? i32Seed + MAX_RANDOM + : i32Seed - MAX_RANDOM)); + } +} + +/************************************************************************* +Desc: Generate the next number in the pseudo-random sequence + i.e., "f_randomLong( &r) > MAX_RANDOM/2" will be true half the + time, on average. Likewise, "f_randomLong( &r) & 0x1" has a 50-50 + chance of being true. +*************************************************************************/ +FLMINT32 F_RandomGenerator::getInt32( void) +{ +#define M 2147483647 +#define A 48271 +#define CHECK 399268537 + + FLMUINT32 ui32High; + FLMUINT32 ui32Low; + FLMUINT32 ui32Seed = m_i32Seed; + + ui32High = (ui32Seed >> 16); + ui32Low = ui32Seed & 0xFFFF; + ui32Low *= A; + ui32High *= A; + + ui32High += (ui32Low >> 16) & 0xFFFF; + ui32Low &= 0xFFFF; + + ui32Low |= (ui32High & 0x7FFF) << 16; + ui32High >>= 15; + ui32Low += ui32High; + + if( ui32Low & 0x80000000L) + { + ui32Low &= 0x7FFFFFFF; + ui32Low++; + } + + return( m_i32Seed = ui32Low); +} + +/************************************************************************* +Desc: Returns a random integer between i32Low and i32High, inclusive. +*************************************************************************/ +FLMINT32 F_RandomGenerator::getInt32( + FLMINT32 i32Low, + FLMINT32 i32High) +{ + FLMINT32 i32Range = i32High - i32Low + 1; + + if( i32Range < (1L << 20)) + { + return( i32Low + getInt32() % i32Range); + } + else + { + FLMINT32 i32Mask = 0; + FLMINT32 i32Val; + + i32Range--; + for( i32Val = i32Range; i32Val > 0; i32Val >>= 1) + { + i32Mask = (i32Mask << 1) | 1; + } + + do + { + i32Val = getInt32() & i32Mask; + } while( i32Val > i32Range); + + return( i32Low + i32Val); + } +} + +/************************************************************************* +Desc: +*************************************************************************/ +FLMBOOL F_RandomGenerator::getBoolean( void) +{ + return( (getInt32( 1, 100) <= 50 ? TRUE : FALSE)); +} diff --git a/ftk/src/ftkrset.cpp b/ftk/src/ftkrset.cpp new file mode 100644 index 0000000..4e1d9a6 --- /dev/null +++ b/ftk/src/ftkrset.cpp @@ -0,0 +1,3340 @@ +//------------------------------------------------------------------------------ +// Desc: Result set routines +// +// Tabs: 3 +// +// Copyright (c) 1996-1998, 2003-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: frset.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +// Make sure that the extension is in lower case characters. + +#define FRSET_FILENAME_EXTENSION "frs" + +/* +** Sorting Result Sets: +** + New algorithm 7/2/97. This is a good one! + Below are refinements to the existing result set code. + + 1) We now have two files that are used in the merge-sort process. + The first file is used to hold all of the blocks created when + adding entries into the result set. The second file is used for + the first and thereafter odd merge steps. The first file is then + truncated and used for each even merge step. At the end of the + merge one of the files will be deleted. Three buffers are used + during merge and only one will remain after the merge is done. + This is safer than the previous method and uses a little less + disk space. There are many small improvements that can be made. + + 2) The result set code now takes a buffer and a length and has no + knowledge of the data in the result set. In fact, the data may + be fixed length or variable length. We removed the record cache + from the result set and made a record cache manager. + + Future enhancements to consider: + 1) Do a 3, 4 or N-Way merge. + This will greatly increase memory allocations but save a lot of time + reading and writing when the number of entries is large. + 2) Use 3 buffers on the initial load. This is really doing the first + phase of the merge when adding entries. The algorithm would add + entries to two buffers, and when full merge to the third buffer and + write out two sorted buffer. + 3) Don't write out the last block - use it as the first block of the + merge when not complete. This will save a write and read on each + pass. In addition, the I/O cache may be helped out. + In addition, the last block of each phase should be used first + on the next phase. + + Old Notes: + Duplicate Entries: + Duplicate entries are very difficult for a general purpose sorter + to find. In some cases the user would want to compare only these + fields and not these to determine a duplicate. This result set + code lets the user pass in a callback routine to determine if + two entries are the same and if one should be dropped. The user + could pass in NULL to cause all duplicates to be retained. + Quick Sort Algorithm: + This algorithm, in FRSETBLK.CPP is a great algorithm that Scott + came up with. It will recurse only Log(base2)N times (number of + bits needed to represent N). This is a breakthrough because + all sorting algorithms I have seen will recurse N-1 times if + the data is in order or in reverse order. This will crash the + stack for a production quality sort. + Variable Length + This sorting engine (result set) supports variable length and + fixed length data. There is very low overhead for variable + length support. This sorting engine can be used for a variety + of tasks. + + Example: + + All numbers are logical block numbers. + + Adding Pass 1 Pass2 Pass3 Pass4 + Phase File 2 File 1 File 2 File 1 + File 1 (created) (truncated) (truncated) (truncated) + ========= ========= ========= =========== ==================== + 1 10 (1+2) 14 (10+11) 16 (14+15) 17 Final file (16+9) + 2 + 3 11 (3+4) 15 (12+13) 9 + 4 + 5 12 (5+6) 9 + 6 + 7 13 (7+8) + 8 + 9 9 +*/ + +/***************************************************************************** +Desc: +*****************************************************************************/ +F_ResultSet::F_ResultSet() +{ + m_pCompare = NULL; + m_pSortStatus = NULL; + m_ui64EstTotalUnits = 0; + m_ui64UnitsDone = 0; + + m_uiEntrySize = 0; + m_ui64TotalEntries = 0; + m_pCurRSBlk = NULL; + m_pFirstRSBlk = NULL; + m_pLastRSBlk = NULL; + + f_memset( &m_szIoDefaultPath[0], 0, F_PATH_MAX_SIZE); + + m_pucBlockBuf1 = NULL; + m_pucBlockBuf2 = NULL; + m_pucBlockBuf3 = NULL; + m_uiBlockBuf1Len = 0; + m_bFile1Opened = FALSE; + m_bFile2Opened = FALSE; + m_pMultiFileHdl1 = NULL; + m_pMultiFileHdl2 = NULL; + m_bOutput2ndFile = FALSE; + m_bInitialAdding = TRUE; + m_bFinalizeCalled = FALSE; + m_bSetupCalled = FALSE; + m_uiBlkSize = RS_BLOCK_SIZE; +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +F_ResultSet::F_ResultSet( + FLMUINT uiBlkSize) +{ + m_pCompare = NULL; + m_pSortStatus = NULL; + m_ui64EstTotalUnits = 0; + m_ui64UnitsDone = 0; + + m_uiEntrySize = 0; + m_ui64TotalEntries = 0; + m_pCurRSBlk = NULL; + m_pFirstRSBlk = NULL; + m_pLastRSBlk = NULL; + + f_memset( &m_szIoDefaultPath[0], 0, F_PATH_MAX_SIZE); + + m_pucBlockBuf1 = NULL; + m_pucBlockBuf2 = NULL; + m_pucBlockBuf3 = NULL; + m_uiBlockBuf1Len = 0; + m_bFile1Opened = FALSE; + m_bFile2Opened = FALSE; + m_pMultiFileHdl1 = NULL; + m_pMultiFileHdl2 = NULL; + m_bOutput2ndFile = FALSE; + m_bInitialAdding = TRUE; + m_bFinalizeCalled = FALSE; + m_bSetupCalled = FALSE; + m_uiBlkSize = uiBlkSize; +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +F_ResultSet::~F_ResultSet() +{ + F_ResultSetBlk * pCurRSBlk; + F_ResultSetBlk * pNextRSBlk; + + // Free up the result set block chain. + + for( pCurRSBlk = m_pFirstRSBlk; pCurRSBlk; pCurRSBlk = pNextRSBlk) + { + FLMUINT uiCount; + + pNextRSBlk = pCurRSBlk->m_pNext; + uiCount = pCurRSBlk->Release(); + flmAssert( !uiCount); + } + + // Set list to NULL for debugging in memory. + + m_pFirstRSBlk = NULL; + m_pLastRSBlk = NULL; + m_pCurRSBlk = NULL; + + // Free up all of the block buffers in the list. + + f_free( &m_pucBlockBuf1); + f_free( &m_pucBlockBuf2); + f_free( &m_pucBlockBuf3); + + // Close all opened files + + closeFile( &m_pMultiFileHdl1); + closeFile( &m_pMultiFileHdl2); + + if( m_pCompare) + { + m_pCompare->Release(); + } + + if( m_pSortStatus) + { + m_pSortStatus->Release(); + } +} + +/***************************************************************************** +Desc: Reset the result set so it can be reused. +*****************************************************************************/ +RCODE FLMAPI F_ResultSet::resetResultSet( + FLMBOOL bDelete) +{ + RCODE rc = NE_FLM_OK; + F_ResultSetBlk * pCurRSBlk; + F_ResultSetBlk * pNextRSBlk; + + // Free up the result set block chain - except for the first one. + + for( pCurRSBlk = m_pFirstRSBlk; pCurRSBlk; pCurRSBlk = pNextRSBlk) + { + FLMUINT uiCount; + + pNextRSBlk = pCurRSBlk->m_pNext; + if( pCurRSBlk != m_pFirstRSBlk) + { + uiCount = pCurRSBlk->Release(); + flmAssert( !uiCount); + } + } + + // Free up all of the block buffers in the list, except for the first one. + + f_free( &m_pucBlockBuf2); + f_free( &m_pucBlockBuf3); + + if( !m_pucBlockBuf1 || m_uiBlockBuf1Len != m_uiBlkSize) + { + if( m_pucBlockBuf1) + { + f_free( &m_pucBlockBuf1); + } + + if( RC_BAD( rc = f_alloc( m_uiBlkSize, &m_pucBlockBuf1))) + { + goto Exit; + } + + m_uiBlockBuf1Len = m_uiBlkSize; + } + + // Close all opened files + + closeFile( &m_pMultiFileHdl1, bDelete ); + closeFile( &m_pMultiFileHdl2 ); + m_bFile1Opened = m_bFile2Opened = FALSE; + m_pMultiFileHdl1 = m_pMultiFileHdl2 = NULL; + + // Reset some other variables + + if( m_pSortStatus) + { + m_pSortStatus->Release(); + m_pSortStatus = NULL; + } + + m_ui64EstTotalUnits = 0; + m_ui64UnitsDone = 0; + m_ui64TotalEntries = 0; + m_bOutput2ndFile = FALSE; + m_bInitialAdding = TRUE; + m_bEntriesInOrder = m_bAppAddsInOrder; + m_bFinalizeCalled = FALSE; + + // If we don't have a block, allocate it. Otherwise + // reset the one we have left. + + if( !m_pFirstRSBlk) + { + if( (m_pFirstRSBlk = f_new F_ResultSetBlk) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + } + else + { + m_pFirstRSBlk->reset(); + } + + m_pLastRSBlk = m_pCurRSBlk = m_pFirstRSBlk; + (void)m_pFirstRSBlk->setup( &m_pMultiFileHdl1, m_pCompare, + m_uiEntrySize, TRUE, m_bDropDuplicates, + m_bEntriesInOrder); + (void) m_pFirstRSBlk->setBuffer( m_pucBlockBuf1, m_uiBlockBuf1Len); + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Setup the result set with all of the needed input values. + This method must only be called once. +*****************************************************************************/ +RCODE FLMAPI F_ResultSet::setupResultSet( + const char * pszDirPath, + IF_ResultSetCompare * pCompare, + FLMUINT uiEntrySize, + FLMBOOL bDropDuplicates, + FLMBOOL bEntriesInOrder, + const char * pszInputFileName) +{ + RCODE rc = NE_FLM_OK; + FLMBOOL bNewBlock = FALSE; + FLMBOOL bNewBuffer = FALSE; + + flmAssert( !m_bSetupCalled ); + flmAssert( uiEntrySize <= RS_MAX_FIXED_ENTRY_SIZE); + + // Perform all of the allocations first. + + m_pFirstRSBlk = m_pLastRSBlk = m_pCurRSBlk = f_new F_ResultSetBlk; + + // Allocation Error? + + if( !m_pCurRSBlk) + { + rc = RC_SET( NE_FLM_MEM ); + goto Exit; + } + + bNewBlock = TRUE; + m_pCurRSBlk->setup( &m_pMultiFileHdl1, pCompare, + uiEntrySize, TRUE, bDropDuplicates, bEntriesInOrder); + + // Allocate only the first buffer - other buffers only used in merge. + + if( RC_BAD( rc = f_alloc( m_uiBlkSize, &m_pucBlockBuf1))) + { + goto Exit; + } + + m_uiBlockBuf1Len = m_uiBlkSize; + bNewBuffer = TRUE; + (void) m_pCurRSBlk->setBuffer( m_pucBlockBuf1, m_uiBlockBuf1Len); + + // Set the input variables. + + if( pszDirPath) + { + f_strcpy( m_szIoDefaultPath, pszDirPath); + } + + if( m_pCompare) + { + m_pCompare->Release(); + } + + if( (m_pCompare = pCompare) != NULL) + { + m_pCompare->AddRef(); + } + + m_uiEntrySize = uiEntrySize; + m_bDropDuplicates = bDropDuplicates; + m_bEntriesInOrder = m_bAppAddsInOrder = bEntriesInOrder; + + // If a filename was passed in, then we will try to open it and read whatever + // data it holds into the result set. If the file does not exist, it will not + // be created at this time. + + if( pszInputFileName) + { + f_strcpy( m_szIoFilePath1, m_szIoDefaultPath); + + if( RC_BAD( rc = gv_pFileSystem->pathAppend( + m_szIoFilePath1, pszInputFileName))) + { + goto Exit; + } + + f_strcat( m_szIoFilePath1, "." FRSET_FILENAME_EXTENSION); + + if( RC_BAD( rc = setupFromFile())) + { + goto Exit; + } + } + +Exit: + + // Free allocations on any error + + if( RC_BAD(rc)) + { + if( bNewBlock) + { + if( m_pCurRSBlk) + { + m_pCurRSBlk->Release(); + m_pFirstRSBlk = m_pLastRSBlk = m_pCurRSBlk = NULL; + } + } + + if( bNewBuffer) + { + f_free( &m_pucBlockBuf1); + m_uiBlockBuf1Len = 0; + } + } + else + { + m_bSetupCalled = TRUE; + } + + return( rc); +} + +/***************************************************************************** +Desc: Attempt to establish the result set from an existing file. +*****************************************************************************/ +RCODE F_ResultSet::setupFromFile( void) +{ + RCODE rc = NE_FLM_OK; + F_ResultSetBlk * pNextRSBlk; + FLMUINT uiOffset; + FLMUINT uiBytesRead; + F_BLOCK_HEADER BlkHdr; + + flmAssert( !m_bSetupCalled); + + if( (m_pMultiFileHdl1 = f_new F_MultiFileHdl) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pMultiFileHdl1->open( m_szIoFilePath1))) + { + if( rc == NE_FLM_IO_PATH_NOT_FOUND) + { + if( RC_BAD( rc = m_pMultiFileHdl1->create( m_szIoFilePath1))) + { + rc = NE_FLM_OK; + m_pMultiFileHdl1->Release(); + m_pMultiFileHdl1 = NULL; + goto Exit; + } + } + else + { + rc = NE_FLM_OK; + m_pMultiFileHdl1->Release(); + m_pMultiFileHdl1 = NULL; + goto Exit; + } + } + + m_bFile1Opened = TRUE; + + // Release the current set of blocks. + + while( m_pFirstRSBlk) + { + m_pCurRSBlk = m_pFirstRSBlk; + m_pFirstRSBlk = m_pFirstRSBlk->m_pNext; + m_pCurRSBlk->Release(); + } + + m_pFirstRSBlk = m_pLastRSBlk = m_pCurRSBlk = NULL; + + // Allocate the buffer that we will use to read the data in. + + if( !m_pucBlockBuf1) + { + if( RC_BAD( rc = f_calloc( m_uiBlkSize, &m_pucBlockBuf1))) + { + goto Exit; + } + m_uiBlockBuf1Len = m_uiBlkSize; + } + else + { + f_memset( m_pucBlockBuf1, 0, m_uiBlkSize); + } + + // Now read every block in the file and create a F_ResultSetBlk chain. + + f_memset( (void *)&BlkHdr, 0, sizeof( F_BLOCK_HEADER)); + + for( uiOffset = 0;;) + { + // Read the block header + + if( RC_BAD( rc = m_pMultiFileHdl1->read( + BlkHdr.ui64FilePos + BlkHdr.uiBlockSize + uiOffset, + sizeof( F_BLOCK_HEADER), &BlkHdr, &uiBytesRead))) + { + if( rc == NE_FLM_EOF_HIT || rc == NE_FLM_IO_END_OF_FILE) + { + rc = NE_FLM_OK; + break; + } + + goto Exit; + } + + // Put the previous block out of fous. + + if( m_pCurRSBlk) + { + if( RC_BAD( rc = m_pCurRSBlk->setBuffer( NULL, m_uiBlkSize))) + { + goto Exit; + } + } + + // Allocate a new RSBlk and link into the result block list. + + if( (pNextRSBlk = f_new F_ResultSetBlk) == NULL) + { + rc = RC_SET( NE_FLM_MEM ); + goto Exit; + } + + if( !m_pFirstRSBlk) + { + m_pFirstRSBlk = m_pLastRSBlk = m_pCurRSBlk = pNextRSBlk; + } + else + { + m_pCurRSBlk->m_pNext = pNextRSBlk; + pNextRSBlk->m_pPrev = m_pCurRSBlk; + m_pLastRSBlk = m_pCurRSBlk = pNextRSBlk; + } + + m_pCurRSBlk->setup( &m_pMultiFileHdl1, m_pCompare, m_uiEntrySize, + BlkHdr.bFirstBlock, m_bDropDuplicates, !m_bInitialAdding); + + f_memcpy( (void *)&m_pCurRSBlk->m_BlockHeader, + (void *)&BlkHdr, sizeof(F_BLOCK_HEADER)); + + // Process the block... + + if( RC_BAD( rc = m_pCurRSBlk->setBuffer( m_pucBlockBuf1, m_uiBlkSize))) + { + goto Exit; + } + + m_pCurRSBlk->adjustState( m_uiBlkSize); + uiOffset = sizeof(F_BLOCK_HEADER); + } + + // If the file is empty or just created, we won't have a RS Block yet. + + if( !m_pCurRSBlk) + { + // Allocate a new RSBlk + + if( (pNextRSBlk = f_new F_ResultSetBlk) == NULL) + { + rc = RC_SET( NE_FLM_MEM ); + goto Exit; + } + + if( !m_pFirstRSBlk) + { + m_pFirstRSBlk = m_pLastRSBlk = m_pCurRSBlk = pNextRSBlk; + } + else + { + m_pCurRSBlk->m_pNext = pNextRSBlk; + pNextRSBlk->m_pPrev = m_pCurRSBlk; + m_pLastRSBlk = m_pCurRSBlk = pNextRSBlk; + } + + m_pCurRSBlk->setup( &m_pMultiFileHdl1, m_pCompare, + m_uiEntrySize, m_bInitialAdding, m_bDropDuplicates, + !m_bInitialAdding ); + + if( RC_BAD( rc = m_pCurRSBlk->setBuffer( m_pucBlockBuf1, m_uiBlkSize))) + { + goto Exit; + } + } + else + { + // Resize the file. + + if( RC_BAD(rc = m_pCurRSBlk->truncate( (FLMBYTE *)m_szIoFilePath1))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Write the current block and close the file. Call this function befor + calling resetResultSet so that it can be reused. +*****************************************************************************/ +RCODE FLMAPI F_ResultSet::flushToFile() +{ + RCODE rc = NE_FLM_OK; + + flmAssert( m_bFile1Opened); + + // Flush to disk what ever we have. + + if( RC_BAD( rc = m_pCurRSBlk->flush( m_bInitialAdding, TRUE))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pCurRSBlk->setBuffer( NULL, m_uiBlkSize))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Interface to add a variable length entry to the result set. +Notes: Public method used by application and by the internal sort + and merge steps during finalize. The user must never add an + entry that is larger than the block size. +*****************************************************************************/ +RCODE FLMAPI F_ResultSet::addEntry( + const void * pvEntry, + FLMUINT uiEntryLength) // If zero then entry is fixed length +{ + RCODE rc = NE_FLM_OK; + + flmAssert( m_bSetupCalled); + flmAssert( !m_bFinalizeCalled); + + rc = m_pCurRSBlk->addEntry( (FLMBYTE *)pvEntry, uiEntryLength); + + // See if current block is full + + if( rc == NE_FLM_EOF_HIT) + { + F_ResultSetBlk * pNextRSBlk; + IF_MultiFileHdl ** ppMultiFileHdl; + + if( m_bInitialAdding && !m_bFile1Opened) + { + // Need to create and open the output file? + // In a merge we may be working on the 2nd file and NOT the 1st. + // There just isn't a better place to open the 1st file. + + if( RC_BAD(rc = openFile( &m_pMultiFileHdl1))) + { + goto Exit; + } + } + + ppMultiFileHdl = (m_bOutput2ndFile) + ? &m_pMultiFileHdl2 + : &m_pMultiFileHdl1; + + // Always flush to disk (TRUE) from here. + + if( RC_BAD( rc = m_pCurRSBlk->flush( m_bInitialAdding, TRUE))) + { + goto Exit; + } + + (void) m_pCurRSBlk->setBuffer( NULL, m_uiBlkSize); + + // Adding the current block is complete so allocate a new + // block object and link it into the list. + // We must continue to use this same block buffer. + + // Allocate a new RSBlk and link into the result block list. + + if( (pNextRSBlk = f_new F_ResultSetBlk) == NULL) + { + rc = RC_SET( NE_FLM_MEM ); + goto Exit; + } + + m_pCurRSBlk->m_pNext = pNextRSBlk; + pNextRSBlk->m_pPrev = m_pCurRSBlk; + m_pLastRSBlk = m_pCurRSBlk = pNextRSBlk; + m_pCurRSBlk->setup( ppMultiFileHdl, m_pCompare, + m_uiEntrySize, m_bInitialAdding, m_bDropDuplicates, + !m_bInitialAdding ); + + // Reset all of the buffer pointers and values. + + (void)m_pCurRSBlk->setBuffer( m_pucBlockBuf1, m_uiBlockBuf1Len); + + // Make the callback only during the merge phase. + + if( !m_bInitialAdding && m_pSortStatus) + { + if( m_ui64EstTotalUnits <= ++m_ui64UnitsDone ) + { + m_ui64EstTotalUnits = m_ui64UnitsDone; + } + + if( RC_BAD( rc = m_pSortStatus->reportSortStatus( m_ui64EstTotalUnits, + m_ui64UnitsDone))) + { + goto Exit; + } + } + + // Add the entry again. This call should never fail because of space. + // If it does fail then the entry is larger than the buffer size. + + if( RC_BAD( rc = m_pCurRSBlk->addEntry( + (FLMBYTE *)pvEntry, uiEntryLength))) + { + if( rc == NE_FLM_EOF_HIT) + { + rc = RC_SET_AND_ASSERT( NE_FLM_FAILURE); + } + + goto Exit; + } + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Done adding entries. Sort all of the entries and perform a merge. +*****************************************************************************/ +RCODE FLMAPI F_ResultSet::finalizeResultSet( + FLMUINT64 * pui64TotalEntries) +{ + RCODE rc = NE_FLM_OK; + FLMBOOL bMergeSort; + + // Avoid being called more than once. + + flmAssert( !m_bFinalizeCalled); + flmAssert( m_bSetupCalled ); + + // Not a bug - but for future possibilities just check + // if there is more than one block and if so then + // the while() loop merge sort needs to be called. + + bMergeSort = (m_pFirstRSBlk != m_pLastRSBlk) ? TRUE : FALSE; + + // Force the write to disk if bMergeSort is TRUE. + + if( RC_BAD(rc = m_pCurRSBlk->finalize( bMergeSort))) + { + goto Exit; + } + + m_bInitialAdding = FALSE; + + // If the entries are in order fixup the block chain and we are done. + + if( m_bEntriesInOrder) + { + F_ResultSetBlk * pBlk; + + if( numberOfBlockChains() > 1) + { + // Entries already in order - need to fixup the blocks. + + for( pBlk = m_pFirstRSBlk; pBlk; pBlk = pBlk->m_pNext) + { + pBlk->m_BlockHeader.bFirstBlock = FALSE; + pBlk->m_BlockHeader.bLastBlock = FALSE; + } + + m_pFirstRSBlk->m_BlockHeader.bFirstBlock = TRUE; + m_pLastRSBlk->m_BlockHeader.bLastBlock = TRUE; + m_pCurRSBlk = NULL; + } + + goto Exit; + } + + // Compute total number of blocks. + + if( m_pSortStatus) + { + // Estimate total number of unit blocks to be written. + + FLMUINT64 ui64Units = numberOfBlockChains(); + FLMUINT64 ui64Loops; + + m_ui64EstTotalUnits = 0; + for( ui64Loops = ui64Units; ui64Loops > 1; + ui64Loops = (ui64Loops + 1) / 2 ) + { + m_ui64EstTotalUnits += ui64Units; + } + } + + // Do the merge sort. + // Keep looping until we have only one block in the result set list. + + while( numberOfBlockChains() > 1) + { + // Allocate two more buffers. Merge will open the 2nd file. + // Exit will free these allocations and close one of the files. + + // Are the 2nd and 3rd buffers allocated? + + if( !m_pucBlockBuf2) + { + if( RC_BAD( rc = f_alloc( m_uiBlkSize, &m_pucBlockBuf2))) + { + goto Exit; + } + } + + if( !m_pucBlockBuf3) + { + if( RC_BAD( rc = f_alloc( m_uiBlkSize, &m_pucBlockBuf3))) + { + goto Exit; + } + } + + // Swap which file is selected as the output file. + + m_bOutput2ndFile = m_bOutput2ndFile ? FALSE : TRUE; + + // Here is the magical call that does all of the work! + + if( RC_BAD( rc = mergeSort())) + { + goto Exit; + } + } + +Exit: + + // If we did a merge sort of multiple blocks then + // free the first and second buffers and close one of the files. + + if( RC_BAD(rc)) + { + f_free( &m_pucBlockBuf1); + m_uiBlockBuf1Len = 0; + } + + f_free( &m_pucBlockBuf2); + f_free( &m_pucBlockBuf3); + + // Close the non-output opened file. Close both on error. + // If m_bFile2Opened then we did a merge - close one file + + if( m_bFile2Opened || RC_BAD( rc)) + { + if( m_bOutput2ndFile || RC_BAD( rc)) + { + if( m_bFile1Opened) + { + m_pMultiFileHdl1->close( TRUE); + m_bFile1Opened = FALSE; + } + + if( m_pMultiFileHdl1) + { + m_pMultiFileHdl1->Release(); + m_pMultiFileHdl1 = NULL; + } + } + + if( !m_bOutput2ndFile || RC_BAD( rc)) + { + if( m_bFile2Opened) + { + m_pMultiFileHdl2->close( TRUE); + m_bFile2Opened = FALSE; + } + + if( m_pMultiFileHdl2) + { + m_pMultiFileHdl2->Release(); + m_pMultiFileHdl2 = NULL; + } + } + } + + if( RC_OK(rc)) + { + FLMUINT64 ui64Pos; + F_ResultSetBlk * pRSBlk; + + m_bFinalizeCalled = TRUE; + m_bEntriesInOrder = TRUE; + + m_ui64TotalEntries = getTotalEntries(); + + // Set the return value for total entries. + + if( pui64TotalEntries) + { + *pui64TotalEntries = m_ui64TotalEntries; + } + + if( !m_ui64TotalEntries) + { + if( m_pCurRSBlk) + { + m_pCurRSBlk->Release(); + } + + m_pCurRSBlk = NULL; + m_pFirstRSBlk = NULL; + m_pLastRSBlk = NULL; + f_free( &m_pucBlockBuf1); + m_uiBlockBuf1Len = 0; + } + + // Set the ui64BlkEntryPosition values in each block. + + for( ui64Pos = 0, pRSBlk = m_pFirstRSBlk; + pRSBlk; + pRSBlk = pRSBlk->m_pNext) + { + pRSBlk->m_ui64BlkEntryPosition = ui64Pos; + ui64Pos += pRSBlk->m_BlockHeader.uiEntryCount; + } + + // Resize the buffer to save space if only one block & in memory. + + if( m_pFirstRSBlk == m_pLastRSBlk && m_pCurRSBlk) + { + FLMBYTE * pucNewBlk; + FLMUINT uiLen = m_pCurRSBlk->bytesUsedInBuffer(); + + if( uiLen != m_uiBlockBuf1Len) + { + if( RC_OK( rc = f_alloc( uiLen, &pucNewBlk))) + { + f_memcpy( pucNewBlk, m_pucBlockBuf1, uiLen); + f_free( &m_pucBlockBuf1); + m_pucBlockBuf1 = pucNewBlk; + m_uiBlockBuf1Len = uiLen; + } + } + + // Need to always do the SetBuffer, because it causes the + // result set to get positioned. + + if( RC_OK( rc)) + { + rc = m_pCurRSBlk->setBuffer( m_pucBlockBuf1, uiLen); + } + } + } + + // else on error finalize leaves the block list in an awful state. + + return( rc); +} + +/***************************************************************************** +Desc: Perform a Merge Sort on a list of result set blocks. This new + algorithm uses two files for the sort. The end result may + be one of the two files. At the end of the sort all old result set + block objects will be freed and only one result set block object + will be left. This RSBlk object will be used for reading the + entries. At this point there are at least 'N' result set block + objects that will be merged into ('N'/2) block objects. +*****************************************************************************/ +RCODE F_ResultSet::mergeSort( void) +{ + RCODE rc = NE_FLM_OK; + F_ResultSetBlk * pBlkList = NULL; + F_ResultSetBlk * pTempBlk; + F_ResultSetBlk * pLeftBlk; + F_ResultSetBlk * pRightBlk; + IF_MultiFileHdl ** ppMultiFileHdl; + + // Set output file and truncate it. + + // OpenFilex() Closes and creats a new file. + // This is prefered over truncating the file because + // if a database gets truncated we will be blamed. + + rc = (m_bOutput2ndFile) + ? openFile( &m_pMultiFileHdl2) + : openFile( &m_pMultiFileHdl1); + + if( RC_BAD( rc)) + { + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + + ppMultiFileHdl = ( m_bOutput2ndFile) + ? &m_pMultiFileHdl2 + : &m_pMultiFileHdl1; + + // Get the list to the RS blocks + + pBlkList = m_pFirstRSBlk; + + // Form an empty list to build. + + m_pFirstRSBlk = m_pLastRSBlk = m_pCurRSBlk = NULL; + + // Read and UNION one or two blocks at a time getting rid of duplicates. + // Reading the entries when performing a union of only one block + // is a lot of work for nothing - but it simplifies the code. + + pTempBlk = pBlkList; + while (pTempBlk) + { + pLeftBlk = pTempBlk; + pRightBlk = pTempBlk->m_pNext; + + while( pRightBlk && !pRightBlk->m_BlockHeader.bFirstBlock) + { + pRightBlk = pRightBlk->m_pNext; + } + + // Allocate a new result set block list and link into the new list. + + if( (m_pCurRSBlk = f_new F_ResultSetBlk) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if( !m_pLastRSBlk) + { + // First time + + m_pFirstRSBlk = m_pLastRSBlk = m_pCurRSBlk; + } + else + { + m_pLastRSBlk->m_pNext = m_pCurRSBlk; + m_pCurRSBlk->m_pPrev = m_pLastRSBlk; + m_pLastRSBlk = m_pCurRSBlk; + } + + m_pCurRSBlk->setup( ppMultiFileHdl, m_pCompare, + m_uiEntrySize, TRUE, m_bDropDuplicates, TRUE); + + // Output to block buffer 1 + + (void)m_pCurRSBlk->setBuffer( m_pucBlockBuf1, m_uiBlkSize ); + if( RC_BAD( rc = pLeftBlk->setBuffer( m_pucBlockBuf2, m_uiBlkSize))) + { + goto Exit; + } + + if( pRightBlk) + { + if( RC_BAD( rc = pRightBlk->setBuffer( m_pucBlockBuf3, m_uiBlkSize))) + { + goto Exit; + } + } + + // pRightBlk may be NULL - will move left block to output. + // Output leftBlk and rightBlk to the output block (m_pCurRSBlk) + + if( RC_BAD(rc = unionBlkLists( pLeftBlk, pRightBlk))) + { + goto Exit; + } + + // Setup for the next loop. + + pTempBlk = pRightBlk ? pRightBlk->m_pNext : NULL; + while( pTempBlk && !pTempBlk->m_BlockHeader.bFirstBlock) + { + pTempBlk = pTempBlk->m_pNext; + } + } + +Exit: + + // Free the working block list. + + pTempBlk = pBlkList; + while( pTempBlk) + { + FLMUINT uiTemp; + + pRightBlk = pTempBlk->m_pNext; + uiTemp = pTempBlk->Release(); + flmAssert( uiTemp == 0); + pTempBlk = pRightBlk; + } + + return( rc); +} + +/***************************************************************************** +Desc: Return the Current entry reference in the result set. +*****************************************************************************/ +RCODE FLMAPI F_ResultSet::getCurrent( + void * pvBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength) +{ + RCODE rc = NE_FLM_OK; + + flmAssert( m_bFinalizeCalled); + + if( !m_pCurRSBlk) + { + rc = RC_SET( NE_FLM_NOT_FOUND); + } + else + { + rc = m_pCurRSBlk->getCurrent( (FLMBYTE *)pvBuffer, uiBufferLength, + puiReturnLength ); + } + + return( rc); +} + +/***************************************************************************** +Desc: Return the next reference in the result set. If the result set + is not positioned then the first entry will be returned. +*****************************************************************************/ +RCODE FLMAPI F_ResultSet::getNext( + void * pvBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength) +{ + RCODE rc = NE_FLM_OK; + + flmAssert( m_bFinalizeCalled); + + // Make sure we are positioned to a block. + + if( !m_pCurRSBlk) + { + m_pCurRSBlk = m_pFirstRSBlk; + if( !m_pCurRSBlk) + { + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + + if( RC_BAD( rc = m_pCurRSBlk->setBuffer( m_pucBlockBuf1, m_uiBlkSize))) + { + goto Exit; + } + } + + rc = m_pCurRSBlk->getNext( (FLMBYTE *)pvBuffer, uiBufferLength, + puiReturnLength ); + + // Position to the next block? + + if( rc == NE_FLM_EOF_HIT) + { + if( m_pCurRSBlk->m_pNext) + { + m_pCurRSBlk->setBuffer( NULL); + m_pCurRSBlk = m_pCurRSBlk->m_pNext; + + if( RC_BAD( rc = m_pCurRSBlk->setBuffer( m_pucBlockBuf1, m_uiBlkSize))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pCurRSBlk->getNext( + (FLMBYTE *)pvBuffer, uiBufferLength, puiReturnLength))) + { + goto Exit; + } + } + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Return the previous reference in the result set. If the result set + is not positioned then the last entry will be returned. +*****************************************************************************/ +RCODE FLMAPI F_ResultSet::getPrev( + void * pvBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength) +{ + RCODE rc; + + flmAssert( m_bFinalizeCalled); + + // Make sure we are positioned to a block. + + if( !m_pCurRSBlk) + { + if( (m_pCurRSBlk = m_pLastRSBlk) == NULL) + { + rc = RC_SET( NE_FLM_BOF_HIT); + goto Exit; + } + + if( RC_BAD( rc = m_pCurRSBlk->setBuffer( m_pucBlockBuf1, m_uiBlkSize))) + { + goto Exit; + } + } + + rc = m_pCurRSBlk->getPrev( (FLMBYTE *)pvBuffer, uiBufferLength, + puiReturnLength ); + + // Position to the previous block? + + if( rc == NE_FLM_BOF_HIT) + { + if( m_pCurRSBlk->m_pPrev) + { + m_pCurRSBlk->setBuffer( NULL); + m_pCurRSBlk = m_pCurRSBlk->m_pPrev; + if( RC_BAD( rc = m_pCurRSBlk->setBuffer( m_pucBlockBuf1, m_uiBlkSize))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pCurRSBlk->getPrev( (FLMBYTE *)pvBuffer, + uiBufferLength, + puiReturnLength))) + { + goto Exit; + } + } + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Return the first reference in the result set. +*****************************************************************************/ +RCODE FLMAPI F_ResultSet::getFirst( + void * pvBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength) +{ + RCODE rc; + + flmAssert( m_bFinalizeCalled); + + if( m_pCurRSBlk != m_pFirstRSBlk) + { + if( m_pCurRSBlk) + { + m_pCurRSBlk->setBuffer( NULL); + } + + m_pCurRSBlk = m_pFirstRSBlk; + + if( RC_BAD( rc = m_pCurRSBlk->setBuffer( m_pucBlockBuf1, m_uiBlkSize))) + { + goto Exit; + } + } + else if( !m_pCurRSBlk) + { + rc = RC_SET( NE_FLM_NOT_FOUND); + goto Exit; + } + + if( RC_BAD( rc = m_pCurRSBlk->getNext( (FLMBYTE *)pvBuffer, + uiBufferLength, puiReturnLength))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Return the last reference in the result set. +*****************************************************************************/ +RCODE FLMAPI F_ResultSet::getLast( + void * pvBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength) +{ + RCODE rc = NE_FLM_OK; + + flmAssert( m_bFinalizeCalled); + + if( m_pCurRSBlk != m_pLastRSBlk) + { + if( m_pCurRSBlk) + { + m_pCurRSBlk->setBuffer( NULL); + } + + m_pCurRSBlk = m_pLastRSBlk; + + if( RC_BAD( rc = m_pCurRSBlk->setBuffer( m_pucBlockBuf1, m_uiBlkSize))) + { + goto Exit; + } + } + else if( !m_pCurRSBlk) + { + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + + if( RC_BAD( rc = m_pCurRSBlk->getPrev( (FLMBYTE *) pvBuffer, + uiBufferLength, puiReturnLength))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Find the matching entry in the result set using the compare routine. + This does a binary search on the list of blocks. +*****************************************************************************/ +RCODE FLMAPI F_ResultSet::findMatch( + const void * pvMatchEntry, // Entry to match + FLMUINT uiMatchEntryLength, // Variable length of above entry + void * pvFoundEntry, // (out) Entry to return + FLMUINT * puiFoundEntryLength) // (out) Length of entry returned +{ + RCODE rc = NE_FLM_OK; + FLMINT iBlkCompare; // 0 if key is/would be in block. + F_ResultSetBlk * pLowBlk; // Used for locating block. + F_ResultSetBlk * pHighBlk; // Low and High are exclusive. + + flmAssert( m_bFinalizeCalled); + + // If not positioned anywhere, position to the midpoint. + // Otherwise, start on the current block we are on. + + if( !m_pCurRSBlk) + { + // m_pFirstRSBlk will be NULL if no entries. + + if( !m_pFirstRSBlk) + { + rc = RC_SET( NE_FLM_NOT_FOUND); + goto Exit; + } + + if( m_pFirstRSBlk == m_pLastRSBlk) + { + m_pCurRSBlk = m_pFirstRSBlk; + } + else + { + m_pCurRSBlk = selectMidpoint( m_pFirstRSBlk, m_pLastRSBlk, FALSE); + } + + if( RC_BAD( rc = m_pCurRSBlk->setBuffer( m_pucBlockBuf1, m_uiBlkSize))) + { + goto Exit; + } + } + + // Set the exclusive low block and high block. + + pLowBlk = m_pFirstRSBlk; + pHighBlk = m_pLastRSBlk; + + // Loop until the correct block is found. + + for( ;;) + { + // Two return value returned: rc and iBlkCompare. + // FindMatch returns NE_FLM_OK if the entry if found in the block. + // It returns NE_FLM_NOT_FOUND if not found in the block. + // uiCompare returns 0 if entry would be within the block. + // otherwise < 0 if previous blocks should be checked + // and > 0 if next blocks should be checked. + + rc = m_pCurRSBlk->findMatch( + (FLMBYTE *) pvMatchEntry, uiMatchEntryLength, + (FLMBYTE *) pvFoundEntry, puiFoundEntryLength, + &iBlkCompare ); + + // Found match or should key be within the block. + + if( RC_OK(rc) || iBlkCompare == 0) + { + goto Exit; + } + + if( iBlkCompare < 0) + { + // Done if the low block + // Keep NE_FLM_NOT_FOUND return code + + if( m_pCurRSBlk == pLowBlk) + { + goto Exit; + } + + // Set the new high block + + pHighBlk = m_pCurRSBlk->m_pPrev; + } + else + { + // Done if we are at the high block + // Keep the NE_FLM_NOT_FOUND return code + + if( m_pCurRSBlk == pHighBlk) + { + goto Exit; + } + + pLowBlk = m_pCurRSBlk->m_pNext; + } + + if( RC_BAD( rc = m_pCurRSBlk->setBuffer( NULL))) + { + goto Exit; + } + + m_pCurRSBlk = selectMidpoint( pLowBlk, pHighBlk, FALSE); + + // Need to set the working buffer. + + if( RC_BAD( rc = m_pCurRSBlk->setBuffer( m_pucBlockBuf1, m_uiBlkSize))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Select the midpoint between two different blocks in a list. + Entries should not be the same value. +*****************************************************************************/ +F_ResultSetBlk * F_ResultSet::selectMidpoint( + F_ResultSetBlk * pLowBlk, + F_ResultSetBlk * pHighBlk, + FLMBOOL bPickHighIfNeighbors) +{ + FLMUINT uiCount; + F_ResultSetBlk * pTempBlk; + + // If the same then return. + + if( pLowBlk == pHighBlk) + { + pTempBlk = pLowBlk; + goto Exit; + } + + // Check if neighbors and use the boolean flag. + + if( pLowBlk->m_pNext == pHighBlk) + { + pTempBlk = (F_ResultSetBlk *)(bPickHighIfNeighbors + ? pHighBlk + : pLowBlk); + goto Exit; + } + + // Count the total blocks exclusive between low and high and add one. + // Check pTempBlk against null to not crash. + + for( pTempBlk = pLowBlk, uiCount = 1; + pTempBlk && (pTempBlk != pHighBlk); + uiCount++) + { + pTempBlk = pTempBlk->m_pNext; + } + + // Check for implementation error - pTempBlk is NULL and handle. + + if( !pTempBlk) + { + flmAssert( 0); + pTempBlk = pLowBlk; + goto Exit; + } + + // Loop to the middle item + // Divide count by 2 + + uiCount >>= 1; + for( pTempBlk = pLowBlk; uiCount > 0; uiCount--) + { + pTempBlk = pTempBlk->m_pNext; + } + +Exit: + + return( pTempBlk); +} + +/***************************************************************************** +Desc: Set the current entry position. +*****************************************************************************/ +RCODE FLMAPI F_ResultSet::setPosition( + FLMUINT64 ui64Position) +{ + RCODE rc = NE_FLM_OK; + F_ResultSetBlk * pInitialBlk = m_pCurRSBlk; + + flmAssert( m_bFinalizeCalled); + + if( ui64Position == RS_POSITION_NOT_SET) + { + // Set out of focus + + if( m_pCurRSBlk) + { + if( RC_BAD( rc = m_pCurRSBlk->setBuffer( NULL))) + { + goto Exit; + } + } + + m_pCurRSBlk = NULL; + goto Exit; + } + + if( !m_pCurRSBlk) + { + m_pCurRSBlk = m_pFirstRSBlk; + } + + // Check for empty result set. + + if( !m_pCurRSBlk) + { + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + + if( ui64Position < m_pCurRSBlk->m_ui64BlkEntryPosition) + { + // Go backwards looking for the correct block. + + do + { + m_pCurRSBlk = m_pCurRSBlk->m_pPrev; + flmAssert( m_pCurRSBlk); + } + while( ui64Position < m_pCurRSBlk->m_ui64BlkEntryPosition); + } + else if( ui64Position >= m_pCurRSBlk->m_ui64BlkEntryPosition + + m_pCurRSBlk->m_BlockHeader.uiEntryCount) + { + // Go forward looking for the correct block. + + do + { + if( !m_pCurRSBlk->m_pNext) + { + // Will set rc to EOF in SetPosition below. + + break; + } + + m_pCurRSBlk = m_pCurRSBlk->m_pNext; + } + while( ui64Position >= m_pCurRSBlk->m_ui64BlkEntryPosition + + m_pCurRSBlk->m_BlockHeader.uiEntryCount); + } + + // Need working buffer out of focus. + + if( pInitialBlk != m_pCurRSBlk) + { + if( pInitialBlk) + { + if( RC_BAD( rc = pInitialBlk->setBuffer( NULL))) + { + goto Exit; + } + } + + // Need working buffer into focus. + + if( RC_BAD( rc = m_pCurRSBlk->setBuffer( m_pucBlockBuf1, m_uiBlkSize))) + { + goto Exit; + } + } + + // Now we are positioned to the correct block. + + if( RC_BAD( rc = m_pCurRSBlk->setPosition( ui64Position))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Return a pointer to the next entry in the list. +*****************************************************************************/ +RCODE F_ResultSet::getNextPtr( + F_ResultSetBlk ** ppCurBlk, + FLMBYTE ** ppucBuffer, + FLMUINT * puiReturnLength) +{ + RCODE rc = NE_FLM_OK; + F_ResultSetBlk * pCurBlk = *ppCurBlk; + F_ResultSetBlk * pNextBlk; + FLMBYTE * pucBuffer; + + flmAssert( pCurBlk); + + while( RC_BAD( rc = pCurBlk->getNextPtr( ppucBuffer, puiReturnLength))) + { + if( rc == NE_FLM_EOF_HIT) + { + if( pCurBlk->m_pNext) + { + pNextBlk = pCurBlk->m_pNext; + if( !pNextBlk->m_BlockHeader.bFirstBlock) + { + pucBuffer = pCurBlk->m_pucBlockBuf; + pCurBlk->setBuffer( NULL ); + pCurBlk = pNextBlk; + if( RC_BAD( rc = pCurBlk->setBuffer( pucBuffer, m_uiBlkSize))) + { + goto Exit; + } + *ppCurBlk = pCurBlk; + continue; + } + } + } + + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Union two block lists into a result output list. This may be + called to union two result sets or to perform the initial merge-sort + on a create result set. + + Performing an N-way merge would be fast when we have over 10K + of entries. However, the code is more complex. +*****************************************************************************/ +RCODE F_ResultSet::unionBlkLists( + F_ResultSetBlk * pLeftBlk, + F_ResultSetBlk * pRightBlk) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucLeftEntry; + FLMBYTE * pucRightEntry; + FLMUINT uiLeftLength; + FLMUINT uiRightLength; + + // If no right block then copy all of the items from the left block + // to the output block. We could optimize this in the future. + + if( !pRightBlk) + { + rc = copyRemainingItems( pLeftBlk); + goto Exit; + } + + // Now the fun begins. Read entries from both lists and union + // while checking the order of the entries. + + if( RC_BAD( rc = getNextPtr( &pLeftBlk, &pucLeftEntry, &uiLeftLength))) + { + if( rc == NE_FLM_EOF_HIT) + { + rc = copyRemainingItems( pRightBlk); + } + + goto Exit; + } + + if( RC_BAD( rc = getNextPtr( &pRightBlk, &pucRightEntry, &uiRightLength))) + { + if( rc == NE_FLM_EOF_HIT) + { + rc = copyRemainingItems( pLeftBlk); + } + + goto Exit; + } + + for (;;) + { + FLMINT iCompare; + + if( RC_BAD(rc = m_pCompare->compare( pucLeftEntry, uiLeftLength, + pucRightEntry, uiRightLength, &iCompare ))) + { + goto Exit; + } + + if( iCompare < 0) + { + // Take the left item. + + if( RC_BAD(rc = addEntry( pucLeftEntry, uiLeftLength))) + { + goto Exit; + } + + if( RC_BAD( rc = getNextPtr( &pLeftBlk, + &pucLeftEntry, &uiLeftLength))) + { + if( rc != NE_FLM_EOF_HIT) + { + goto Exit; + } + + if( RC_BAD( rc = addEntry( pucRightEntry, uiRightLength))) + { + goto Exit; + } + + // Left entries are done - read all of the right entries. + + rc = copyRemainingItems( pRightBlk); + goto Exit; + } + } + else + { + // If equals then drop the right item and continue comparing left. + // WARNING: Don't try to optimize for equals because when one + // list runs out the remaining duplicate entries must be dropped. + // Continuing to compare the duplicate item is the correct way. + + if( iCompare > 0 || !m_bDropDuplicates) + { + // Take the right item. + + if( RC_BAD(rc = addEntry( pucRightEntry, uiRightLength))) + { + goto Exit; + } + } + + if( RC_BAD(rc = getNextPtr( &pRightBlk, + &pucRightEntry, &uiRightLength))) + { + if( rc != NE_FLM_EOF_HIT) + { + goto Exit; + } + + if( RC_BAD(rc = addEntry( pucLeftEntry, uiLeftLength))) + { + goto Exit; + } + + // Right entries are done - read all of the left entries. + + rc = copyRemainingItems( pLeftBlk); + goto Exit; + } + } + } + +Exit: + + if( RC_OK( rc)) + { + // Flush out the output entries. + + rc = m_pCurRSBlk->finalize( TRUE ); + m_pCurRSBlk->setBuffer( NULL); + m_pCurRSBlk = NULL; + + if( m_pSortStatus) + { + RCODE rc2; + + ++m_ui64UnitsDone; + if( RC_BAD( rc2 = m_pSortStatus->reportSortStatus( m_ui64EstTotalUnits, + m_ui64UnitsDone))) + { + if( RC_OK( rc)) + { + rc = rc2; + } + } + } + } + + return( rc); +} + +/***************************************************************************** +Desc: Copy the remaining items from a block list to the output. +*****************************************************************************/ +RCODE F_ResultSet::copyRemainingItems( + F_ResultSetBlk * pCurBlk) +{ + RCODE rc; + FLMBYTE * pucEntry; + FLMUINT uiLength; + + while( RC_OK( rc = getNextPtr( &pCurBlk, &pucEntry, &uiLength))) + { + if( RC_BAD( rc = addEntry( pucEntry, uiLength))) + { + goto Exit; + } + } + + if( rc == NE_FLM_EOF_HIT) + { + rc = NE_FLM_OK; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Closes and deletes one of two files. +*****************************************************************************/ +void F_ResultSet::closeFile( + IF_MultiFileHdl ** ppMultiFileHdl, + FLMBOOL bDelete) +{ + if( ppMultiFileHdl == &m_pMultiFileHdl1) + { + if( m_bFile1Opened) + { + m_pMultiFileHdl1->close( bDelete); + m_bFile1Opened = FALSE; + } + + if( m_pMultiFileHdl1) + { + m_pMultiFileHdl1->Release(); + m_pMultiFileHdl1 = NULL; + } + } + else + { + if( m_bFile2Opened) + { + m_pMultiFileHdl2->close( TRUE); + m_bFile2Opened = FALSE; + } + + if( m_pMultiFileHdl2) + { + m_pMultiFileHdl2->Release(); + m_pMultiFileHdl2 = NULL; + } + } +} + +/***************************************************************************** +Desc: Close the file if previously opened and creates the file. +*****************************************************************************/ +RCODE F_ResultSet::openFile( + IF_MultiFileHdl ** ppMultiFileHdl) +{ + RCODE rc = NE_FLM_OK; + FLMBOOL * pbFileOpened; + char * pszDirPath; + + // Will close and delete if opened, else will do nothing. + + closeFile( ppMultiFileHdl); + + if( ppMultiFileHdl == &m_pMultiFileHdl1) + { + pbFileOpened = &m_bFile1Opened; + pszDirPath = &m_szIoFilePath1 [0]; + } + else + { + pbFileOpened = &m_bFile2Opened; + pszDirPath = &m_szIoFilePath2 [0]; + } + + f_strcpy( pszDirPath, m_szIoDefaultPath); + + if( (*ppMultiFileHdl = f_new F_MultiFileHdl) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = (*ppMultiFileHdl)->createUnique( pszDirPath, + FRSET_FILENAME_EXTENSION))) + { + (*ppMultiFileHdl)->Release(); + *ppMultiFileHdl = NULL; + goto Exit; + } + + *pbFileOpened = TRUE; + +Exit: + + return( rc); +} + +///***************************************************************************** +//Desc: Create and empty data vector and return it's interface... +//*****************************************************************************/ +//RCODE FLMAPI F_DbSystem::createIFResultSet( +// IF_ResultSet ** ppResultSet) +//{ +// RCODE rc = NE_FLM_OK; +// +// if( (*ppResultSet = f_new FResultSet) == NULL) +// { +// rc = RC_SET( NE_FLM_MEM); +// goto Exit; +// } +// +//Exit: +// +// return( rc); +//} + +/***************************************************************************** +Desc: +******************************************************************************/ +F_ResultSetBlk::F_ResultSetBlk() +{ + m_pNext = m_pPrev = NULL; + m_pCompare = NULL; + reset(); +} + +/***************************************************************************** +Desc: Reset a block so it can be reused. +******************************************************************************/ +void F_ResultSetBlk::reset( void) +{ + flmAssert( !m_pNext && !m_pPrev); + + // Initialize all of the member variables + // between this constructor, SetBuffer() and Setup(). + + m_BlockHeader.ui64FilePos = RSBLK_UNSET_FILE_POS; + m_BlockHeader.uiEntryCount = 0; + m_ppMultiFileHdl = NULL; + m_ui64BlkEntryPosition = RS_POSITION_NOT_SET; + m_iEntryPos = 0; + m_bDuplicateFound = FALSE; + m_bPositioned = FALSE; + m_bModifiedEntry = FALSE; + m_pucBlockBuf = NULL; +} + +/***************************************************************************** +Desc: +******************************************************************************/ +void F_ResultSetBlk::setup( + IF_MultiFileHdl ** ppMultiFileHdl, + IF_ResultSetCompare * pCompare, + FLMUINT uiEntrySize, + FLMBOOL bFirstInList, + FLMBOOL bDropDuplicates, + FLMBOOL bEntriesInOrder) +{ + flmAssert( ppMultiFileHdl); + m_ppMultiFileHdl = ppMultiFileHdl; + + if( m_pCompare) + { + m_pCompare->Release(); + } + + if( (m_pCompare = pCompare) != NULL) + { + m_pCompare->AddRef(); + } + + m_uiEntrySize = uiEntrySize; + m_BlockHeader.bFirstBlock = bFirstInList; + m_BlockHeader.bLastBlock = FALSE; + m_bFixedEntrySize = m_uiEntrySize ? TRUE : FALSE; + + if( !m_uiEntrySize) + { + m_uiEntrySize = sizeof( F_VAR_HEADER); + } + + m_bDropDuplicates = bDropDuplicates; + m_bEntriesInOrder = bEntriesInOrder; +} + +/***************************************************************************** +Desc: The buffer is NOT allocated the by the result set block object. + Setup the pucBuffer and associated variables. Read in the data + for this block if necessary. If NULL is passed in as pucBuffer + then this block is not the active block anymore. +Notes: Must be called before other methods below are called. +*****************************************************************************/ +RCODE F_ResultSetBlk::setBuffer( + FLMBYTE * pucBuffer, // Working buffer or NULL + FLMUINT uiBufferLength) // Default value is RSBLK_BLOCK_SIZE. +{ + RCODE rc = NE_FLM_OK; + + // If a buffer is defined then read in the data from disk. + + if( pucBuffer) + { + m_pucBlockBuf = pucBuffer; + if( !m_BlockHeader.uiEntryCount) + { + // uiBlockSize is the final block size after squeeze. + // uiLengthRemaining is working value of bytes available. + + m_BlockHeader.uiBlockSize = uiBufferLength; + m_uiLengthRemaining = uiBufferLength; + + if( m_bFixedEntrySize) + { + m_pucEndPoint = m_pucBlockBuf; + } + else + { + m_pucEndPoint = m_pucBlockBuf + uiBufferLength; + } + } + else + { + // Read in the data if necessary. + + if( RC_BAD( rc = read())) + { + goto Exit; + } + } + + // The block is now in focus + + m_bPositioned = TRUE; + } + else + { + // Deactivating block so the buffer can be reused. + // Check if the block has been modified + + if( m_bModifiedEntry) + { + // Is this a lone block? + + if( !m_BlockHeader.bLastBlock || !m_BlockHeader.bFirstBlock) + { + if( RC_BAD( rc = write())) + { + goto Exit; + } + } + m_bModifiedEntry = FALSE; + } + + // The block is now out of focus + + m_bPositioned = FALSE; + m_pucEndPoint = m_pucBlockBuf = NULL; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Add a variable length entry to the result set. If fixed length + entry then call AddEntry for fixed length entries. +*****************************************************************************/ +RCODE F_ResultSetBlk::addEntry( + FLMBYTE * pucEntry, + FLMUINT uiEntryLength) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiAlignLength; + F_VAR_HEADER * pEntry; + + flmAssert( m_pucBlockBuf); + + // Was setup called for fixed length entries? + + if( m_bFixedEntrySize ) + { + rc = addEntry( pucEntry ); + goto Exit; + } + + uiAlignLength = (uiEntryLength + FLM_ALLOC_ALIGN) & (~FLM_ALLOC_ALIGN); + + // Check to see if the current buffer will overflow. + + if( m_uiLengthRemaining < uiAlignLength + sizeof( F_VAR_HEADER)) + { + // Caller should call Flush and setup correctly what to do next. + + rc = RC_SET( NE_FLM_EOF_HIT ); + goto Exit; + } + + // Copy entry and compute the offset value for pNextEntryPtr. + + m_pucEndPoint -= uiAlignLength; + f_memcpy( m_pucEndPoint, pucEntry, uiEntryLength ); + + pEntry = ((F_VAR_HEADER *)m_pucBlockBuf) + m_BlockHeader.uiEntryCount; + pEntry->ui32Offset = (FLMUINT32)(m_pucEndPoint - m_pucBlockBuf); + pEntry->ui32Length = (FLMUINT32)uiEntryLength; + + m_uiLengthRemaining -= (uiAlignLength + sizeof( F_VAR_HEADER)); + m_BlockHeader.uiEntryCount++; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Add a fixed length entry to the result set. +*****************************************************************************/ +RCODE F_ResultSetBlk::addEntry( + FLMBYTE * pucEntry) +{ + RCODE rc = NE_FLM_OK; + + // Check that setup was called for fixed length entries. + + flmAssert( m_bFixedEntrySize); + + // Check to see if the current buffer is full. + + if( m_uiLengthRemaining < m_uiEntrySize) + { + // Caller should call Flush and setup correctly what to do next. + + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + + f_memcpy( m_pucBlockBuf + (m_uiEntrySize * m_BlockHeader.uiEntryCount), + pucEntry, m_uiEntrySize); + m_BlockHeader.uiEntryCount++; + m_pucEndPoint += m_uiEntrySize; + m_uiLengthRemaining -= m_uiEntrySize; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Modify the current entry being references. +Notes: The size of each block cannot be modified. This is to allow + writing to the same location on disk and not waste disk memory. +*****************************************************************************/ +RCODE F_ResultSetBlk::modifyEntry( + FLMBYTE * pucEntry, + FLMUINT uiEntryLength) +{ + RCODE rc = NE_FLM_OK; + + F_UNREFERENCED_PARM( uiEntryLength); + + flmAssert( m_pucBlockBuf); + + // The incoming entry MUST be the same size. + + if( m_bFixedEntrySize ) + { + // Assert that the entry length must be zero. + // If not - still use m_uiEntrySize; + + flmAssert( !uiEntryLength); + + // Copy over the current item. + + f_memcpy( &m_pucBlockBuf [m_iEntryPos * m_uiEntrySize], + pucEntry, m_uiEntrySize ); + } + else + { + // Variable Length + + F_VAR_HEADER * pCurEntry; + + pCurEntry = ((F_VAR_HEADER *)m_pucBlockBuf) + m_iEntryPos; + + // We cannot support changing the entry size at this time. + + flmAssert( uiEntryLength == (FLMUINT)pCurEntry->ui32Length); + + f_memcpy( m_pucBlockBuf + pCurEntry->ui32Offset, + pucEntry, uiEntryLength); + } + + m_bModifiedEntry = TRUE; + return( rc); +} + +/***************************************************************************** +Desc: The block is full and need to flush the block to disk. If + bForceWrite is FALSE then will not write block to disk. +*****************************************************************************/ +RCODE F_ResultSetBlk::flush( + FLMBOOL bLastBlockInList, // Last block in a block list. + FLMBOOL bForceWrite) // if TRUE write out to disk. +{ + RCODE rc = NE_FLM_OK; + + // Make sure SetBuffer was called + + flmAssert( m_pucBlockBuf); + squeezeSpace(); + + if( !m_bEntriesInOrder) + { + // Remove duplicate entries. + + if( RC_BAD( rc = sortAndRemoveDups())) + { + goto Exit; + } + } + + m_bEntriesInOrder = TRUE; + m_BlockHeader.bLastBlock = bLastBlockInList; + + if( bForceWrite) + { + if( RC_BAD( rc = write())) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: If there is length remaining, squeeze out additional space. +*****************************************************************************/ +void F_ResultSetBlk::squeezeSpace( void) +{ + FLMUINT uiPos; + + // Fixed Entry Size? + + if( m_bFixedEntrySize) + { + // Yes, no need to squeeze out any space. + + goto Exit; + } + + // Is there room to shift things down? + // Don't do if no entries or if less than 64 bytes. + + if( m_uiLengthRemaining >= 64 && m_BlockHeader.uiEntryCount) + { + FLMUINT uiBytesToMoveUp; + F_VAR_HEADER * pEntry; + + uiBytesToMoveUp = m_uiLengthRemaining; + m_uiLengthRemaining = 0; + + // Overlapping memory move call. + + flmAssert( (m_pucBlockBuf + m_BlockHeader.uiBlockSize) > m_pucEndPoint ); + flmAssert( uiBytesToMoveUp < m_BlockHeader.uiBlockSize ); + + f_memmove( m_pucEndPoint - uiBytesToMoveUp, m_pucEndPoint, + (FLMUINT) ((m_pucBlockBuf + m_BlockHeader.uiBlockSize ) - m_pucEndPoint )); + + m_BlockHeader.uiBlockSize -= uiBytesToMoveUp; + m_pucEndPoint -= uiBytesToMoveUp; + + // Change all of the offsets for every entry. This is expensive. + + for( uiPos = 0, pEntry = (F_VAR_HEADER *)m_pucBlockBuf; + uiPos < m_BlockHeader.uiEntryCount; + pEntry++, uiPos++) + { + pEntry->ui32Offset -= (FLMUINT32)uiBytesToMoveUp; + } + } + +Exit: + + return; +} + +/***************************************************************************** +Desc: Sort the current block and remove all duplicates. +*****************************************************************************/ +RCODE F_ResultSetBlk::sortAndRemoveDups( void) +{ + RCODE rc = NE_FLM_OK; + + // Nothing to do if one or zero entries in the block. + + if( m_BlockHeader.uiEntryCount <= 1 || !m_pCompare) + { + goto Exit; + } + + m_bDuplicateFound = FALSE; + if( RC_BAD( rc = quickSort( 0, m_BlockHeader.uiEntryCount - 1))) + { + goto Exit; + } + + // Some users of result sets may not have any duplicates to remove + // or may want the side effect of having duplicates to further + // process the entries like for sorting tracker records. It is up + // to the compare routine to never return 0 in this case. + + // This algorithm is tuned for the case where there are zero or few + // duplicate records. Removing duplicates is expensive in this design. + + if( m_bDropDuplicates && m_bDuplicateFound) + { + FLMUINT uiEntriesRemaining; + FLMINT iCompare; + + if( m_bFixedEntrySize) + { + FLMBYTE * pucEntry; + FLMBYTE * pucNextEntry; + + pucEntry = m_pucBlockBuf; + for( uiEntriesRemaining = m_BlockHeader.uiEntryCount - 1 + ; uiEntriesRemaining > 0 + ; uiEntriesRemaining-- ) + { + pucNextEntry = pucEntry + m_uiEntrySize; + + if( RC_BAD( rc = m_pCompare->compare( pucEntry, m_uiEntrySize, + pucNextEntry, m_uiEntrySize, + &iCompare))) + { + goto Exit; + } + + if( iCompare == 0) + { + removeEntry( pucEntry); + + // Leave pucEntry alone - everyone will scoot down + } + else + { + pucEntry += m_uiEntrySize; + } + } + } + else + { + F_VAR_HEADER * pEntry = (F_VAR_HEADER *)m_pucBlockBuf; + F_VAR_HEADER * pNextEntry; + + for( uiEntriesRemaining = m_BlockHeader.uiEntryCount - 1 + ; uiEntriesRemaining > 0 + ; uiEntriesRemaining-- ) + { + pNextEntry = pEntry + 1; + + if( RC_BAD( rc = m_pCompare->compare( m_pucBlockBuf + pEntry->ui32Offset, + (FLMUINT)pEntry->ui32Length, + m_pucBlockBuf + pNextEntry->ui32Offset, + (FLMUINT)pNextEntry->ui32Length, + &iCompare))) + { + goto Exit; + } + + if( iCompare == 0) + { + removeEntry( (FLMBYTE *)pEntry); + + // Leave pEntry alone - everyone will scoot down + } + else + { + pEntry++; + } + } + } + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Remove the current entry from the block. +*****************************************************************************/ +void F_ResultSetBlk::removeEntry( + FLMBYTE * pucEntry) +{ + if( m_bFixedEntrySize) + { + // Don't like moving zero bytes - check first. + + if( pucEntry + m_uiEntrySize < m_pucEndPoint) + { + // This is really easy - just memmove everyone down. + + f_memmove( pucEntry, pucEntry + m_uiEntrySize, + (FLMUINT)(m_pucEndPoint - pucEntry) - m_uiEntrySize); + } + + m_BlockHeader.uiEntryCount--; + m_BlockHeader.uiBlockSize -= m_uiEntrySize; + m_pucEndPoint -= m_uiEntrySize; + } + else + { + // Variable length entries - much harder + + // Example - remove entry 3 below... + + // [entryOfs1:len][entryOfs2:len][entryOfs3:len][entryOfs4:len] + // [entryData1][entryData2][entryData3][entryData4] + + // Need to reduce EntryOfs1 and entryOfs2 by m_uiEntrySize+entryLen3. + // because these entries are stored AFTER entry 3 - entries are first + // stored going from the back of the block to the front of the block. + // Need to reduce Ofs4 by OFFSET_SIZE. + + F_VAR_HEADER * pEntry = (F_VAR_HEADER *)pucEntry; + FLMUINT uiDeletedOffset = (FLMUINT)pEntry->ui32Offset; + FLMUINT uiTempOffset; + FLMUINT uiDeletedLength = (FLMUINT)pEntry->ui32Length; + F_VAR_HEADER * pCurEntry; + FLMUINT uiPos; + FLMUINT uiMoveBytes; + + flmAssert( m_BlockHeader.uiBlockSize >= + (uiDeletedOffset + uiDeletedLength )); + + uiMoveBytes = (FLMUINT) + (m_BlockHeader.uiBlockSize - (uiDeletedOffset + uiDeletedLength)); + + if( uiMoveBytes) + { + + // First move down the variable length entry data. + + f_memmove( m_pucBlockBuf + uiDeletedOffset, + m_pucBlockBuf + uiDeletedOffset + uiDeletedLength, + uiMoveBytes ); + } + + flmAssert( m_BlockHeader.uiBlockSize >= + (FLMUINT)((FLMBYTE *)(&pEntry[1]) - m_pucBlockBuf) ); + + uiMoveBytes = m_BlockHeader.uiBlockSize - + (FLMUINT)((FLMBYTE *)(&pEntry [1]) - m_pucBlockBuf); + + if( uiMoveBytes) + { + f_memmove( pEntry, &pEntry[1], uiMoveBytes ); + } + + m_BlockHeader.uiBlockSize -= (uiDeletedLength + sizeof( F_VAR_HEADER)); + + // Adjust the offset values. + + m_BlockHeader.uiEntryCount--; + + for( uiPos = 0, pCurEntry = (F_VAR_HEADER *)m_pucBlockBuf + ; uiPos < m_BlockHeader.uiEntryCount + ; uiPos++, pCurEntry++) + { + // Assume that the offsets are NOT in descending order. + // This will help in the future additional adding and deleting + // to an existing result set. + + uiTempOffset = (FLMUINT)pCurEntry->ui32Offset; + if (uiTempOffset > uiDeletedOffset) + { + uiTempOffset -= uiDeletedLength; + } + uiTempOffset -= sizeof( F_VAR_HEADER); + pCurEntry->ui32Offset = (FLMUINT32)uiTempOffset; + } + } +} + +/***************************************************************************** +Desc: Quick sort an array of values. +Notes: Optimized the above quicksort algorithm. On page 559 the book + suggests that "The worst case can sometimes be avioded by choosing + more carefully the record for final placement at each state." + This algorithm picks a mid point for the compare value. Doing + this helps the worst case where the entries are in order. In Order + tests went from 101 seconds down to 6 seconds! + This helps the 'in order' sorts from worst case Order(N^^2)/2 with + the normal quickSort to Order(NLog2 N) for the worst case. + Also optimized the number of recursions to Log2 N from (N-2). + Will recurse the SMALLER side and will iterate to the top of + the routine for the LARGER side. Follow comments below. +*****************************************************************************/ +RCODE F_ResultSetBlk::quickSort( + FLMUINT uiLowerBounds, + FLMUINT uiUpperBounds) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucEntryTbl = m_pucBlockBuf; + FLMBYTE * pucCurEntry; + FLMUINT uiLBPos; + FLMUINT uiUBPos; + FLMUINT uiMIDPos; + FLMUINT uiLeftItems; + FLMUINT uiRightItems; + FLMINT iCompare; + FLMUINT uiEntrySize = m_uiEntrySize; + FLMBYTE ucaSwapBuffer[ RS_MAX_FIXED_ENTRY_SIZE]; + +#define RS_SWAP(pTbl,pos1,pos2) { \ + f_memcpy( ucaSwapBuffer, &pTbl[pos2*uiEntrySize], uiEntrySize); \ + f_memcpy( &pTbl[ pos2 * uiEntrySize ], &pTbl[ pos1 * uiEntrySize ], uiEntrySize ); \ + f_memcpy( &pTbl[ pos1 * uiEntrySize ], ucaSwapBuffer, uiEntrySize ); } + +Iterate_Larger_Half: + + uiUBPos = uiUpperBounds; + uiLBPos = uiLowerBounds; + uiMIDPos = (uiUpperBounds + uiLowerBounds + 1) / 2; + pucCurEntry = &pucEntryTbl[ uiMIDPos * uiEntrySize ]; + + for (;;) + { + // Don't compare with target + + while( uiLBPos == uiMIDPos || + (RC_OK( rc = entryCompare( &pucEntryTbl[ uiLBPos * uiEntrySize], + pucCurEntry, + &iCompare)) && + iCompare < 0)) + { + if( uiLBPos >= uiUpperBounds) + { + break; + } + uiLBPos++; + } + + if( RC_BAD( rc)) + { + goto Exit; + } + + // Don't compare with target + + while( uiUBPos == uiMIDPos || + (RC_OK( rc = entryCompare( pucCurEntry, + &pucEntryTbl[uiUBPos * uiEntrySize], + &iCompare)) && + iCompare < 0)) + { + // Check for underflow + + if( !uiUBPos) + { + break; + } + + uiUBPos--; + } + + if (RC_BAD( rc)) + { + goto Exit; + } + + // Interchange and continue loop + + if( uiLBPos < uiUBPos) + { + // Interchange [uiLBPos] with [uiUBPos]. + + RS_SWAP( pucEntryTbl, uiLBPos, uiUBPos ); + uiLBPos++; // Scan from left to right. + uiUBPos--; // Scan from right to left. + } + else + { + // Past each other - done + + break; + } + } + + // 5 cases to check. + // 1) UB < MID < LB - Don't need to do anything. + // 2) MID < UB < LB - swap( UB, MID ) + // 3) UB < LB < MID - swap( LB, MID ) + // 4) UB = LB < MID - swap( LB, MID ) - At first position + // 5) MID < UB = LB - swap( UB, MID ) - At last position + + // Check for swap( LB, MID ) - cases 3 and 4 + + if( uiLBPos < uiMIDPos) + { + // Interchange [uiLBPos] with [uiMIDPos] + + RS_SWAP( pucEntryTbl, uiMIDPos, uiLBPos ); + uiMIDPos = uiLBPos; + } + else if (uiMIDPos < uiUBPos) + { + // Cases 2 and 5 + // Interchange [uUBPos] with [uiMIDPos] + + RS_SWAP( pucEntryTbl, uiMIDPos, uiUBPos ); + uiMIDPos = uiUBPos; + } + + // To save stack space - recurse the SMALLER Piece. For the larger + // piece goto the top of the routine. Worst case will be + // (Log2 N) levels of recursion. + + // Don't recurse in the following cases: + // 1) We are at an end. Just loop to the top. + // 2) There are two on one side. Compare and swap. Loop to the top. + // Don't swap if the values are equal. There are many recursions + // with one or two entries. This doesn't speed up any so it is + // commented out. + + // Check the left piece. + + uiLeftItems = (uiLowerBounds + 1 < uiMIDPos ) + ? uiMIDPos - uiLowerBounds // 2 or more + : 0; + uiRightItems = (uiMIDPos + 1 < uiUpperBounds ) + ? uiUpperBounds - uiMIDPos // 2 or more + : 0; + + if( uiLeftItems < uiRightItems) + { + // Recurse on the LEFT side and goto the top on the RIGHT side. + + if( uiLeftItems) + { + // Recursive call. + + if( RC_BAD( rc = quickSort( uiLowerBounds, uiMIDPos - 1))) + { + goto Exit; + } + } + + uiLowerBounds = uiMIDPos + 1; + goto Iterate_Larger_Half; + } + else if( uiLeftItems) + { + // Recurse on the RIGHT side and goto the top for the LEFT side. + + if( uiRightItems) + { + // Recursive call. + + if( RC_BAD( rc = quickSort( uiMIDPos + 1, uiUpperBounds))) + { + goto Exit; + } + } + + uiUpperBounds = uiMIDPos - 1; + goto Iterate_Larger_Half; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Write this block to disk. +*****************************************************************************/ +RCODE F_ResultSetBlk::write( void) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiBytesWritten; + + // By this time there better be something to write... + // The file should be opened by default. + + if( m_BlockHeader.ui64FilePos == RSBLK_UNSET_FILE_POS) + { + if( RC_BAD(rc = (*m_ppMultiFileHdl)->size( &m_BlockHeader.ui64FilePos))) + { + goto Exit; + } + } + + // Write out the block header definition. + + if( RC_BAD( rc = (*m_ppMultiFileHdl)->write( + m_BlockHeader.ui64FilePos, + sizeof( F_BLOCK_HEADER), &m_BlockHeader, + &uiBytesWritten))) + { + goto Exit; + } + + // Write out the data buffer + + if( RC_BAD( rc = (*m_ppMultiFileHdl)->write( + m_BlockHeader.ui64FilePos + sizeof( F_BLOCK_HEADER), + m_BlockHeader.uiBlockSize, + m_pucBlockBuf, + &uiBytesWritten))) + { + goto Exit; + } + +Exit: + + return rc; +} + +/***************************************************************************** +Desc: Read in the specified block into memory. +*****************************************************************************/ +RCODE F_ResultSetBlk::read( void) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiBytesRead; + F_BLOCK_HEADER BlockHeader; + + // Nothing to do? + + if (m_BlockHeader.ui64FilePos == RSBLK_UNSET_FILE_POS) + { + goto Exit; + } + + // First read the block header in. + + if (RC_BAD( rc = (*m_ppMultiFileHdl)->read( m_BlockHeader.ui64FilePos, + sizeof( F_BLOCK_HEADER ), + (void *)&BlockHeader, &uiBytesRead))) + { + goto Exit; + } + + // Verify that the block header data is the same. + // This is the best we can do to verify that the file handle + // is not junky. + + if (BlockHeader.ui64FilePos != m_BlockHeader.ui64FilePos || + BlockHeader.uiEntryCount != m_BlockHeader.uiEntryCount) + { + rc = RC_SET( NE_FLM_FAILURE); + goto Exit; + } + + // Read in the data buffer + + if (RC_BAD( rc = (*m_ppMultiFileHdl)->read( + m_BlockHeader.ui64FilePos + sizeof( F_BLOCK_HEADER), + m_BlockHeader.uiBlockSize, + m_pucBlockBuf, &uiBytesRead))) + { + goto Exit; + } + +Exit: + + if (RC_OK(rc)) + { + m_bPositioned = TRUE; + m_iEntryPos = -1; + } + + return( rc); +} + +/***************************************************************************** +Desc: Copies the current entry into the user buffer. Checks for overflow. +*****************************************************************************/ +RCODE F_ResultSetBlk::copyCurrentEntry( + FLMBYTE * pucBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiEntrySize; + F_VAR_HEADER * pEntry; + FLMBYTE * pucEntry; + + flmAssert( pucBuffer); + + // Copy the current entry. This is a shared routine + // because the code to copy an entry is a little complicated. + + if( !m_bFixedEntrySize) + { + pEntry = ((F_VAR_HEADER *)m_pucBlockBuf) + m_iEntryPos; + uiEntrySize = pEntry->ui32Length; + pucEntry = m_pucBlockBuf + pEntry->ui32Offset; + } + else + { + uiEntrySize = m_uiEntrySize; + pucEntry = &m_pucBlockBuf[ m_iEntryPos * uiEntrySize ]; + } + + if( uiBufferLength && (uiEntrySize > uiBufferLength)) + { + uiEntrySize = uiBufferLength; + rc = RC_SET( NE_FLM_CONV_DEST_OVERFLOW ); + // Fall through into memcpy. + } + + f_memcpy( pucBuffer, pucEntry, uiEntrySize); + + if( puiReturnLength) + { + *puiReturnLength = uiEntrySize; + } + + return( rc); +} + +/***************************************************************************** +Desc: Return the Current entry reference in the result set. +*****************************************************************************/ +RCODE F_ResultSetBlk::getCurrent( + FLMBYTE * pBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength) +{ + RCODE rc; + + flmAssert( m_pucBlockBuf); + if( !m_bPositioned ) + { + rc = RC_SET( NE_FLM_NOT_FOUND); + goto Exit; + } + + // Check for EOF and BOF conditions - otherwise return current. + + if (m_iEntryPos >= (FLMINT) m_BlockHeader.uiEntryCount) + { + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + + if( m_iEntryPos == -1) + { + rc = RC_SET( NE_FLM_BOF_HIT ); + goto Exit; + } + + rc = copyCurrentEntry( pBuffer, uiBufferLength, puiReturnLength ); + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Return a pointer to the next reference in the result set. + If the result set is not positioned then the first entry will + be returned. +*****************************************************************************/ +RCODE F_ResultSetBlk::getNextPtr( + FLMBYTE ** ppucBuffer, + FLMUINT * puiReturnLength) +{ + RCODE rc = NE_FLM_OK; + + flmAssert( ppucBuffer); + flmAssert( puiReturnLength); + flmAssert( m_bPositioned); + + // Are we on the last entry or past the last entry? + + if (m_iEntryPos + 1 >= (FLMINT) m_BlockHeader.uiEntryCount) + { + m_iEntryPos = (FLMINT)m_BlockHeader.uiEntryCount; + rc = RC_SET( NE_FLM_EOF_HIT ); + goto Exit; + } + + // Position to the next entry + + m_iEntryPos++; + + if (!m_bFixedEntrySize) + { + F_VAR_HEADER * pEntry; + + pEntry = ((F_VAR_HEADER *)m_pucBlockBuf) + m_iEntryPos; + *puiReturnLength = (FLMUINT)pEntry->ui32Length; + *ppucBuffer = m_pucBlockBuf + pEntry->ui32Offset; + } + else + { + *puiReturnLength = m_uiEntrySize; + *ppucBuffer = &m_pucBlockBuf[ m_iEntryPos * m_uiEntrySize]; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Return the previous reference in the result set. If the result set + is not positioned then the last entry will be returned. +*****************************************************************************/ +RCODE F_ResultSetBlk::getPrev( + FLMBYTE * pucBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength) +{ + RCODE rc = NE_FLM_OK; + + flmAssert( m_bPositioned); + + // If not positioned then position past last entry. + + if (m_iEntryPos == -1) + { + m_iEntryPos = (FLMINT) m_BlockHeader.uiEntryCount; + } + + // Are we on the first entry or before the first entry? + + if (m_iEntryPos == 0) + { + m_iEntryPos = -1; + rc = RC_SET( NE_FLM_BOF_HIT); + goto Exit; + } + + m_iEntryPos--; // position to previous entry. + + if (RC_BAD( rc = copyCurrentEntry( pucBuffer, uiBufferLength, + puiReturnLength))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Set the current entry position for this block. +*****************************************************************************/ +RCODE F_ResultSetBlk::setPosition( + FLMUINT64 ui64Position) +{ + RCODE rc = NE_FLM_OK; + + // Buffer must be set or SetBuffer() will set iEntryPos back to -1. + + flmAssert( m_bPositioned); + + if( ui64Position == RS_POSITION_NOT_SET) + { + m_iEntryPos = -1; + goto Exit; + } + + flmAssert( ui64Position >= m_ui64BlkEntryPosition); + + // Convert to a zero based number relative to this block. + + if (ui64Position >= m_ui64BlkEntryPosition) + { + ui64Position -= m_ui64BlkEntryPosition; + } + else + { + ui64Position = 0; + } + + if (ui64Position >= m_BlockHeader.uiEntryCount) + { + rc = RC_SET( NE_FLM_EOF_HIT); + m_iEntryPos = m_BlockHeader.uiEntryCount; + } + else + { + m_iEntryPos = (FLMINT)ui64Position; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Find the matching entry within the block using the compare routine. + This does a binary search on entries. Watch the (out) variable. +Out: *piCompare = 0 - match found OR entry would compare between + the low and high entries in the block. + > 0 - match entry is greater than + the highest entry in the block. + < 0 - match entry is less than the + lowest entry in the block. +Notes: One side effect is that m_iEntryPos is set to the matched + entry or some random entry if not found is returned. +*****************************************************************************/ +RCODE F_ResultSetBlk::findMatch( // Find and return an etnry that + // matches in this block. + FLMBYTE * pucMatchEntry, // Entry to match + FLMUINT uiMatchEntryLength, // Variable length of above entry + FLMBYTE * pucFoundEntry, // (out) Entry to return + FLMUINT * puiFoundEntryLength, // (out) Length of entry returned + FLMINT * piCompare) // See comments above. +{ + RCODE rc = NE_FLM_OK; + FLMINT iCompare; // Return from CompareEntry + FLMUINT uiLow; + FLMUINT uiHigh; + FLMUINT uiMid; + FLMUINT uiLimit; + + uiLow = 0; + uiHigh = uiLimit = m_BlockHeader.uiEntryCount - 1; + + // Set the match entry length + + if (!uiMatchEntryLength) + { + uiMatchEntryLength = m_uiEntrySize; + } + + // Check the first and last entries in the block. + // Copy the current entry if found. + + if( RC_BAD( rc = compareEntry( pucMatchEntry, uiMatchEntryLength, uiLow, + &iCompare))) + { + goto Exit; + } + + if( iCompare <= 0) + { + if( iCompare < 0) + { + rc = RC_SET( NE_FLM_NOT_FOUND); + } + else + { + if( pucFoundEntry) + { + rc = copyCurrentEntry( pucFoundEntry, 0, puiFoundEntryLength); + } + } + + *piCompare = iCompare; + goto Exit; + } + + if( RC_BAD( rc = compareEntry( pucMatchEntry, uiMatchEntryLength, uiHigh, + &iCompare ))) + { + goto Exit; + } + + if (iCompare >= 0) + { + if (iCompare > 0) + { + rc = RC_SET( NE_FLM_NOT_FOUND); + } + else + { + rc = copyCurrentEntry( pucFoundEntry, 0, puiFoundEntryLength); + } + *piCompare = iCompare; + goto Exit; + } + + // Set the iCompare to equals because + // the match entry sorts within the block somewhere. + // Binary search the entries in the block. May still + // not find the matching entry. + + *piCompare = 0; + for( ;;) + { + uiMid = (uiLow + uiHigh) >> 1; // (uiLow + uiHigh) / 2 + + if( RC_BAD( rc = compareEntry( pucMatchEntry, uiMatchEntryLength, uiMid, + &iCompare))) + { + goto Exit; + } + + if( iCompare == 0) + { + // Found Match! All set up to return. + + if( pucFoundEntry) + { + rc = copyCurrentEntry( pucFoundEntry, 0, puiFoundEntryLength); + } + + goto Exit; + } + + // Check if we are done - where uiLow >= uiHigh. + + if( uiLow >= uiHigh) + { + // Done - item not found + + break; + } + + if( iCompare < 0) + { + // Way too high? + + if( !uiMid) + { + break; + } + + // Too high + + uiHigh = uiMid - 1; + } + else + { + if( uiMid == uiLimit) + { + // Done - hit the top + break; + } + + // Too low + + uiLow = uiMid + 1; + } + } + + // On break set we did not find the matching entry. + + rc = RC_SET( NE_FLM_NOT_FOUND); + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Compare the buffer entry with entry identifies by + uiEntryPos. +Out: *piCompare = 0 - match found OR entry value would compare + between the low and high entries. + > 0 - match entry is greater than + the highest entry in the block. + < 0 - match entry is less than the + lowest entry in the block. +*****************************************************************************/ +RCODE F_ResultSetBlk::compareEntry( // Compares match entry with entry + // identified by uiEntryPos. + FLMBYTE * pucMatchEntry, // Entry to match + FLMUINT uiMatchEntryLength, // Variable length of pMatchEntry. + FLMUINT uiEntryPos, // Position of entry in block. + FLMINT * piCompare) // Return from compare. +{ + FLMBYTE * pucEntry; + FLMUINT uiEntrySize; + + // Position to the entry. + + m_iEntryPos = (FLMINT) uiEntryPos; + + if (!m_bFixedEntrySize) + { + F_VAR_HEADER * pEntry; + + pEntry = ((F_VAR_HEADER *)m_pucBlockBuf) + m_iEntryPos; + uiEntrySize = (FLMUINT)pEntry->ui32Length; + pucEntry = m_pucBlockBuf + pEntry->ui32Offset; + } + else + { + uiEntrySize = m_uiEntrySize; + pucEntry = &m_pucBlockBuf[ m_iEntryPos * uiEntrySize ]; + } + + return( m_pCompare->compare( pucMatchEntry, uiMatchEntryLength, + pucEntry, uiEntrySize, + piCompare)); +} + +/***************************************************************************** +Desc: Make sure the state reflects what we have in the blocks. +*****************************************************************************/ +void F_ResultSetBlk::adjustState( + FLMUINT uiBlkBufferSize) +{ + F_VAR_HEADER * pVarHdr; + FLMUINT uiTotalSize = 0; + FLMBYTE * pucFromPos; + FLMBYTE * pucToPos; + FLMUINT uiBytesMoved; + FLMUINT uiPos; + + // Are the entries in the block fixed length or variable length? + + if( m_bFixedEntrySize) + { + // Fixed Length. + + m_uiLengthRemaining = uiBlkBufferSize - + (m_BlockHeader.uiEntryCount * m_uiEntrySize); + m_ui64BlkEntryPosition = 0; + m_pucEndPoint = m_pucBlockBuf + (m_BlockHeader.uiEntryCount * m_uiEntrySize); + } + else + { + // Variable length Entries. + // We may need to move the entries around. First, determine if the block is full. + + if( m_BlockHeader.uiBlockSize < uiBlkBufferSize) + { + uiTotalSize = m_BlockHeader.uiBlockSize - + (sizeof(F_VAR_HEADER) * m_BlockHeader.uiEntryCount); + + pucFromPos = m_pucBlockBuf + (sizeof(F_VAR_HEADER) * m_BlockHeader.uiEntryCount); + + pucToPos = (m_pucBlockBuf + uiBlkBufferSize) - uiTotalSize; + + f_memmove( pucToPos, pucFromPos, uiTotalSize); + + for( uiBytesMoved = (pucToPos - pucFromPos), + uiPos = 0, + pVarHdr = (F_VAR_HEADER *)m_pucBlockBuf; + uiPos < m_BlockHeader.uiEntryCount; + pVarHdr++, uiPos++) + { + pVarHdr->ui32Offset += (FLMUINT32)uiBytesMoved; + } + + m_pucEndPoint = pucToPos; + m_uiLengthRemaining = uiBlkBufferSize - m_BlockHeader.uiBlockSize; + m_ui64BlkEntryPosition = pucToPos - m_pucBlockBuf; + } + else + { + m_uiLengthRemaining = 0; + } + } + + m_BlockHeader.uiBlockSize = uiBlkBufferSize; +} + +/***************************************************************************** +Desc: truncate the file to the current file position. +*****************************************************************************/ +RCODE F_ResultSetBlk::truncate( + FLMBYTE * pszPath) +{ + RCODE rc = NE_FLM_OK; + + if( RC_BAD( rc = (*m_ppMultiFileHdl)->truncate( + m_BlockHeader.ui64FilePos))) + { + goto Exit; + } + + (*m_ppMultiFileHdl)->close( FALSE); + + if( RC_BAD( rc = (*m_ppMultiFileHdl)->open( ( char *)pszPath))) + { + goto Exit; + } + + m_BlockHeader.ui64FilePos = RSBLK_UNSET_FILE_POS; + +Exit: + + return( rc); +} diff --git a/ftk/src/ftksem.cpp b/ftk/src/ftksem.cpp new file mode 100644 index 0000000..b6e4a33 --- /dev/null +++ b/ftk/src/ftksem.cpp @@ -0,0 +1,402 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains mutex and semaphore functions +// +// Tabs: 3 +// +// Copyright (c) 2000-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: ftksem.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +#ifdef FLM_WIN + + typedef struct + { + FLMATOMIC locked; +#ifdef FLM_DEBUG + FLMUINT uiThreadId; + FLMATOMIC lockedCount; + FLMATOMIC waitCount; +#endif + } F_INTERLOCK; + +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_WIN) +RCODE FLMAPI f_mutexCreate( + F_MUTEX * phMutex) +{ + flmAssert( phMutex != NULL); + + if( (*phMutex = (F_MUTEX)malloc( sizeof( F_INTERLOCK))) == F_MUTEX_NULL) + { + return( RC_SET( NE_FLM_MEM)); + } + + ((F_INTERLOCK *)(*phMutex))->locked = 0; +#ifdef FLM_DEBUG + ((F_INTERLOCK *)(*phMutex))->uiThreadId = 0; + ((F_INTERLOCK *)(*phMutex))->lockedCount = 0; + ((F_INTERLOCK *)(*phMutex))->waitCount = 0; +#endif + + return( NE_FLM_OK); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_WIN) +void FLMAPI f_mutexDestroy( + F_MUTEX * phMutex) +{ + flmAssert( phMutex != NULL); + + if (*phMutex != F_MUTEX_NULL) + { + free( *phMutex); + *phMutex = F_MUTEX_NULL; + } +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_UNIX) +RCODE FLMAPI f_mutexCreate( + F_MUTEX * phMutex) +{ + RCODE rc = NE_FLM_OK; + pthread_mutexattr_t * pMutexAttr = NULL; + + flmAssert( phMutex != NULL); + + // NOTE: Cannot call f_alloc because the memory initialization needs + // to be able to set up mutexes. + + if ((*phMutex = (F_MUTEX)malloc( + sizeof( pthread_mutex_t))) == F_MUTEX_NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + +#if defined( FLM_DEBUG) && defined( FLM_LINUX) + { + pthread_mutexattr_t mutexAttr; + + if( !pthread_mutexattr_init( &mutexAttr)) + { + pMutexAttr = &mutexAttr; + pthread_mutexattr_settype( pMutexAttr, PTHREAD_MUTEX_ERRORCHECK_NP); + } + } +#endif + + if( pthread_mutex_init( *phMutex, pMutexAttr) != 0) + { + // NOTE: Cannot call f_free because we had to use malloc up above due + // to the fact that the memory subsystem uses a mutex before itis + // completely ready to go. + + free( *phMutex); + *phMutex = F_MUTEX_NULL; + rc = RC_SET( NE_FLM_COULD_NOT_CREATE_MUTEX); + goto Exit; + } + +Exit: + + if( pMutexAttr) + { + pthread_mutexattr_destroy( pMutexAttr); + } + + return( rc); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_UNIX) +void FLMAPI f_mutexDestroy( + F_MUTEX * phMutex) +{ + flmAssert( phMutex != NULL); + + if (*phMutex != F_MUTEX_NULL) + { + pthread_mutex_destroy( *phMutex); + + // NOTE: Cannot call f_free because we had to use malloc up above due + // to the fact that the memory subsystem uses a mutex before it is + // completely ready to go. + + free( *phMutex); + *phMutex = F_MUTEX_NULL; + } +} +#endif + +/**************************************************************************** +Desc: Initializes a semaphore handle on UNIX +****************************************************************************/ +#if defined( FLM_UNIX) +FINLINE int sema_init( + sema_t * pSem) +{ + int iErr = 0; + + if( (iErr = pthread_mutex_init( &pSem->lock, NULL)) < 0) + { + goto Exit; + } + + if( (iErr = pthread_cond_init( &pSem->cond, NULL)) < 0) + { + pthread_mutex_destroy( &pSem->lock); + goto Exit; + } + + pSem->count = 0; + +Exit: + + return( iErr); +} +#endif + +/**************************************************************************** +Desc: Frees a semaphore handle on UNIX +****************************************************************************/ +#if defined( FLM_UNIX) +FINLINE void sema_destroy( + sema_t * pSem) +{ + pthread_mutex_destroy( &pSem->lock); + pthread_cond_destroy( &pSem->cond); +} +#endif + +/**************************************************************************** +Desc: Waits for a semaphore to be signaled on UNIX +****************************************************************************/ +#if defined( FLM_UNIX) +FINLINE int sema_wait( + sema_t * pSem) +{ + int iErr = 0; + + pthread_mutex_lock( &pSem->lock); + while( !pSem->count) + { + if( (iErr = pthread_cond_wait( &pSem->cond, &pSem->lock))) + { + if( iErr == EINTR) + { + iErr = 0; + } + else + { + goto Exit; + } + } + } + + pSem->count--; + flmAssert( pSem->count >= 0); + +Exit: + + pthread_mutex_unlock( &pSem->lock); + return( iErr); +} +#endif + +/**************************************************************************** +Desc: Waits a specified number of milliseconds for a semaphore + to be signaled on UNIX +****************************************************************************/ +#if defined( FLM_UNIX) +FINLINE int sema_timedwait( + sema_t * pSem, + unsigned int msecs) +{ + struct timeval now; + struct timespec abstime; + int iErr = 0; + + // If timeout is F_SEM_WAITFOREVER, do sem_wait. + + if( msecs == F_SEM_WAITFOREVER) + { + iErr = sema_wait( pSem); + return( iErr); + } + + pthread_mutex_lock( &pSem->lock); + + gettimeofday( &now, NULL); + abstime.tv_sec = now.tv_sec + ((msecs) ? (msecs / 1000) : 0); + abstime.tv_nsec = ( now.tv_usec + ((msecs % 1000) * 1000)) * 1000; + +Restart: + + while( !pSem->count) + { + if( (iErr = pthread_cond_timedwait( &pSem->cond, + &pSem->lock, &abstime))) + { + if( iErr == EINTR) + { + iErr = 0; + goto Restart; + } + goto Exit; + } + } + + pSem->count--; + flmAssert( pSem->count >= 0); + +Exit: + + pthread_mutex_unlock( &pSem->lock); + return( iErr); +} +#endif + +/**************************************************************************** +Desc: Signals a semaphore on UNIX +****************************************************************************/ +#if defined( FLM_UNIX) +int sema_signal( + sema_t * pSem) +{ + pthread_mutex_lock( &pSem->lock); + pSem->count++; + flmAssert( pSem->count > 0); + pthread_cond_signal( &pSem->cond); + pthread_mutex_unlock( &pSem->lock); + + return( 0); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_UNIX) +RCODE f_semCreate( + F_SEM * phSem) +{ + RCODE rc = NE_FLM_OK; + + flmAssert( phSem != NULL); + + if( RC_BAD( rc = f_alloc( sizeof( sema_t), phSem))) + { + goto Exit; + } + + if( sema_init( *phSem) < 0) + { + f_free( phSem); + *phSem = F_SEM_NULL; + rc = RC_SET( NE_FLM_COULD_NOT_CREATE_SEMAPHORE); + goto Exit; + } + +Exit: + + return( rc); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_UNIX) +void f_semDestroy( + F_SEM * phSem) +{ + flmAssert( phSem != NULL); + + if (*phSem != F_SEM_NULL) + { + sema_destroy( *phSem); + f_free( phSem); + *phSem = F_SEM_NULL; + } +} +#endif + +/**************************************************************************** +Desc: Get the lock on a semaphore - p operation +****************************************************************************/ +#if defined( FLM_UNIX) +RCODE f_semWait( + F_SEM hSem, + FLMUINT uiTimeout) +{ + RCODE rc = NE_FLM_OK; + + flmAssert( hSem != F_SEM_NULL); + + //catch the F_SEM_WAITFOREVER flag so we can directly call sema_wait + //instead of passing F_SEM_WAITFOREVER through to sema_timedwait. + //Note that on AIX the datatype of the uiTimeout (in the timespec + //struct) is surprisingly a signed int, which makes this catch + //essential. + + if( uiTimeout == F_SEM_WAITFOREVER) + { + if( sema_wait( hSem)) + { + rc = RC_SET( NE_FLM_ERROR_WAITING_ON_SEMPAHORE); + } + } + else + { + if( sema_timedwait( hSem, (unsigned int)uiTimeout)) + { + rc = RC_SET( NE_FLM_ERROR_WAITING_ON_SEMPAHORE); + } + } + + return( rc); +} +#endif + +/**************************************************************************** +Desc: Get the lock on a semaphore - p operation +****************************************************************************/ +#if defined( FLM_WATCOM_NLM) +int gv_DummyFtksem(void) +{ + return( 0); +} +#endif diff --git a/ftk/src/ftkstrm.cpp b/ftk/src/ftkstrm.cpp new file mode 100644 index 0000000..61c8ac0 --- /dev/null +++ b/ftk/src/ftkstrm.cpp @@ -0,0 +1,3310 @@ +//------------------------------------------------------------------------------ +// Desc: Streaming interface +// +// Tabs: 3 +// +// Copyright (c) 2004-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: fstream.cpp 3123 2006-01-24 17:19:50 -0700 (Tue, 24 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +#define LZW_MAGIC_NUMBER 0x3482 +#define LZW_END_OF_DATA 256 +#define LZW_NEW_DICT 257 +#define LZW_STOP_COMPRESSION 258 +#define LZW_START_CODE 259 +#define LZW_MAX_CODE 0xFFFF + +#define MULTI_FILE_OUT_STREAM_MIN_FILE_SIZE 1048510 +#define MULTI_FILE_OUT_STREAM_MAX_FILE_SIZE 2147483647 + +FLMBYTE F_Base64EncoderIStream::m_ucEncodeTable[ 64] = +{ + ASCII_UPPER_A, ASCII_UPPER_B, ASCII_UPPER_C, ASCII_UPPER_D, + ASCII_UPPER_E, ASCII_UPPER_F, ASCII_UPPER_G, ASCII_UPPER_H, + ASCII_UPPER_I, ASCII_UPPER_J, ASCII_UPPER_K, ASCII_UPPER_L, + ASCII_UPPER_M, ASCII_UPPER_N, ASCII_UPPER_O, ASCII_UPPER_P, + ASCII_UPPER_Q, ASCII_UPPER_R, ASCII_UPPER_S, ASCII_UPPER_T, + ASCII_UPPER_U, ASCII_UPPER_V, ASCII_UPPER_W, ASCII_UPPER_X, + ASCII_UPPER_Y, ASCII_UPPER_Z, ASCII_LOWER_A, ASCII_LOWER_B, + ASCII_LOWER_C, ASCII_LOWER_D, ASCII_LOWER_E, ASCII_LOWER_F, + ASCII_LOWER_G, ASCII_LOWER_H, ASCII_LOWER_I, ASCII_LOWER_J, + ASCII_LOWER_K, ASCII_LOWER_L, ASCII_LOWER_M, ASCII_LOWER_N, + ASCII_LOWER_O, ASCII_LOWER_P, ASCII_LOWER_Q, ASCII_LOWER_R, + ASCII_LOWER_S, ASCII_LOWER_T, ASCII_LOWER_U, ASCII_LOWER_V, + ASCII_LOWER_W, ASCII_LOWER_X, ASCII_LOWER_Y, ASCII_LOWER_Z, + ASCII_ZERO, ASCII_ONE, ASCII_TWO, ASCII_THREE, + ASCII_FOUR, ASCII_FIVE, ASCII_SIX, ASCII_SEVEN, + ASCII_EIGHT, ASCII_NINE, ASCII_PLUS, ASCII_SLASH +}; + +FLMBYTE F_Base64DecoderIStream::m_ucDecodeTable[ 256] = +{ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0 .. 7 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 8 .. 15 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 16 .. 23 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 24 .. 31 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 32 .. 39 + 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, 0xFF, 0x3F, // 40 .. 47 + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, // 48 .. 55 + 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, // 56 .. 63 + 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // 64 .. 71 + 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, // 72 .. 79 + 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, // 80 .. 87 + 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 88 .. 95 + 0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, // 96 .. 103 + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, // 104 .. 111 + 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, // 112 .. 119 + 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 120 .. 127 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 128 .. 135 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 136 .. 143 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 144 .. 151 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 152 .. 159 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 160 .. 167 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 168 .. 175 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 176 .. 183 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 184 .. 191 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 192 .. 199 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 200 .. 207 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 208 .. 215 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 216 .. 223 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 224 .. 231 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 232 .. 239 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 240 .. 247 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF // 248 .. 255 +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +F_IStream::F_IStream() +{ +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_IStream::~F_IStream() +{ +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_PosIStream::F_PosIStream() +{ +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_PosIStream::~F_PosIStream() +{ +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_OStream::F_OStream() +{ +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_OStream::~F_OStream() +{ +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FLMAPI F_FileIStream::open( + const char * pszFilePath) +{ + RCODE rc = NE_FLM_OK; + + close(); + + if( RC_BAD( rc = gv_pFileSystem->openFile( (char *)pszFilePath, + FLM_IO_RDONLY | FLM_IO_SH_DENYNONE, &m_pFileHdl))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Closes the input stream and frees any resources +*****************************************************************************/ +RCODE F_FileIStream::close( void) +{ + RCODE rc = NE_FLM_OK; + + if( m_pFileHdl) + { + rc = m_pFileHdl->close(); + m_pFileHdl->Release(); + m_pFileHdl = NULL; + } + + m_ui64FileOffset = 0; + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMUINT64 FLMAPI F_FileIStream::totalSize( void) +{ + FLMUINT64 ui64FileSize = 0; + + (void)m_pFileHdl->size( &ui64FileSize); + return( ui64FileSize); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMUINT64 FLMAPI F_FileIStream::remainingSize( void) +{ + FLMUINT64 ui64TotalSize = totalSize(); + FLMUINT64 ui64Offset = getCurrPosition(); + + if( ui64TotalSize >= ui64Offset) + { + return( ui64TotalSize - ui64Offset); + } + + return( 0); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMUINT64 FLMAPI F_FileIStream::getCurrPosition( void) +{ + return( m_ui64FileOffset); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FLMAPI F_FileIStream::positionTo( + FLMUINT64 ui64Offset) +{ + m_ui64FileOffset = ui64Offset; + return( NE_FLM_OK); +} + +/**************************************************************************** +Desc: Reads the requested amount of data from the stream. +*****************************************************************************/ +RCODE F_FileIStream::read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiBytesRead = 0; + + if( !m_pFileHdl) + { + rc = RC_SET( NE_FLM_READING_FILE); + goto Exit; + } + + rc = m_pFileHdl->read( m_ui64FileOffset, uiBytesToRead, + pvBuffer, &uiBytesRead); + m_ui64FileOffset += uiBytesRead; + + if( RC_BAD( rc)) + { + if( rc == NE_FLM_IO_END_OF_FILE) + { + rc = RC_SET( NE_FLM_EOF_HIT); + } + goto Exit; + } + +Exit: + + if( puiBytesRead) + { + *puiBytesRead = uiBytesRead; + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FLMAPI F_BufferedIStream::open( + IF_IStream * pIStream, + FLMUINT uiBufferSize) +{ + RCODE rc = NE_FLM_OK; + + if( m_pIStream || !pIStream) + { + rc = RC_SET_AND_ASSERT( NE_FLM_ILLEGAL_OP); + goto Exit; + } + + m_pIStream = pIStream; + m_pIStream->AddRef(); + + m_uiBufferSize = uiBufferSize; + m_uiBufferOffset = 0; + m_uiBytesAvail = 0; + + if( RC_BAD( rc = f_alloc( m_uiBufferSize, &m_pucBuffer))) + { + goto Exit; + } + +Exit: + + if (RC_BAD( rc)) + { + close(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FLMAPI F_BufferedIStream::read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; + FLMUINT uiBytesRead = 0; + FLMUINT uiMaxSize; + + if (!m_pIStream) + { + rc = RC_SET_AND_ASSERT( NE_FLM_ILLEGAL_OP); + goto Exit; + } + + while( uiBytesToRead) + { + if( (uiMaxSize = m_uiBytesAvail - m_uiBufferOffset) == 0) + { + if (RC_BAD( rc = m_pIStream->read( + m_pucBuffer, m_uiBufferSize, &m_uiBytesAvail))) + { + if (rc != NE_FLM_EOF_HIT || !m_uiBytesAvail) + { + m_uiBufferOffset = 0; + goto Exit; + } + } + + flmAssert( m_uiBytesAvail <= m_uiBufferSize); + m_uiBufferOffset = 0; + } + else if( uiBytesToRead < uiMaxSize) + { + f_memcpy( pucBuffer, &m_pucBuffer[ m_uiBufferOffset], uiBytesToRead); + m_uiBufferOffset += uiBytesToRead; + uiBytesRead += uiBytesToRead; + uiBytesToRead = 0; + break; + } + else + { + f_memcpy( pucBuffer, &m_pucBuffer[ m_uiBufferOffset], uiMaxSize); + m_uiBufferOffset += uiMaxSize; + pucBuffer += uiMaxSize; + uiBytesToRead -= uiMaxSize; + uiBytesRead += uiMaxSize; + } + } + +Exit: + + if (puiBytesRead) + { + *puiBytesRead = uiBytesRead; + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FLMAPI F_BufferedIStream::close( void) +{ + RCODE rc = NE_FLM_OK; + + if( m_pIStream) + { + if( m_pIStream->getRefCount() == 1) + { + rc = m_pIStream->close(); + } + + m_pIStream->Release(); + m_pIStream = NULL; + } + + if( m_pucBuffer) + { + f_free( &m_pucBuffer); + } + + m_uiBufferSize = 0; + m_uiBufferOffset = 0; + m_uiBytesAvail = 0; + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FLMAPI F_FileOStream::open( + const char * pszFilePath, + FLMBOOL bTruncateIfExists) +{ + RCODE rc = NE_FLM_OK; + + if( m_pFileHdl) + { + rc = RC_SET_AND_ASSERT( NE_FLM_ILLEGAL_OP); + goto Exit; + } + + if( bTruncateIfExists) + { + if( RC_BAD( rc = gv_pFileSystem->deleteFile( (char *)pszFilePath))) + { + if( rc != NE_FLM_IO_PATH_NOT_FOUND) + { + goto Exit; + } + } + + if( RC_BAD( rc = gv_pFileSystem->createFile( + (char *)pszFilePath, FLM_IO_RDWR, &m_pFileHdl))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = gv_pFileSystem->openFile( + (char *)pszFilePath, FLM_IO_RDWR, &m_pFileHdl))) + { + if( rc != NE_FLM_IO_PATH_NOT_FOUND) + { + goto Exit; + } + + if( RC_BAD( rc = gv_pFileSystem->createFile( + (char *)pszFilePath, FLM_IO_RDWR, &m_pFileHdl))) + { + goto Exit; + } + } + } + + if( RC_BAD( rc = m_pFileHdl->size( &m_ui64FileOffset))) + { + goto Exit; + } + +Exit: + + if (RC_BAD( rc)) + { + close(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FLMAPI F_FileOStream::write( + const void * pvBuffer, + FLMUINT uiBytesToWrite, + FLMUINT * puiBytesWritten) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiBytesWritten = 0; + + if (!m_pFileHdl) + { + rc = RC_SET_AND_ASSERT( NE_FLM_ILLEGAL_OP); + goto Exit; + } + + if( RC_BAD( rc = m_pFileHdl->write( (FLMUINT)m_ui64FileOffset, + uiBytesToWrite, (void *)pvBuffer, &uiBytesWritten))) + { + goto Exit; + } + +Exit: + + m_ui64FileOffset += uiBytesWritten; + + if( puiBytesWritten) + { + *puiBytesWritten = uiBytesWritten; + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FLMAPI F_FileOStream::close( void) +{ + RCODE rc = NE_FLM_OK; + + if( m_pFileHdl) + { + m_pFileHdl->Release(); + m_pFileHdl = NULL; + } + + m_ui64FileOffset = 0; + return( rc); +} + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE FLMAPI F_MultiFileIStream::open( + const char * pszDirectory, + const char * pszBaseName) +{ + RCODE rc = NE_FLM_OK; + + if( m_bOpen) + { + rc = RC_SET_AND_ASSERT( NE_FLM_ILLEGAL_OP); + goto Exit; + } + + f_strcpy( m_szDirectory, pszDirectory); + f_strcpy( m_szBaseName, pszBaseName); + + m_uiFileNum = 0xFFFFFFFF; + m_ui64FileOffset = 0; + m_bEndOfStream = FALSE; + m_bOpen = TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_MultiFileIStream::rollToNextFile( void) +{ + RCODE rc = NE_FLM_OK; + F_FileIStream * pFileIStream = NULL; + F_BufferedIStream * pBufferedIStream = NULL; + FLMUINT uiNewFileNum = 0; + char szFilePath[ F_PATH_MAX_SIZE + 1]; + char szFileName[ F_PATH_MAX_SIZE + 1]; + + if( m_pIStream) + { + m_pIStream->Release(); + m_pIStream = NULL; + m_ui64FileOffset = 0; + } + + if( m_uiFileNum == 0xFFFFFFFE) + { + rc = RC_SET( NE_FLM_STREAM_TOO_MANY_FILES); + goto Exit; + } + else if( m_uiFileNum == 0xFFFFFFFF) + { + f_strcpy( szFileName, m_szBaseName); + uiNewFileNum = 0; + } + else + { + uiNewFileNum = m_uiFileNum + 1; + f_sprintf( (char *)szFileName, "%s.%08X", m_szBaseName, uiNewFileNum); + } + + f_strcpy( szFilePath, m_szDirectory); + if( RC_BAD( rc = gv_pFileSystem->pathAppend( + (char *)szFilePath, (char *)szFileName))) + { + goto Exit; + } + + if( (pFileIStream = f_new F_FileIStream) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pFileIStream->open( (const char *)szFilePath))) + { + if (rc == NE_FLM_IO_PATH_NOT_FOUND) + { + m_bEndOfStream = TRUE; + rc = RC_SET( NE_FLM_EOF_HIT); + } + goto Exit; + } + + if( (pBufferedIStream = f_new F_BufferedIStream) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pBufferedIStream->open( pFileIStream, 16384))) + { + goto Exit; + } + + m_uiFileNum = uiNewFileNum; + m_pIStream = pBufferedIStream; + pBufferedIStream = NULL; + +Exit: + + if( pFileIStream) + { + pFileIStream->Release(); + } + + if( pBufferedIStream) + { + pBufferedIStream->Release(); + } + + return( rc); +} + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE FLMAPI F_MultiFileIStream::read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiTmpRead; + FLMUINT uiTotalRead = 0; + FLMBOOL bRollToNextFile = FALSE; + FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; + + if( !m_bOpen) + { + rc = RC_SET_AND_ASSERT( NE_FLM_ILLEGAL_OP); + goto Exit; + } + + if (m_bEndOfStream) + { + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + + if (!m_pIStream) + { + bRollToNextFile = TRUE; + } + + while (uiBytesToRead) + { + if (bRollToNextFile) + { + if (RC_BAD( rc = rollToNextFile())) + { + goto Exit; + } + } + + if (RC_BAD( rc = m_pIStream->read( + pucBuffer, uiBytesToRead, &uiTmpRead))) + { + if (rc != NE_FLM_EOF_HIT) + { + goto Exit; + } + + rc = NE_FLM_OK; + bRollToNextFile = TRUE; + if (!uiTmpRead) + { + continue; + } + } + + pucBuffer += uiTmpRead; + uiBytesToRead -= uiTmpRead; + uiTotalRead += uiTmpRead; + m_ui64FileOffset += uiTmpRead; + } + +Exit: + + if (puiBytesRead) + { + *puiBytesRead = uiTotalRead; + } + + return( rc); +} + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE FLMAPI F_MultiFileIStream::close( void) +{ + if( m_pIStream) + { + m_pIStream->Release(); + m_pIStream = NULL; + } + + m_uiFileNum = 0; + m_ui64FileOffset = 0; + m_szDirectory[ 0] = 0; + m_szBaseName[ 0] = 0; + m_bEndOfStream = FALSE; + m_bOpen = FALSE; + + return( NE_FLM_OK); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_MultiFileOStream::create( + const char * pszDirectory, + const char * pszBaseName, + FLMUINT uiMaxFileSize, + FLMBOOL bOkToOverwrite) +{ + RCODE rc = NE_FLM_OK; + + if( m_bOpen) + { + rc = RC_SET_AND_ASSERT( NE_FLM_ILLEGAL_OP); + goto Exit; + } + + if( RC_BAD( rc = processDirectory( + pszDirectory, pszBaseName, bOkToOverwrite))) + { + goto Exit; + } + + f_strcpy( m_szDirectory, pszDirectory); + f_strcpy( m_szBaseName, pszBaseName); + + if( !uiMaxFileSize) + { + uiMaxFileSize = MULTI_FILE_OUT_STREAM_MAX_FILE_SIZE; + } + else if( uiMaxFileSize < MULTI_FILE_OUT_STREAM_MIN_FILE_SIZE) + { + uiMaxFileSize = MULTI_FILE_OUT_STREAM_MIN_FILE_SIZE; + } + else if( uiMaxFileSize > MULTI_FILE_OUT_STREAM_MAX_FILE_SIZE) + { + uiMaxFileSize = MULTI_FILE_OUT_STREAM_MAX_FILE_SIZE; + } + + m_uiFileNum = 0xFFFFFFFF; + m_ui64FileOffset = 0; + m_ui64MaxFileSize = uiMaxFileSize; + m_bOpen = TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_MultiFileOStream::processDirectory( + const char * pszDirectory, + const char * pszBaseName, + FLMBOOL bOkToDelete) +{ + RCODE rc = NE_FLM_OK; + IF_DirHdl * pDirHandle = NULL; + FLMUINT uiBaseNameLen = f_strlen( pszBaseName); + const char * pszName = NULL; + char szSearchPattern[ F_PATH_MAX_SIZE + 1]; + char szFilePath[ F_PATH_MAX_SIZE + 1]; + + f_sprintf( szSearchPattern, "%s*", pszBaseName); + + if (!pszDirectory || *pszDirectory == 0) + { + pszDirectory = "."; + } + + if( RC_BAD( rc = gv_pFileSystem->openDir( + (char *)pszDirectory, szSearchPattern, &pDirHandle))) + { + goto Exit; + } + + for( ;;) + { + if( RC_BAD( rc = pDirHandle->next())) + { + if( rc != NE_FLM_IO_NO_MORE_FILES) + { + goto Exit; + } + + rc = NE_FLM_OK; + break; + } + + // Verify that the file belongs to the stream + + pszName = pDirHandle->currentItemName(); + if( f_strcmp( pszName, pszBaseName) == 0 || + (f_strncmp( pszName, pszBaseName, uiBaseNameLen) == 0 && + pszName[ uiBaseNameLen] == '.' && + f_isValidHexNum( (FLMBYTE *)&pszName[ uiBaseNameLen + 1]))) + { + if (!bOkToDelete) + { + rc = RC_SET( NE_FLM_STREAM_EXISTS); + goto Exit; + } + + // Delete the file + + f_strcpy( szFilePath, pszDirectory); + + if( RC_BAD( rc = gv_pFileSystem->pathAppend( + szFilePath, pszName))) + { + goto Exit; + } + + if( RC_BAD( gv_pFileSystem->deleteFile( szFilePath))) + { + if (rc != NE_FLM_IO_PATH_NOT_FOUND) + { + goto Exit; + } + + rc = NE_FLM_OK; + } + } + } + +Exit: + + if( pDirHandle) + { + pDirHandle->Release(); + pDirHandle = NULL; + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +#if 0 +RCODE FLMAPI F_DbSystem::removeMultiFileStream( + const char * pszDirectory, + const char * pszBaseName) +{ + RCODE rc = NE_FLM_OK; + F_MultiFileOStream * pMultiStream = NULL; + + if( (pMultiStream = f_new F_MultiFileOStream) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pMultiStream->processDirectory( + pszDirectory, pszBaseName, TRUE))) + { + goto Exit; + } + +Exit: + + if( pMultiStream) + { + pMultiStream->Release(); + } + + return( rc); +} +#endif + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_MultiFileOStream::rollToNextFile( void) +{ + RCODE rc = NE_FLM_OK; + F_FileOStream * pFileOStream = NULL; + F_BufferedOStream * pBufferedOStream = NULL; + FLMUINT uiNewFileNum = 0; + char szFilePath[ F_PATH_MAX_SIZE + 1]; + char szFileName[ F_PATH_MAX_SIZE + 1]; + + if( m_pOStream) + { + if( RC_BAD( rc = m_pOStream->close())) + { + goto Exit; + } + + m_pOStream->Release(); + m_pOStream = NULL; + m_ui64FileOffset = 0; + } + + if( m_uiFileNum == 0xFFFFFFFE) + { + rc = RC_SET( NE_FLM_STREAM_TOO_MANY_FILES); + goto Exit; + } + else if( m_uiFileNum == 0xFFFFFFFF) + { + f_strcpy( szFileName, m_szBaseName); + uiNewFileNum = 0; + } + else + { + uiNewFileNum = m_uiFileNum + 1; + f_sprintf( (char *)szFileName, "%s.%08X", m_szBaseName, uiNewFileNum); + } + + f_strcpy( szFilePath, m_szDirectory); + + if( RC_BAD( rc = gv_pFileSystem->pathAppend( + (char *)szFilePath, (char *)szFileName))) + { + goto Exit; + } + + if( (pFileOStream = f_new F_FileOStream) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pFileOStream->open( (const char *)szFilePath, TRUE))) + { + goto Exit; + } + + if( (pBufferedOStream = f_new F_BufferedOStream) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pBufferedOStream->open( pFileOStream, 16384))) + { + goto Exit; + } + + m_uiFileNum = uiNewFileNum; + m_pOStream = pBufferedOStream; + pBufferedOStream = NULL; + +Exit: + + if( pFileOStream) + { + pFileOStream->Release(); + } + + if( pBufferedOStream) + { + pBufferedOStream->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FLMAPI F_MultiFileOStream::write( + const void * pvBuffer, + FLMUINT uiBytesToWrite, + FLMUINT * puiBytesWritten) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiMaxToWrite; + FLMUINT uiBytesWritten = 0; + FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; + + if (!m_bOpen) + { + rc = RC_SET_AND_ASSERT( NE_FLM_ILLEGAL_OP); + goto Exit; + } + + if (!m_pOStream) + { + if (RC_BAD( rc = rollToNextFile())) + { + goto Exit; + } + } + + while (uiBytesToWrite) + { + if ((uiMaxToWrite = (FLMUINT)(m_ui64MaxFileSize - m_ui64FileOffset)) < + uiBytesToWrite) + { + if (RC_BAD( rc = m_pOStream->write( pucBuffer, uiMaxToWrite))) + { + goto Exit; + } + + pucBuffer += uiMaxToWrite; + uiBytesWritten += uiMaxToWrite; + + if (RC_BAD( rc = rollToNextFile())) + { + goto Exit; + } + } + else + { + uiMaxToWrite = uiBytesToWrite; + + if (RC_BAD( rc = m_pOStream->write( pucBuffer, uiBytesToWrite))) + { + goto Exit; + } + } + + uiBytesWritten += uiBytesToWrite; + uiBytesToWrite -= uiMaxToWrite; + m_ui64FileOffset += uiMaxToWrite; + } + +Exit: + + if( puiBytesWritten) + { + *puiBytesWritten = uiBytesWritten; + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FLMAPI F_MultiFileOStream::close( void) +{ + RCODE rc = NE_FLM_OK; + + if( m_pOStream) + { + rc = m_pOStream->close(); + m_pOStream->Release(); + m_pOStream = NULL; + } + + m_uiFileNum = 0; + m_ui64MaxFileSize = 0; + m_ui64FileOffset = 0; + m_szDirectory[ 0] = 0; + m_szBaseName[ 0] = 0; + m_bOpen = FALSE; + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FLMAPI F_BufferedOStream::open( + IF_OStream * pOStream, + FLMUINT uiBufferSize) +{ + RCODE rc = NE_FLM_OK; + + if( !pOStream || m_pOStream || !uiBufferSize) + { + rc = RC_SET_AND_ASSERT( NE_FLM_ILLEGAL_OP); + goto Exit; + } + + if( RC_BAD( rc = f_alloc( uiBufferSize, &m_pucBuffer))) + { + goto Exit; + } + + m_pOStream = pOStream; + m_pOStream->AddRef(); + + m_uiBufferSize = uiBufferSize; + m_uiBufferOffset = 0; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FLMAPI F_BufferedOStream::flush( void) +{ + RCODE rc = NE_FLM_OK; + + if( m_uiBufferOffset) + { + if( RC_BAD( rc = m_pOStream->write( m_pucBuffer, m_uiBufferOffset))) + { + goto Exit; + } + + m_uiBufferOffset = 0; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FLMAPI F_BufferedOStream::write( + const void * pvBuffer, + FLMUINT uiBytesToWrite, + FLMUINT * puiBytesWritten) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiMaxToWrite; + FLMUINT uiBytesWritten = 0; + FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; + + while( uiBytesToWrite) + { + uiMaxToWrite = (FLMUINT)(m_uiBufferSize - m_uiBufferOffset); + uiMaxToWrite = uiMaxToWrite > uiBytesToWrite + ? uiBytesToWrite + : uiMaxToWrite; + + f_memcpy( &m_pucBuffer[ m_uiBufferOffset], pucBuffer, uiMaxToWrite); + pucBuffer += uiMaxToWrite; + m_uiBufferOffset += uiMaxToWrite; + uiBytesToWrite -= uiMaxToWrite; + uiBytesWritten += uiMaxToWrite; + + if (m_uiBufferOffset == m_uiBufferSize) + { + if (RC_BAD( rc = flush())) + { + goto Exit; + } + } + } + +Exit: + + if( puiBytesWritten) + { + *puiBytesWritten = uiBytesWritten; + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FLMAPI F_BufferedOStream::close( void) +{ + RCODE rc = NE_FLM_OK; + + if( m_pOStream) + { + if( RC_OK( rc = flush())) + { + if( m_pOStream->getRefCount() == 1) + { + rc = m_pOStream->close(); + } + } + + m_pOStream->Release(); + m_pOStream = NULL; + } + + if( m_pucBuffer) + { + f_free( &m_pucBuffer); + } + + m_uiBufferSize = 0; + m_uiBufferOffset = 0; + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +F_BufferIStream::~F_BufferIStream() +{ + close(); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE FLMAPI F_BufferIStream::open( + const FLMBYTE * pucBuffer, + FLMUINT uiLength, + FLMBYTE ** ppucAllocatedBuffer) +{ + RCODE rc = NE_FLM_OK; + + flmAssert( !m_pucBuffer); + + if( !pucBuffer && uiLength) + { + if( RC_BAD( rc = f_alloc( uiLength, &m_pucBuffer))) + { + goto Exit; + } + + if( ppucAllocatedBuffer) + { + *ppucAllocatedBuffer = (FLMBYTE *)m_pucBuffer; + } + + m_bAllocatedBuffer = TRUE; + } + else + { + m_pucBuffer = pucBuffer; + } + + m_uiBufferLen = uiLength; + m_uiOffset = 0; + m_bIsOpen = TRUE; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE FLMAPI F_BufferIStream::close( void) +{ + if( m_bIsOpen) + { + if( m_bAllocatedBuffer) + { + if( m_pucBuffer) + { + f_free( &m_pucBuffer); + } + + m_bAllocatedBuffer = FALSE; + } + else + { + m_pucBuffer = NULL; + } + + m_bIsOpen = FALSE; + } + + return( NE_FLM_OK); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE FLMAPI F_BufferIStream::read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; + FLMUINT uiBytesRead; + + flmAssert( m_bIsOpen); + + uiBytesRead = uiBytesToRead < m_uiBufferLen - m_uiOffset + ? uiBytesToRead + : m_uiBufferLen - m_uiOffset; + + if (uiBytesRead) + { + if (pucBuffer) + { + f_memcpy( pucBuffer, &m_pucBuffer[ m_uiOffset], uiBytesRead); + } + + m_uiOffset += uiBytesRead; + } + + if (puiBytesRead) + { + *puiBytesRead = uiBytesRead; + } + + if (uiBytesRead < uiBytesToRead) + { + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE FLMAPI F_Base64DecoderIStream::open( + IF_IStream * pIStream) +{ + RCODE rc = NE_FLM_OK; + + if( m_pIStream || !pIStream) + { + rc = RC_SET_AND_ASSERT( NE_FLM_ILLEGAL_OP); + goto Exit; + } + + m_uiBufOffset = 0; + m_uiAvailBytes = 0; + m_pIStream = pIStream; + pIStream->AddRef(); + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Reads decoded binary from the base64 ASCII source stream. +*****************************************************************************/ +RCODE FLMAPI F_Base64DecoderIStream::read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucOutBuf = (FLMBYTE *)pvBuffer; + FLMBYTE ucQuadBuffer[ 4]; + FLMUINT uiOffset; + FLMUINT uiBytesToCopy; + + if( puiBytesRead) + { + *puiBytesRead = 0; + } + + if( !m_pIStream) + { + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + + while( uiBytesToRead) + { + if( !m_uiAvailBytes) + { + m_uiBufOffset = 0; + + for( uiOffset = 0; uiOffset < 4;) + { + if( RC_BAD( rc = m_pIStream->read( + &ucQuadBuffer[ uiOffset], 1, NULL))) + { + if( rc != NE_FLM_EOF_HIT) + { + goto Exit; + } + + if( uiOffset) + { + rc = RC_SET( NE_FLM_BAD_BASE64_ENCODING); + } + + goto Exit; + } + + if( m_ucDecodeTable[ ucQuadBuffer[ uiOffset]] == 0xFF) + { + FLMBYTE ucTmp = ucQuadBuffer[ uiOffset]; + + if( ucTmp == ASCII_TAB || ucTmp == ASCII_SPACE || + ucTmp == ASCII_NEWLINE || ucTmp == ASCII_CR) + { + continue; + } + + rc = RC_SET( NE_FLM_BAD_BASE64_ENCODING); + goto Exit; + } + + uiOffset++; + } + + m_ucBuffer[ 0] = + (m_ucDecodeTable[ ucQuadBuffer[ 0]] << 2) | + (m_ucDecodeTable[ ucQuadBuffer[ 1]] >> 4); + m_uiAvailBytes++; + + if( ucQuadBuffer[ 2] != '=') + { + m_ucBuffer[ 1] = + (m_ucDecodeTable[ ucQuadBuffer[ 1]] << 4) | + (m_ucDecodeTable[ ucQuadBuffer[ 2]] >> 2); + m_uiAvailBytes++; + } + + if( ucQuadBuffer[ 3] != '=') + { + m_ucBuffer[ 2] = + (m_ucDecodeTable[ ucQuadBuffer[ 2]] << 6) | + m_ucDecodeTable[ ucQuadBuffer[ 3]]; + m_uiAvailBytes++; + } + } + + uiBytesToCopy = f_min( m_uiAvailBytes, uiBytesToRead); + + if( pucOutBuf) + { + f_memcpy( pucOutBuf, &m_ucBuffer[ m_uiBufOffset], uiBytesToCopy); + } + + uiBytesToRead -= uiBytesToCopy; + m_uiAvailBytes -= uiBytesToCopy; + m_uiBufOffset += uiBytesToCopy; + pucOutBuf += uiBytesToCopy; + + if( puiBytesRead) + { + *puiBytesRead += uiBytesToCopy; + } + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE FLMAPI F_Base64EncoderIStream::open( + IF_IStream * pIStream, + FLMBOOL bLineBreaks) +{ + RCODE rc = NE_FLM_OK; + + if( m_pIStream || !pIStream) + { + rc = RC_SET_AND_ASSERT( NE_FLM_ILLEGAL_OP); + goto Exit; + } + + m_uiBase64Count = 0; + m_uiBufOffset = 0; + m_uiAvailBytes = 0; + m_bLineBreaks = bLineBreaks; + m_bInputExhausted = FALSE; + m_bPriorLineEnd = FALSE; + m_pIStream = pIStream; + pIStream->AddRef(); + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Reads ASCII base64 encoded binary from the source stream. +*****************************************************************************/ +RCODE FLMAPI F_Base64EncoderIStream::read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucOutBuf = (FLMBYTE *)pvBuffer; + FLMUINT uiBytesToCopy; + FLMUINT uiBytesToEncode; + FLMBYTE ucTriBuffer[ 3]; + + if( *puiBytesRead) + { + *puiBytesRead = 0; + } + + if( !m_pIStream) + { + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + + while( uiBytesToRead) + { + if( !m_uiAvailBytes) + { + m_uiBufOffset = 0; + + if( m_bInputExhausted) + { + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + + if( RC_BAD( rc = m_pIStream->read( ucTriBuffer, + 3, &uiBytesToEncode))) + { + if( rc != NE_FLM_EOF_HIT) + { + goto Exit; + } + + rc = NE_FLM_OK; + m_bInputExhausted = TRUE; + } + + if( uiBytesToEncode) + { + m_ucBuffer[ m_uiAvailBytes++] = + m_ucEncodeTable[ ucTriBuffer[ 0] >> 2]; + + m_ucBuffer[ m_uiAvailBytes++] = + m_ucEncodeTable[ ((ucTriBuffer[ 0] & 0x03) << 4) | + (ucTriBuffer[ 1] >> 4)]; + + if( uiBytesToEncode >= 2) + { + m_ucBuffer[ m_uiAvailBytes++] = + m_ucEncodeTable[ ((ucTriBuffer[ 1] & 0x0F) << 2) | + (ucTriBuffer[ 2] >> 6)]; + } + else + { + m_ucBuffer[ m_uiAvailBytes++] = ASCII_EQUAL; + } + + if( uiBytesToEncode == 3) + { + m_ucBuffer[ m_uiAvailBytes++] = + m_ucEncodeTable[ ucTriBuffer[ 2] & 0x3F]; + } + else + { + m_ucBuffer[ m_uiAvailBytes++] = ASCII_EQUAL; + } + + m_uiBase64Count += 4; + } + + if( m_bLineBreaks) + { + if( (m_uiBase64Count % 72) == 0 || + (m_bInputExhausted && !m_bPriorLineEnd)) + { +#ifdef FLM_UNIX + m_ucBuffer[ m_uiAvailBytes++] = ASCII_NEWLINE; +#elif FLM_OSX + m_ucBuffer[ m_uiAvailBytes++] = ASCII_CR; +#else + m_ucBuffer[ m_uiAvailBytes++] = ASCII_CR; + m_ucBuffer[ m_uiAvailBytes++] = ASCII_NEWLINE; +#endif + m_bPriorLineEnd = TRUE; + } + else + { + m_bPriorLineEnd = FALSE; + } + } + + if( !m_uiAvailBytes) + { + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + } + + uiBytesToCopy = f_min( m_uiAvailBytes, uiBytesToRead); + + if( pucOutBuf) + { + f_memcpy( pucOutBuf, &m_ucBuffer[ m_uiBufOffset], uiBytesToCopy); + } + + pucOutBuf += uiBytesToCopy; + uiBytesToRead -= uiBytesToCopy; + m_uiAvailBytes -= uiBytesToCopy; + m_uiBufOffset += uiBytesToCopy; + + if( puiBytesRead) + { + *puiBytesRead += uiBytesToCopy; + } + } + +Exit: + + return( rc); +} + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE FLMAPI F_CompressingOStream::open( + IF_OStream * pOStream) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE ucOutBuf[ 2]; + + // Setup the hash table + + m_uiHashTblSize = ((2 * 1024 * 1024) / sizeof( LZWODictItem *)); + if( RC_BAD( rc = f_alloc( + sizeof( LZWODictItem *) * m_uiHashTblSize, &m_ppHashTbl))) + { + goto Exit; + } + + f_memset( m_ppHashTbl, 0, sizeof( LZWODictItem *) * m_uiHashTblSize); + + // Output a magic number so the stream can be identified + + UW2FBA( LZW_MAGIC_NUMBER, ucOutBuf); + if( RC_BAD( rc = pOStream->write( ucOutBuf, 2))) + { + goto Exit; + } + + // Setup misc. member variables + + m_pOStream = pOStream; + m_pOStream->AddRef(); + + m_ui16CurrentCode = LZW_END_OF_DATA; + m_ui16FreeCode = LZW_START_CODE; + m_uiLastRatio = 100; + m_uiBestRatio = 100; + m_uiCurrentBytesIn = 0; + m_uiTotalBytesIn = 0; + m_uiCurrentBytesOut = 0; + m_uiTotalBytesOut = 0; + m_bStopCompression = FALSE; + +Exit: + + if( RC_BAD( rc)) + { + close(); + } + + return( rc); +} + +/****************************************************************************** +Desc: +******************************************************************************/ +LZWODictItem * F_CompressingOStream::findDictEntry( + FLMUINT16 ui16CurrentCode, + FLMBYTE ucChar) +{ + FLMUINT uiHashBucket; + LZWODictItem * pDictItem; + FLMUINT uiLooks = 0; + + uiHashBucket = getHashBucket( ui16CurrentCode, ucChar); + pDictItem = m_ppHashTbl[ uiHashBucket]; + + while( pDictItem) + { + if( pDictItem->ui16ParentCode == ui16CurrentCode && + pDictItem->ucChar == ucChar) + { + break; + } + + pDictItem = pDictItem->pNext; + uiLooks++; + } + + return( pDictItem); +} + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE FLMAPI F_CompressingOStream::write( + const void * pvBuffer, + FLMUINT uiBytesToWrite, + FLMUINT * puiBytesWritten) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiBucket; + FLMUINT uiBytesWritten = 0; + FLMUINT uiTmp; + LZWODictItem * pDictItem; + const FLMBYTE * pucBuffer = (const FLMBYTE *)pvBuffer; + FLMBYTE ucOut[ 2]; + + if( !uiBytesToWrite) + { + goto Exit; + } + + if( m_bStopCompression) + { + rc = m_pOStream->write( pucBuffer, uiBytesToWrite, &uiTmp); + uiBytesWritten += uiTmp; + goto Exit; + } + + if( !m_uiTotalBytesIn) + { + m_ui16CurrentCode = *pucBuffer++; + m_uiCurrentBytesIn++; + m_uiTotalBytesIn++; + uiBytesToWrite--; + } + + while( uiBytesToWrite) + { + if( (pDictItem = findDictEntry( + m_ui16CurrentCode, *pucBuffer)) == NULL) + { + // No match. Output the code. + + UW2FBA( m_ui16CurrentCode, ucOut); + if( RC_BAD( rc = m_pOStream->write( ucOut, 2))) + { + goto Exit; + } + m_uiCurrentBytesOut += 2; + m_uiTotalBytesOut += 2; + uiBytesWritten += 2; + + // Add the new code to the dictionary + + if( m_ui16FreeCode < LZW_MAX_CODE) + { + uiBucket = getHashBucket( m_ui16CurrentCode, *pucBuffer); + if( RC_BAD( rc = m_pool.poolAlloc( + sizeof( LZWODictItem), (void **)&pDictItem))) + { + goto Exit; + } + + pDictItem->pNext = m_ppHashTbl[ uiBucket]; + m_ppHashTbl[ uiBucket] = pDictItem; + + pDictItem->ucChar = *pucBuffer; + pDictItem->ui16Code = m_ui16FreeCode++; + pDictItem->ui16ParentCode = m_ui16CurrentCode; + } + + m_ui16CurrentCode = *pucBuffer; + + // May need to reset the dictionary to improve compression. + // If compression is causing the stream to grow, we + // need to disable it. + + if( m_uiTotalBytesIn > (10 * 1024 * 1024) && + m_uiTotalBytesOut > m_uiTotalBytesIn) + { + // Compression isn't buying us anything. From this + // point forward in the stream, just store the bytes + // without compression. + + UW2FBA( LZW_STOP_COMPRESSION, ucOut); + if (RC_BAD( rc = m_pOStream->write( ucOut, 2))) + { + goto Exit; + } + + uiBytesWritten += 2; + m_bStopCompression = TRUE; + m_ui16CurrentCode = LZW_END_OF_DATA; + + // Finish writing out the rest of the current buffer + + if( RC_BAD( rc = m_pOStream->write( pucBuffer, uiBytesToWrite))) + { + goto Exit; + } + + m_uiCurrentBytesIn = 0; + m_uiTotalBytesIn += uiBytesToWrite; + uiBytesWritten += uiBytesToWrite; + uiBytesToWrite = 0; + break; + } + else if( m_uiCurrentBytesIn >= 8192) + { + FLMUINT uiRatio; + + uiRatio = (m_uiCurrentBytesOut * 100) / m_uiCurrentBytesIn; + m_uiCurrentBytesIn = 0; + m_uiCurrentBytesOut = 0; + + if( uiRatio > m_uiBestRatio) + { + if( uiRatio > 50 && + (uiRatio > 90 || uiRatio > m_uiLastRatio + 10)) + { + // Output the dictionary reset token + + UW2FBA( LZW_NEW_DICT, ucOut); + if (RC_BAD( rc = m_pOStream->write( ucOut, 2))) + { + goto Exit; + } + + uiBytesWritten += 2; + + // Reset the dictionary + + m_pool.poolReset( NULL); + f_memset( m_ppHashTbl, 0, sizeof( LZWODictItem *) * m_uiHashTblSize); + m_ui16FreeCode = LZW_START_CODE; + } + else + { + m_uiBestRatio = uiRatio; + } + + m_uiLastRatio = uiRatio; + } + + // Good time to release the CPU + + f_yieldCPU(); + } + } + else + { + m_ui16CurrentCode = pDictItem->ui16Code; + } + + pucBuffer++; + m_uiCurrentBytesIn++; + m_uiTotalBytesIn++; + uiBytesToWrite--; + } + +Exit: + + if( puiBytesWritten) + { + *puiBytesWritten = uiBytesWritten; + } + + return( rc); +} + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE FLMAPI F_CompressingOStream::close( void) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE ucOut[ 2]; + + if (m_pOStream) + { + if (RC_OK( rc) && m_ui16CurrentCode != LZW_END_OF_DATA) + { + UW2FBA( m_ui16CurrentCode, ucOut); + rc = m_pOStream->write( ucOut, 2); + m_uiCurrentBytesOut += 2; + m_uiTotalBytesOut += 2; + } + + // Write the end-of-data marker + + if (RC_OK( rc)) + { + UW2FBA( LZW_END_OF_DATA, ucOut); + rc = m_pOStream->write( ucOut, 2); + m_uiCurrentBytesOut += 2; + m_uiTotalBytesOut += 2; + } + + if (m_pOStream->getRefCount() == 1) + { + if (RC_OK( rc)) + { + rc = m_pOStream->close(); + } + else + { + m_pOStream->close(); + } + } + + m_pOStream->Release(); + m_pOStream = NULL; + } + + if( m_ppHashTbl) + { + f_free( &m_ppHashTbl); + m_uiHashTblSize = 0; + } + + m_pool.poolReset( NULL); + return( rc); +} + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE FLMAPI F_UncompressingIStream::open( + IF_IStream * pIStream) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE ucInBuf[ 2]; + FLMUINT16 ui16Magic; + + // Allocate the dictonary table + + if( RC_BAD( rc = f_alloc( + sizeof( LZWIDictItem) * LZW_MAX_CODE, &m_pDict))) + { + goto Exit; + } + + f_memset( m_pDict, 0, sizeof( LZWIDictItem) * LZW_MAX_CODE); + + // Allocate the decode buffer + + m_uiDecodeBufferSize = 4096; + if( RC_BAD( rc = f_alloc( m_uiDecodeBufferSize, &m_pucDecodeBuffer))) + { + goto Exit; + } + + // Read the magic number from the stream to ensure that this + // is really an LZW compressed stream + + if( RC_BAD( rc = pIStream->read( ucInBuf, 2, NULL))) + { + goto Exit; + } + ui16Magic = FB2UW( ucInBuf); + + if( ui16Magic != LZW_MAGIC_NUMBER) + { + rc = RC_SET( NE_FLM_STREAM_NOT_COMPRESSED); + goto Exit; + } + + // Add a reference to the passed-in stream object + + m_pIStream = pIStream; + m_pIStream->AddRef(); + + // Setup misc. member variables + + m_ui16FreeCode = LZW_START_CODE; + m_ui16LastCode = LZW_END_OF_DATA; + m_uiDecodeBufferOffset = 0; + m_bStopCompression = FALSE; + m_bEndOfStream = FALSE; + +Exit: + + if( RC_BAD( rc)) + { + close(); + } + + return( rc); +} + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE F_UncompressingIStream::readCode( + FLMUINT16 * pui16Code) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE ucInBuf[ 2]; + + if( m_bEndOfStream) + { + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + + if( RC_BAD( rc = m_pIStream->read( ucInBuf, 2, NULL))) + { + goto Exit; + } + *pui16Code = FB2UW( ucInBuf); + + if( *pui16Code == LZW_END_OF_DATA) + { + m_bEndOfStream = TRUE; + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + +Exit: + + return( rc); +} + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE F_UncompressingIStream::decodeToBuffer( + FLMUINT16 ui16Code) +{ + RCODE rc = NE_FLM_OK; + + if( ui16Code >= m_ui16FreeCode || m_ui16LastCode == LZW_END_OF_DATA) + { + rc = RC_SET_AND_ASSERT( NE_FLM_STREAM_DECOMPRESS_ERROR); + goto Exit; + } + + while( ui16Code > 0x00FF) + { + flmAssert( m_uiDecodeBufferOffset < m_uiDecodeBufferSize); + flmAssert( ui16Code < m_ui16FreeCode); + + m_pucDecodeBuffer[ m_uiDecodeBufferOffset++] = m_pDict[ ui16Code].ucChar; + ui16Code = m_pDict[ ui16Code].ui16ParentCode; + } + m_pucDecodeBuffer[ m_uiDecodeBufferOffset++] = (FLMBYTE)ui16Code; + +Exit: + + return( rc); +} + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE FLMAPI F_UncompressingIStream::read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; + FLMUINT uiTmp; + FLMUINT uiBytesRead = 0; + FLMUINT uiSavePos; + FLMUINT16 ui16Code; + + while( uiBytesToRead) + { + if( m_uiDecodeBufferOffset) + { + // Consume a byte from the decode buffer + + *pucBuffer++ = m_pucDecodeBuffer[ --m_uiDecodeBufferOffset]; + uiBytesToRead--; + continue; + } + + if( m_bStopCompression) + { + if (RC_BAD( rc = m_pIStream->read( pvBuffer, uiBytesToRead, &uiTmp))) + { + if (rc != NE_FLM_EOF_HIT) + { + goto Exit; + } + + uiBytesRead += uiTmp; + } + + break; + } + + if( RC_BAD( rc = readCode( &ui16Code))) + { + goto Exit; + } + + if( ui16Code == LZW_NEW_DICT) + { + m_ui16FreeCode = LZW_START_CODE; + m_ui16LastCode = LZW_END_OF_DATA; + continue; + } + else if( ui16Code == LZW_STOP_COMPRESSION) + { + flmAssert( !m_bStopCompression); + m_bStopCompression = TRUE; + continue; + } + else + { + if( ui16Code >= m_ui16FreeCode) + { + // The code isn't in our dictionary. There is only + // one type of sequence that can result in this + // condition. The code below builds the correct + // sequence of bytes. + + flmAssert( m_ui16LastCode != LZW_END_OF_DATA); + + uiSavePos = m_uiDecodeBufferOffset++; + if( RC_BAD( rc = decodeToBuffer( m_ui16LastCode))) + { + goto Exit; + } + + m_pucDecodeBuffer[ uiSavePos] = + m_pucDecodeBuffer[ m_uiDecodeBufferOffset - 1]; + } + else if( m_ui16LastCode == LZW_END_OF_DATA) + { + flmAssert( ui16Code <= 0x00FF); + *pucBuffer++ = (FLMBYTE)ui16Code; + uiBytesToRead--; + m_ui16LastCode = ui16Code; + continue; + } + else + { + if( RC_BAD( rc = decodeToBuffer( ui16Code))) + { + goto Exit; + } + } + + if( m_ui16FreeCode < LZW_MAX_CODE) + { + flmAssert( m_uiDecodeBufferOffset); + + m_pDict[ m_ui16FreeCode].ui16ParentCode = m_ui16LastCode; + m_pDict[ m_ui16FreeCode].ucChar = + m_pucDecodeBuffer[ m_uiDecodeBufferOffset - 1]; + m_ui16FreeCode++; + } + + m_ui16LastCode = ui16Code; + } + } + +Exit: + + if( puiBytesRead) + { + *puiBytesRead = uiBytesRead; + } + + return( rc); +} + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE FLMAPI F_UncompressingIStream::close( void) +{ + if( m_pIStream) + { + m_pIStream->Release(); + m_pIStream = NULL; + } + + if( m_pDict) + { + f_free( &m_pDict); + } + + if( m_pucDecodeBuffer) + { + f_free( &m_pucDecodeBuffer); + } + + return( NE_FLM_OK); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +#if 0 +RCODE FLMAPI F_DbSystem::openBufferIStream( + const char * pucBuffer, + FLMUINT uiLength, + IF_PosIStream ** ppIStream) +{ + RCODE rc = NE_FLM_OK; + F_BufferIStream * pNewIStream = NULL; + + if( (pNewIStream = f_new F_BufferIStream) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewIStream->open( (FLMBYTE *)pucBuffer, uiLength))) + { + goto Exit; + } + + if( *ppIStream) + { + (*ppIStream)->Release(); + } + + *ppIStream = pNewIStream; + pNewIStream = NULL; + +Exit: + + if( pNewIStream) + { + pNewIStream->Release(); + } + + return( rc); +} +#endif + +/****************************************************************************** +Desc: Return an input stream that reads from multiple files. +******************************************************************************/ +#if 0 +RCODE FLMAPI F_DbSystem::openMultiFileIStream( + const char * pszDirectory, + const char * pszBaseName, + IF_IStream ** ppIStream) +{ + RCODE rc = NE_FLM_OK; + F_MultiFileIStream * pNewIStream = NULL; + + if( (pNewIStream = f_new F_MultiFileIStream) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewIStream->open( pszDirectory, pszBaseName))) + { + goto Exit; + } + + *ppIStream = pNewIStream; + pNewIStream = NULL; + +Exit: + + if( pNewIStream) + { + pNewIStream->Release(); + } + + return( rc); +} +#endif + +/****************************************************************************** +Desc: Return a buffered input stream from pIStream. +******************************************************************************/ +#if 0 +RCODE FLMAPI F_DbSystem::openBufferedIStream( + IF_IStream * pIStream, + FLMUINT uiBufferSize, + IF_IStream ** ppIStream) +{ + RCODE rc = NE_FLM_OK; + F_BufferedIStream * pNewIStream = NULL; + + if( (pNewIStream = f_new F_BufferedIStream) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewIStream->open( pIStream, uiBufferSize))) + { + goto Exit; + } + + *ppIStream = pNewIStream; + pNewIStream = NULL; + +Exit: + + if( pNewIStream) + { + pNewIStream->Release(); + } + + return( rc); +} +#endif + +/****************************************************************************** +Desc: +******************************************************************************/ +#if 0 +RCODE FLMAPI F_DbSystem::openUncompressingIStream( + IF_IStream * pIStream, + IF_IStream ** ppIStream) +{ + RCODE rc = NE_FLM_OK; + F_UncompressingIStream * pNewIStream = NULL; + + if( (pNewIStream = f_new F_UncompressingIStream) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewIStream->open( pIStream))) + { + goto Exit; + } + + *ppIStream = pNewIStream; + pNewIStream = NULL; + +Exit: + + if( pNewIStream) + { + pNewIStream->Release(); + } + + return( rc); +} +#endif + +/****************************************************************************** +Desc: Return a compressing output stream. +******************************************************************************/ +#if 0 +RCODE FLMAPI F_DbSystem::openCompressingOStream( + IF_OStream * pOStream, + IF_OStream ** ppOStream) +{ + RCODE rc = NE_FLM_OK; + F_CompressingOStream * pNewOStream = NULL; + + if( (pNewOStream = f_new F_CompressingOStream) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewOStream->open( pOStream))) + { + goto Exit; + } + + *ppOStream = pNewOStream; + pNewOStream = NULL; + +Exit: + + if( pNewOStream) + { + pNewOStream->Release(); + } + + return( rc); +} +#endif + +/****************************************************************************** +Desc: Return a file output stream. +******************************************************************************/ +#if 0 +RCODE FLMAPI F_DbSystem::openFileOStream( + const char * pszFileName, + FLMBOOL bTruncateIfExists, + IF_OStream ** ppOStream) +{ + RCODE rc = NE_FLM_OK; + F_FileOStream * pNewOStream = NULL; + + if( (pNewOStream = f_new F_FileOStream) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewOStream->open( pszFileName, bTruncateIfExists))) + { + goto Exit; + } + + *ppOStream = pNewOStream; + pNewOStream = NULL; + +Exit: + + if( pNewOStream) + { + pNewOStream->Release(); + } + + return( rc); +} +#endif + +/****************************************************************************** +Desc: Return a multi-file output stream. +******************************************************************************/ +#if 0 +RCODE FLMAPI F_DbSystem::openMultiFileOStream( + const char * pszDirectory, + const char * pszBaseName, + FLMUINT uiMaxFileSize, + FLMBOOL bOverwrite, + IF_OStream ** ppOStream) +{ + RCODE rc = NE_FLM_OK; + F_MultiFileOStream * pNewOStream = NULL; + + if( (pNewOStream = f_new F_MultiFileOStream) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewOStream->create( pszDirectory, pszBaseName, + uiMaxFileSize, bOverwrite))) + { + goto Exit; + } + + *ppOStream = pNewOStream; + pNewOStream = NULL; + +Exit: + + if( pNewOStream) + { + pNewOStream->Release(); + } + + return( rc); +} +#endif + +/****************************************************************************** +Desc: Return a buffered output stream. +******************************************************************************/ +#if 0 +RCODE FLMAPI F_DbSystem::openBufferedOStream( + IF_OStream * pOStream, + FLMUINT uiBufferSize, + IF_OStream ** ppOStream) +{ + RCODE rc = NE_FLM_OK; + F_BufferedOStream * pNewOStream = NULL; + + if( (pNewOStream = f_new F_BufferedOStream) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewOStream->open( pOStream, uiBufferSize))) + { + goto Exit; + } + + *ppOStream = pNewOStream; + pNewOStream = NULL; + +Exit: + + if( pNewOStream) + { + pNewOStream->Release(); + } + + return( rc); +} +#endif + +/****************************************************************************** +Desc: Read all data from input stream and write to the output stream. +******************************************************************************/ +#if 0 +RCODE FLMAPI F_DbSystem::writeToOStream( + IF_IStream * pIStream, + IF_OStream * pOStream) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE ucBuffer[ 512]; + FLMUINT uiBufferSize = sizeof( ucBuffer); + FLMUINT uiBytesToWrite; + FLMUINT uiBytesRead; + + for (;;) + { + if( RC_BAD( rc = pIStream->read( + ucBuffer, uiBufferSize, &uiBytesRead))) + { + if( rc != NE_FLM_EOF_HIT) + { + goto Exit; + } + + rc = NE_FLM_OK; + + if (!uiBytesRead) + { + goto Exit; + } + } + + uiBytesToWrite = uiBytesRead; + if( RC_BAD( rc = pOStream->write( ucBuffer, uiBytesToWrite))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} +#endif + +/**************************************************************************** +Desc: +*****************************************************************************/ +#if 0 +RCODE FLMAPI F_DbSystem::openFileIStream( + const char * pszPath, + IF_PosIStream ** ppIStream) +{ + RCODE rc = NE_FLM_OK; + F_FileIStream * pNewIStream = NULL; + + if( (pNewIStream = f_new F_FileIStream) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewIStream->open( pszPath))) + { + goto Exit; + } + + if( *ppIStream) + { + (*ppIStream)->Release(); + } + + *ppIStream = pNewIStream; + pNewIStream = NULL; + +Exit: + + if( pNewIStream) + { + pNewIStream->Release(); + } + + return( rc); +} +#endif + +/***************************************************************************** +Desc: +******************************************************************************/ +#if 0 +RCODE FLMAPI F_DbSystem::openBase64Encoder( + IF_IStream * pInputStream, + FLMBOOL bInsertLineBreaks, + IF_IStream ** ppEncodedStream) +{ + RCODE rc = NE_FLM_OK; + F_Base64EncoderIStream * pNewIStream = NULL; + + if( (pNewIStream = f_new F_Base64EncoderIStream) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewIStream->open( pInputStream, bInsertLineBreaks))) + { + goto Exit; + } + + if( *ppEncodedStream) + { + (*ppEncodedStream)->Release(); + } + + *ppEncodedStream = pNewIStream; + pNewIStream = NULL; + +Exit: + + if( pNewIStream) + { + pNewIStream->Release(); + } + + return( rc); +} +#endif + +/***************************************************************************** +Desc: +******************************************************************************/ +#if 0 +RCODE FLMAPI F_DbSystem::openBase64Decoder( + IF_IStream * pInputStream, + IF_IStream ** ppDecodedStream) +{ + RCODE rc = NE_FLM_OK; + F_Base64DecoderIStream * pNewIStream = NULL; + + if( (pNewIStream = f_new F_Base64DecoderIStream) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewIStream->open( pInputStream))) + { + goto Exit; + } + + if( *ppDecodedStream) + { + (*ppDecodedStream)->Release(); + } + + *ppDecodedStream = pNewIStream; + pNewIStream = NULL; + +Exit: + + if( pNewIStream) + { + pNewIStream->Release(); + } + + return( rc); +} +#endif + +/******************************************************************** +Desc: +*********************************************************************/ +F_TCPStream::F_TCPStream( void) +{ + m_pszIp[ 0] = 0; + m_pszName[ 0] = 0; + m_pszPeerIp[ 0] = 0; + m_pszPeerName[ 0] = 0; + m_uiIOTimeout = 10; + m_iSocket = INVALID_SOCKET; + m_ulRemoteAddr = 0; + m_bInitialized = FALSE; + m_bConnected = FALSE; + +#ifndef FLM_UNIX + if( !WSAStartup( MAKEWORD( 2, 0), &m_wsaData)) + { + m_bInitialized = TRUE; + } +#endif +} + +/******************************************************************** +Desc: +*********************************************************************/ +F_TCPStream::~F_TCPStream( void) +{ + if( m_bConnected) + { + close(); + } + +#ifndef FLM_UNIX + if( m_bInitialized) + { + WSACleanup(); + } +#endif +} + +/******************************************************************** +Desc: Opens a new connection +*********************************************************************/ +RCODE F_TCPStream::openConnection( + const char * pucHostName, + FLMUINT uiPort, + FLMUINT uiConnectTimeout, + FLMUINT uiDataTimeout) +{ + RCODE rc = NE_FLM_OK; + FLMINT iSockErr; + FLMINT iTries; + FLMINT iMaxTries = 5; + struct sockaddr_in address; + struct hostent * pHostEntry; + unsigned long ulIPAddr; + int iTmp; + + flmAssert( !m_bConnected); + m_iSocket = INVALID_SOCKET; + + if( pucHostName && pucHostName[ 0] != '\0') + { + ulIPAddr = inet_addr( (char *)pucHostName); + if( ulIPAddr == (unsigned long)(-1)) + { + pHostEntry = gethostbyname( (char *)pucHostName); + + if( !pHostEntry) + { + rc = RC_SET( NE_FLM_NOIP_ADDR); + goto Exit; + } + else + { + ulIPAddr = *((unsigned long *)pHostEntry->h_addr); + } + + } + } + else + { + ulIPAddr = inet_addr( (char *)"127.0.0.1"); + } + + // Fill in the Socket structure with family type + + f_memset( (char *)&address, 0, sizeof( struct sockaddr_in)); + address.sin_family = AF_INET; + address.sin_addr.s_addr = (unsigned)ulIPAddr; + address.sin_port = htons( (unsigned short)uiPort); + + // Allocate a socket, then attempt to connect to it! + + if( (m_iSocket = socket( AF_INET, + SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) + { + rc = RC_SET( NE_FLM_SOCKET_FAIL); + goto Exit; + } + + // Now attempt to connect with the specified partner host, + // time-out if connection doesn't complete within alloted time + +#ifdef FLM_WIN + + if( uiConnectTimeout) + { + if ( uiConnectTimeout < 5 ) + { + iMaxTries = (iMaxTries * uiConnectTimeout) / 5; + uiConnectTimeout = 5; + } + } + else + { + iMaxTries = 1; + } +#endif + + for( iTries = 0; iTries < iMaxTries; iTries++ ) + { + iSockErr = 0; + if( connect( m_iSocket, (struct sockaddr *)&address, + (unsigned)sizeof(struct sockaddr)) >= 0) + { + break; + } + + #ifndef FLM_UNIX + iSockErr = WSAGetLastError(); + #else + iSockErr = errno; + #endif + + #ifdef FLM_WIN + + // In WIN, we sometimes get WSAEINVAL when, if we keep + // trying, we will eventually connect. Therefore, + // here we'll treat WSAEINVAL as EINPROGRESS. + + if( iSockErr == WSAEINVAL) + { + #ifndef FLM_UNIX + closesocket( m_iSocket); + #else + ::close( m_iSocket); + #endif + if( (m_iSocket = socket( AF_INET, + SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) + { + rc = RC_SET( NE_FLM_SOCKET_FAIL); + goto Exit; + } + #if defined( FLM_WIN) || defined( FLM_NLM) + iSockErr = WSAEINPROGRESS; + #else + iSockErr = EINPROGRESS; + #endif + continue; + } + #endif + + #if defined( FLM_WIN) || defined( FLM_NLM) + if( iSockErr == WSAEISCONN ) + #else + if( iSockErr == EISCONN ) + #endif + { + break; + } + #if defined( FLM_WIN) || defined( FLM_NLM) + else if( iSockErr == WSAEWOULDBLOCK) + #else + else if( iSockErr == EWOULDBLOCK) + #endif + { + // Let's wait a split second to give the connection + // request a chance. + + f_sleep( 100 ); + continue; + } + #if defined( FLM_WIN) || defined( FLM_NLM) + else if( iSockErr == WSAEINPROGRESS) + #else + else if( iSockErr == EINPROGRESS) + #endif + { + if( RC_OK( rc = socketPeek( uiConnectTimeout, FALSE))) + { + // Let's wait a split second to give the connection + // request a chance. + + f_sleep( 100 ); + continue; + } + } + + rc = RC_SET( NE_FLM_CONNECT_FAIL); + goto Exit; + } + + // Disable Nagel's algorithm + + iTmp = 1; + if( (setsockopt( m_iSocket, IPPROTO_TCP, TCP_NODELAY, (char *)&iTmp, + (unsigned)sizeof( iTmp) )) < 0) + { + rc = RC_SET( NE_FLM_SOCKET_SET_OPT_FAIL); + goto Exit; + } + + m_uiIOTimeout = uiDataTimeout; + m_bConnected = TRUE; + +Exit: + + if( RC_BAD( rc)) + { + if( m_iSocket != INVALID_SOCKET) + { + #ifndef FLM_UNIX + closesocket( m_iSocket); + #else + ::close( m_iSocket); + #endif + m_iSocket = INVALID_SOCKET; + } + } + + return( rc); +} + +/******************************************************************** +Desc: Gets information about the local host machine. +*********************************************************************/ +RCODE F_TCPStream::getLocalInfo( void) +{ + RCODE rc = NE_FLM_OK; + struct hostent * pHostEnt; + FLMUINT32 ui32IPAddr; + + m_pszIp[ 0] = 0; + m_pszName[ 0] = 0; + + if( !m_pszName[ 0]) + { + if( gethostname( m_pszName, (unsigned)sizeof( m_pszName))) + { + rc = RC_SET( NE_FLM_SOCKET_FAIL); + goto Exit; + } + } + + if( !m_pszIp[ 0] && (pHostEnt = gethostbyname( m_pszName)) != NULL) + { + ui32IPAddr = (FLMUINT32)(*((unsigned long *)pHostEnt->h_addr)); + if( ui32IPAddr != (FLMUINT32)-1) + { + struct in_addr InAddr; + + InAddr.s_addr = ui32IPAddr; + f_strcpy( m_pszIp, inet_ntoa( InAddr)); + } + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Gets information about the remote machine. +*********************************************************************/ +RCODE F_TCPStream::getRemoteInfo( void) +{ + RCODE rc = NE_FLM_OK; + struct sockaddr_in SockAddrIn; + char * InetAddr = NULL; + struct hostent * HostsName; + + m_pszPeerIp[ 0] = 0; + m_pszPeerName[ 0] = 0; + + SockAddrIn.sin_addr.s_addr = (unsigned)m_ulRemoteAddr; + + InetAddr = inet_ntoa( SockAddrIn.sin_addr); + f_strcpy( m_pszPeerIp, InetAddr); + + // Try to get the peer's host name by looking up his IP + // address. + + HostsName = gethostbyaddr( (char *)&SockAddrIn.sin_addr.s_addr, + (unsigned)sizeof( unsigned long), AF_INET ); + + if( HostsName != NULL) + { + f_strcpy( m_pszPeerName, (char*) HostsName->h_name ); + } + else + { + if (!InetAddr) + { + InetAddr = inet_ntoa( SockAddrIn.sin_addr); + } + + f_strcpy( m_pszPeerName, InetAddr); + } + + return( rc); +} + +/******************************************************************** +Desc: Tests for socket data readiness +*********************************************************************/ +RCODE F_TCPStream::socketPeek( + FLMINT iTimeoutVal, + FLMBOOL bPeekRead) +{ + RCODE rc = NE_FLM_OK; + struct timeval TimeOut; + int iMaxDescs; + fd_set GenDescriptors; + fd_set * DescrRead; + fd_set * DescrWrt; + + if( m_iSocket != INVALID_SOCKET) + { + FD_ZERO( &GenDescriptors); +#ifdef FLM_WIN + #pragma warning( push) + #pragma warning( disable : 4127) +#endif + FD_SET( m_iSocket, &GenDescriptors); +#ifdef FLM_WIN + #pragma warning( pop) +#endif + + iMaxDescs = (int)(m_iSocket + 1); + DescrRead = bPeekRead ? &GenDescriptors : NULL; + DescrWrt = bPeekRead ? NULL : &GenDescriptors; + + TimeOut.tv_sec = (long)iTimeoutVal; + TimeOut.tv_usec = (long)0; + + if( select( iMaxDescs, DescrRead, DescrWrt, NULL, &TimeOut) < 0 ) + { + rc = RC_SET( NE_FLM_SELECT_ERR); + goto Exit; + } + else + { + if( !FD_ISSET( m_iSocket, &GenDescriptors)) + { + rc = bPeekRead + ? RC_SET( NE_FLM_SOCKET_READ_TIMEOUT) + : RC_SET( NE_FLM_SOCKET_WRITE_TIMEOUT); + } + } + } + else + { + rc = RC_SET( NE_FLM_CONNECT_FAIL); + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE FLMAPI F_TCPStream::write( + const void * pucBuffer, + FLMUINT uiBytesToWrite, + FLMUINT * puiBytesWritten) +{ + RCODE rc = NE_FLM_OK; + FLMINT iRetryCount = 0; + FLMINT iBytesWritten = 0; + + if( m_iSocket == INVALID_SOCKET) + { + rc = RC_SET( NE_FLM_CONNECT_FAIL); + goto Exit; + } + + flmAssert( pucBuffer && uiBytesToWrite); + +Retry: + + *puiBytesWritten = 0; + if( RC_OK( rc = socketPeek( m_uiIOTimeout, FALSE))) + { + iBytesWritten = send( m_iSocket, + (char *)pucBuffer, (int)uiBytesToWrite, 0); + + switch ( iBytesWritten) + { + case -1: + { + *puiBytesWritten = 0; + rc = RC_SET( NE_FLM_SOCKET_WRITE_FAIL); + break; + } + + case 0: + { + rc = RC_SET( NE_FLM_SOCKET_DISCONNECT); + break; + } + + default: + { + *puiBytesWritten = (FLMUINT)iBytesWritten; + break; + } + } + } + + if( RC_BAD( rc) && rc != NE_FLM_SOCKET_WRITE_TIMEOUT) + { +#ifndef FLM_UNIX + FLMINT iSockErr = WSAGetLastError(); +#else + FLMINT iSockErr = errno; +#endif + +#if defined( FLM_WIN) || defined( FLM_NLM) + if( iSockErr == WSAECONNABORTED) +#else + if( iSockErr == ECONNABORTED) +#endif + { + rc = RC_SET( NE_FLM_SOCKET_DISCONNECT); + } +#if defined( FLM_WIN) || defined( FLM_NLM) + else if( iSockErr == WSAEWOULDBLOCK && iRetryCount < 5) +#else + else if( iSockErr == EWOULDBLOCK && iRetryCount < 5) +#endif + { + iRetryCount++; + f_sleep( (FLMUINT)(100 * iRetryCount)); + goto Retry; + } + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE FLMAPI F_TCPStream::read( + void * pucBuffer, + FLMUINT uiBytesToWrite, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_FLM_OK; + FLMINT iReadCnt = 0; + + flmAssert( m_bConnected && pucBuffer && uiBytesToWrite); + + if( RC_OK( rc = socketPeek( m_uiIOTimeout, TRUE))) + { + iReadCnt = (FLMINT)recv( m_iSocket, + (char *)pucBuffer, (int)uiBytesToWrite, 0); + + switch ( iReadCnt) + { + case -1: + { + iReadCnt = 0; +#if defined( FLM_WIN) || defined( FLM_NLM) + if ( WSAGetLastError() == WSAECONNRESET) +#else + if( errno == ECONNRESET) +#endif + { + rc = RC_SET( NE_FLM_SOCKET_DISCONNECT); + } + else + { + rc = RC_SET( NE_FLM_SOCKET_READ_FAIL); + } + break; + } + + case 0: + { + rc = RC_SET( NE_FLM_SOCKET_DISCONNECT); + break; + } + + default: + { + break; + } + } + } + + if( puiBytesRead) + { + *puiBytesRead = (FLMUINT)iReadCnt; + } + + return( rc); +} + +/******************************************************************** +Desc: Reads data from the connection - Timeout valkue is zero, no error + is generated if timeout occurs. +*********************************************************************/ +RCODE F_TCPStream::readNoWait( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_FLM_OK; + FLMINT iReadCnt = 0; + + flmAssert( m_bConnected && pvBuffer && uiBytesToRead); + + if( puiBytesRead) + { + *puiBytesRead = 0; + } + + if( RC_OK( rc = socketPeek( (FLMUINT)0, TRUE))) + { + iReadCnt = recv( m_iSocket, (char *)pvBuffer, (int)uiBytesToRead, 0); + switch ( iReadCnt) + { + case -1: + { + *puiBytesRead = 0; +#if defined( FLM_WIN) || defined( FLM_NLM) + if ( WSAGetLastError() == WSAECONNRESET) +#else + if( errno == ECONNRESET) +#endif + { + rc = RC_SET( NE_FLM_SOCKET_DISCONNECT); + } + else + { + rc = RC_SET( NE_FLM_SOCKET_READ_FAIL); + } + goto Exit; + } + + case 0: + { + rc = RC_SET( NE_FLM_SOCKET_DISCONNECT); + goto Exit; + } + + default: + { + break; + } + } + } + else if (rc == NE_FLM_SOCKET_READ_TIMEOUT) + { + rc = NE_FLM_OK; + } + + if( puiBytesRead) + { + *puiBytesRead = (FLMUINT)iReadCnt; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Reads data and does not return until all requested data has + been read or a timeout error has been encountered. +*********************************************************************/ +RCODE F_TCPStream::readAll( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiToRead = 0; + FLMUINT uiHaveRead = 0; + FLMUINT uiPartialCnt; + FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; + + flmAssert( m_bConnected && pvBuffer && uiBytesToRead); + + uiToRead = uiBytesToRead; + while( uiToRead) + { + if( RC_BAD( rc = read( pucBuffer, uiToRead, &uiPartialCnt))) + { + goto Exit; + } + + pucBuffer += uiPartialCnt; + uiHaveRead += uiPartialCnt; + uiToRead = (FLMUINT)(uiBytesToRead - uiHaveRead); + + if( puiBytesRead) + { + *puiBytesRead = uiHaveRead; + } + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Closes any open connections +*********************************************************************/ +RCODE FLMAPI F_TCPStream::close( void) +{ + if( m_iSocket == INVALID_SOCKET) + { + goto Exit; + } + +#ifndef FLM_UNIX + closesocket( m_iSocket); +#else + ::close( m_iSocket); +#endif + +Exit: + + m_iSocket = INVALID_SOCKET; + m_bConnected = FALSE; + + return( NE_FLM_OK); +} diff --git a/ftk/src/ftksys.h b/ftk/src/ftksys.h new file mode 100644 index 0000000..1b32714 --- /dev/null +++ b/ftk/src/ftksys.h @@ -0,0 +1,6877 @@ +//------------------------------------------------------------------------------ +// Desc: Cross-platform macros, defines, etc. Must visit this file +// to port XFLAIM to another platform. +// +// Tabs: 3 +// +// Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: ftk.h 3123 2006-01-24 17:19:50 -0700 (Tue, 24 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------------ + +#ifndef FTKSYS_H +#define FTKSYS_H + + #include "ftk.h" + + #ifndef FLM_PLATFORM_CONFIGURED + #error Platform not configured + #endif + + class F_Thread; + class F_ThreadMgr; + class F_IOBuffer; + class F_FileSystem; + class F_ThreadMgr; + class F_ResultSet; + class F_ResultSetBlk; + + #define RS_BLOCK_SIZE (1024 * 512) + #define RS_POSITION_NOT_SET FLM_MAX_UINT64 + #define RS_MAX_FIXED_ENTRY_SIZE 64 + + // Cell sizes for buffer allocator + + #define CELL_SIZE_0 16 + #define CELL_SIZE_1 32 + #define CELL_SIZE_2 64 + #define CELL_SIZE_3 128 + #define CELL_SIZE_4 192 + #define CELL_SIZE_5 320 + #define CELL_SIZE_6 512 + #define CELL_SIZE_7 672 + #define CELL_SIZE_8 832 + #define CELL_SIZE_9 1088 + #define CELL_SIZE_10 1344 + #define CELL_SIZE_11 1760 + #define CELL_SIZE_12 2176 + #define CELL_SIZE_13 2848 + #define CELL_SIZE_14 3520 + #define CELL_SIZE_15 4608 + #define CELL_SIZE_16 5152 + #define CELL_SIZE_17 5696 + #define CELL_SIZE_18 8164 + #define CELL_SIZE_19 13068 + #define CELL_SIZE_20 16340 + #define CELL_SIZE_21 21796 + #define MAX_CELL_SIZE CELL_SIZE_21 + + #define NUM_BUF_ALLOCATORS 22 + + /**************************************************************************** + Desc: Global data + ****************************************************************************/ + + #ifndef ALLOCATE_SYS_DATA + extern F_FileSystem * gv_pFileSystem; + extern F_ThreadMgr * gv_pThreadMgr; + #else + F_FileSystem * gv_pFileSystem; + F_ThreadMgr * gv_pThreadMgr; + #endif + + FINLINE RCODE MapPlatformError( + FLMINT iError, + RCODE defaultRc); + + #define F_MULTI_FHDL_LIST_SIZE 8 + #define F_MULTI_FHDL_DEFAULT_MAX_FILE_SIZE ((FLMUINT)0xFFFFFFFF) + + typedef struct + { + IF_FileHdl * pFileHdl; + FLMUINT uiFileNum; + FLMBOOL bDirty; + } FH_INFO; + + typedef struct xmlChar + { + FLMBYTE ucFlags; + } XMLCHAR; + + #define FLM_MAX_KEY_SIZE 1024 + + typedef struct FlmBlockHdrTag + { + FLMUINT32 ui32BlkAddr; // BH_ADDR + FLMUINT32 ui32PrevBlkInChain; // BH_PREV_BLK + FLMUINT32 ui32NextBlkInChain; // BH_NEXT_BLK + FLMUINT32 ui32PriorBlkImgAddr; // BH_PREV_BLK_ADDR + FLMUINT64 ui64TransID; // BH_TRANS_ID + FLMUINT32 ui32BlkCRC; // Block CRC + FLMUINT16 ui16BlkBytesAvail; // BH_BLK_END, BH_ELM_END + FLMUINT8 ui8BlkFlags; // Flags for the block + #define BLK_FORMAT_IS_LITTLE_ENDIAN 0x01 + #define BLK_IS_BEFORE_IMAGE 0x02 + // This bit gets ORed into type if the + // block is a Before Image block that + // should be restored on transaction + // abort. This is only set when a block + // is written to the log, so it only + // needs to be unset when the block is + // read back from the log. + #define BLK_IS_ENCRYPTED 0x04 + + + FLMUINT8 ui8BlkType; // BH_TYPE + #define BT_FREE 0 // Free block - avail list + #define BT_LFH_BLK 1 // LFH Header block + #define BT_LEAF 2 // New B-Tree Leaf block + #define BT_NON_LEAF 3 // New B-Tree Non-leaf block block - fixed key size + #define BT_NON_LEAF_COUNTS 4 // New B-Tree Non-leaf index with counts + #define BT_LEAF_DATA 5 // New B-Tree Leaf block with Data + #define BT_DATA_ONLY 6 // Data-only block + // NOTE: IF adding more types, may need to modify the blkIsNewBTree function + // below. + + // IMPORTANT NOTE: If anything is changed in here, need to make + // corresponding changes to convertBlkHdr routine and + // flmVerifyDiskStructOffsets routine. + + #define F_BLK_HDR_ui32BlkAddr_OFFSET 0 + #define F_BLK_HDR_ui32PrevBlkInChain_OFFSET 4 + #define F_BLK_HDR_ui32NextBlkInChain_OFFSET 8 + #define F_BLK_HDR_ui32PriorBlkImgAddr_OFFSET 12 + #define F_BLK_HDR_ui64TransID_OFFSET 16 + #define F_BLK_HDR_ui32BlkCRC_OFFSET 24 + #define F_BLK_HDR_ui16BlkBytesAvail_OFFSET 28 + #define F_BLK_HDR_ui8BlkFlags_OFFSET 30 + #define F_BLK_HDR_ui8BlkType_OFFSET 31 + } F_BLK_HDR; + + typedef struct FlmBTreeBlkHdr + { + F_BLK_HDR stdBlkHdr; // Standard block header + FLMUINT16 ui16BtreeId; // BH_LOG_FILE_NUM + FLMUINT16 ui16NumKeys; // Number of keys + FLMUINT8 ui8BlkLevel; // BH_LEVEL + #define BH_MAX_LEVELS 8 // Max allowable b-tree levels + #define MAX_LEVELS BH_MAX_LEVELS + FLMUINT8 ui8BTreeFlags; // Flags for BTree + #define BLK_IS_ROOT 0x01 + #define BLK_IS_INDEX 0x02 + FLMUINT16 ui16HeapSize; // Contiguous available space + #define F_BTREE_BLK_HDR_stdBlkHdr_OFFSET 0 + #define F_BTREE_BLK_HDR_ui16BtreeId_OFFSET 32 + #define F_BTREE_BLK_HDR_ui16NumKeys_OFFSET 34 + #define F_BTREE_BLK_HDR_ui8BlkLevel_OFFSET 36 + #define F_BTREE_BLK_HDR_ui8BTreeFlags_OFFSET 37 + #define F_BTREE_BLK_HDR_ui16HeapSize_OFFSET 38 + } F_BTREE_BLK_HDR; + + enum BTREE_ERR_TYPE + { + NO_ERR = 0, // FYI: Visual Studio already defines NOERROR + BT_HEADER, + KEY_ORDER, + DUPLICATE_KEYS, + INFINITY_MARKER, + CHILD_BLOCK_ADDRESS, + SCA_GET_BLOCK_FAILED, + MISSING_OVERALL_DATA_LENGTH, + NOT_DATA_ONLY_BLOCK, + BAD_DO_BLOCK_LENGTHS, + BAD_COUNTS, + CATASTROPHIC_FAILURE = 999 + }; + + typedef struct + { + FLMUINT uiKeyCnt; + FLMUINT uiFirstKeyCnt; + FLMUINT uiBlkCnt; + FLMUINT uiBytesUsed; + FLMUINT uiDOBlkCnt; + FLMUINT uiDOBytesUsed; + } BTREE_LEVEL_STATS; + + typedef struct + { + FLMUINT uiBlkAddr; + FLMUINT uiBlockSize; + FLMUINT uiBlocksChecked; + FLMUINT uiAvgFreeSpace; + FLMUINT uiLevels; + FLMUINT uiNumKeys; + FLMUINT64 ui64FreeSpace; + BTREE_LEVEL_STATS LevelStats[ BH_MAX_LEVELS]; + char szMsg[ 64]; + BTREE_ERR_TYPE type; + } BTREE_ERR_STRUCT; + + typedef struct + { + FLMUINT uiParentLevel; + FLMUINT uiParentKeyLen; + FLMUINT uiParentChildBlkAddr; + FLMUINT uiNewKeyLen; + FLMUINT uiChildBlkAddr; + FLMUINT uiCounts; + void * pPrev; + FLMBYTE pucParentKey[ FLM_MAX_KEY_SIZE]; + FLMBYTE pucNewKey[ FLM_MAX_KEY_SIZE]; + } BTREE_REPLACE_STRUCT; + + typedef struct + { + F_BTREE_BLK_HDR * pBlkHdr; + IF_Block * pBlock; + const FLMBYTE * pucKeyBuf; + FLMUINT uiKeyBufSize; + FLMUINT uiKeyLen; + FLMUINT uiCurOffset; + FLMUINT uiLevel; + FLMUINT16 * pui16OffsetArray; + FLMUINT32 ui32BlkAddr; + } F_BTSK; + + typedef enum + { + ELM_INSERT_DO, + ELM_INSERT, + ELM_REPLACE_DO, + ELM_REPLACE, + ELM_REMOVE, + ELM_BLK_MERGE, + ELM_DONE + } F_ELM_UPD_ACTION; + + // Represent the maximum size for data & key before needing two bytes to + // store the length. + + #define ONE_BYTE_SIZE 0xFF + + // Flag definitions - BT_LEAF_DATA + + #define BTE_LEAF_DATA_OVHD 7 // Offset (2) Flags (1) OA Data (4) + + #define BTE_FLAG 0 // Offset to the FLAGS field + #define BTE_FLAG_LAST_ELEMENT 0x04 + #define BTE_FLAG_FIRST_ELEMENT 0x08 + #define BTE_FLAG_DATA_BLOCK 0x10 // Data is stored in a Data-only Block + #define BTE_FLAG_OA_DATA_LEN 0x20 // Overall data length + #define BTE_FLAG_DATA_LEN 0x40 + #define BTE_FLAG_KEY_LEN 0x80 + + // BT_LEAF (no data) + + #define BTE_LEAF_OVHD 4 // Offset (2) KeyLen (2) + #define BTE_KEY_LEN 0 + #define BTE_KEY_START 2 + + // BT_NON_LEAF_DATA + + #define BTE_NON_LEAF_OVHD 8 // Offset (2) Child Blk Addr (4) KeyLen (2) + #define BTE_NL_CHILD_BLOCK_ADDR 0 + #define BTE_NL_KEY_LEN 4 + #define BTE_NL_KEY_START 6 + + // BT_NON_LEAF_COUNTS + + #define BTE_NON_LEAF_COUNTS_OVHD 12 // Offset (2) Child Blk Addr (4) Counts (4) KeyLen (2) + #define BTE_NLC_CHILD_BLOCK_ADDR 0 + #define BTE_NLC_COUNTS 4 + #define BTE_NLC_KEY_LEN 8 + #define BTE_NLC_KEY_START 10 + + // Low water mark for coalescing blocks (as a percentage) + + #define BT_LOW_WATER_MARK 65 + + FINLINE FLMBOOL bteKeyLenFlag( + FLMBYTE * pucEntry) + { + return( (pucEntry[ BTE_FLAG] & BTE_FLAG_KEY_LEN) ? TRUE : FALSE); + } + + FINLINE FLMBOOL bteDataLenFlag( + FLMBYTE * pucEntry) + { + return( (pucEntry[ BTE_FLAG] & BTE_FLAG_DATA_LEN) ? TRUE : FALSE); + } + + FINLINE FLMBOOL bteOADataLenFlag( + FLMBYTE * pucEntry) + { + return( (pucEntry[ BTE_FLAG] & BTE_FLAG_OA_DATA_LEN) ? TRUE : FALSE); + } + + FINLINE FLMBOOL bteDataBlockFlag( + FLMBYTE * pucEntry) + { + return( (pucEntry[ BTE_FLAG] & BTE_FLAG_DATA_BLOCK) ? TRUE : FALSE); + } + + FINLINE FLMBOOL bteFirstElementFlag( + FLMBYTE * pucEntry) + { + return( (pucEntry[ BTE_FLAG] & BTE_FLAG_FIRST_ELEMENT) ? TRUE : FALSE); + } + + FINLINE FLMBOOL bteLastElementFlag( + FLMBYTE * pucEntry) + { + return( (pucEntry[ BTE_FLAG] & BTE_FLAG_LAST_ELEMENT) ? TRUE : FALSE); + } + + FINLINE FLMUINT32 bteGetBlkAddr( + const FLMBYTE * pucEntry) + { + return( FB2UD( pucEntry)); + } + + FINLINE void bteSetEntryOffset( + FLMUINT16 * pui16OffsetArray, + FLMUINT uiOffsetIndex, + FLMUINT ui16Offset) + { + UW2FBA( ui16Offset, (FLMBYTE *)&pui16OffsetArray[ uiOffsetIndex]); + } + + FINLINE FLMUINT16 bteGetEntryOffset( + const FLMUINT16 * pui16OffsetArray, + FLMUINT uiOffsetIndex) + { + return( FB2UW( (FLMBYTE *)&pui16OffsetArray[ uiOffsetIndex])); + } + + /**************************************************************************** + Desc: Internal return code macros + ****************************************************************************/ + #ifdef FLM_DEBUG + RCODE flmMakeErr( + RCODE rc, + const char * pszFile, + int iLine, + FLMBOOL bAssert); + + #define RC_SET( rc) \ + flmMakeErr( rc, __FILE__, __LINE__, FALSE) + + #define RC_SET_AND_ASSERT( rc) \ + flmMakeErr( rc, __FILE__, __LINE__, TRUE) + + #define RC_UNEXPECTED_ASSERT( rc) \ + flmMakeErr( rc, __FILE__, __LINE__, TRUE) + #else + #define RC_SET( rc) (rc) + #define RC_SET_AND_ASSERT( rc) (rc) + #define RC_UNEXPECTED_ASSERT( rc) + #endif + + #define F_SEM_WAITFOREVER (0xFFFFFFFF) + + /**************************************************************************** + Desc: NLM + ****************************************************************************/ + #if defined( FLM_NLM) + + #if defined( FLM_WATCOM_NLM) + #pragma warning 007 9 + + // Disable "Warning! W549: col(XX) 'sizeof' operand contains + // compiler generated information" + + #pragma warning 549 9 + + // Disable "Warning! W656: col(1) define this function inside its class + // definition (may improve code quality)" + + #pragma warning 656 9 + #endif + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + // The typedef for va_list in stdarg.h do not function properly when + // a va_list is passed down multiple layers as a pointer (va_list *). + // Therefore, the following definitions/typedefs were taken from a + // "fixed" version of stdarg.h implemented by DS. + + // typedef unsigned long f_va_list; + + #define f_argsize(x) \ + ((sizeof(x)+sizeof(int)-1) & ~(sizeof(int)-1)) + + #define f_va_start(ap, parmN) \ + ((void)((ap) = (unsigned long)&(parmN) + f_argsize(parmN))) + + #define f_va_arg(ap, type) \ + (*(type *)(((ap) += f_argsize(type)) - (f_argsize(type)))) + + #define f_va_end(ap) ((void)0) + #define FSTATIC + + #ifndef _SIZE_T + #define _SIZE_T + typedef unsigned int size_t; + #endif + + #ifndef _WCHAR_T + #define _WCHAR_T + typedef unsigned short wchar_t; + #endif + + #ifndef WCHAR + #define WCHAR wchar_t + #endif + + #ifndef LONG + #define LONG unsigned long + #endif + + #ifndef BYTE + #define BYTE unsigned char + #endif + + #ifndef UINT + #define UINT unsigned int + #endif + + typedef void * SEMAPHORE; + typedef unsigned long ERROR; + + extern "C" SEMAPHORE kSemaphoreAlloc( + BYTE * pSemaName, + UINT SemaCount); + + extern "C" ERROR kSemaphoreFree( + SEMAPHORE SemaHandle); + + extern "C" ERROR kSemaphoreWait( + SEMAPHORE SemaHandle); + + extern "C" ERROR kSemaphoreTimedWait( + SEMAPHORE SemaHandle, + UINT MilliSecondTimeOut); + + extern "C" ERROR kSemaphoreSignal( + SEMAPHORE SemaHandle); + + extern "C" UINT kSemaphoreExamineCount( + SEMAPHORE SemaHandle); + + extern "C" MUTEX kMutexAlloc( + BYTE * MutexName); + + extern "C" ERROR kMutexFree( + MUTEX MutexHandle); + + extern "C" ERROR kMutexLock( + MUTEX MutexHandle); + + extern "C" ERROR kMutexUnlock( + MUTEX MutexHandle); + + // External Netware Symbols + + extern "C" FLMUINT f_getNLMHandle( void); + + extern "C" RCODE f_netwareStartup( void); + + extern "C" void f_netwareShutdown( void); + +// #define f_stricmp(str1,str2) \ +// strcasecmp((char *)(str1),(char *)(str2)) +// +// #define f_strnicmp(str1,str2,size_t) \ +// strncasecmp((char *)(str1),(char *)(str2),size_t) +// +// #define f_memmove( dest, src, len) \ +// memmove( (void*)(dest), (void*)(src), len) +// +// #define f_memset( src, chr, size) \ +// memset((void *)(src),(chr),(size_t)(size)) +// +// #define f_memcmp( str1, str2, length) \ +// memcmp((void *)(str1), (void *)(str2),(size_t)(length)) +// +// #define f_strcat( dest, src) \ +// strcat( (char*)(dest), (char*)(src)) +// +// #define f_strchr( str, value) \ +// strchr( (char*)str, (int)value) +// +// #define f_strcmp( str1, str2) \ +// strcmp( (char*)(str1), (char*)(str2)) +// +// #define f_strcpy( dest, src) \ +// strcpy( (char*)(dest), (char*)(src)) +// +// #define f_strncpy( dest, src, length) \ +// strncpy( (char*)(dest), (char*)(src), (size_t)(length)) +// +// #define f_strlen( str) \ +// strlen( (char*)(str)) +// +// #define f_strncmp( str1, str2, size) \ +// strncmp( (char*)(str1), (char*)(str2), (size_t)(size)) +// +// #define f_strrchr( str, value ) \ +// strrchr( (char*)(str), (int)value) +// +// #define f_strstr( str1, str2) \ +// (char *)strstr( (char*)(str1), (char*)(str2)) +// +// #define f_strncat( str1, str2, n) \ +// strncat( (char *)(str1), (char *)(str2), n) +// +// #define f_strupr( str) \ +// strupr( (char *)(str)) + + #endif + + /**************************************************************************** + Desc: WIN + ****************************************************************************/ + #if defined( FLM_WIN) + + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + + #ifndef WIN32_EXTRA_LEAN + #define WIN32_EXTRA_LEAN + #endif + + // Enable critical section and spin count API to be visible in header + // file. + + #define _WIN32_WINNT 0x0403 + + #pragma pack( push, enter_windows, 8) + #include + #include + #include + #include + #include + #include + #include + #include + #include + #pragma pack( pop, enter_windows) + + // Conversion from XXX to YYY, possible loss of data + #pragma warning( disable : 4244) + + // Local variable XXX may be used without having been initialized + #pragma warning( disable : 4701) + + // Function XXX not inlined + #pragma warning( disable : 4710) + + #define FSTATIC static + + #define ENDLINE ENDLINE_CRLF +// #define f_va_list va_list + #define f_va_start va_start + #define f_va_arg va_arg + #define f_va_end va_end + +// #define f_stricmp( str1, str2) \ +// _stricmp((char *)(str1), (char *)(str2)) +// +// #define f_strnicmp( str1, str2, size) \ +// _strnicmp((char *)(str1), (char *)(str2),(size_t)(size)) +// +// #define f_memmove( dest, src, length) \ +// memmove((void *)(dest), (void *)(src),(size_t)(length)) +// +// #define f_memset( src, chr, size) \ +// memset((void *)(src),(chr),(size_t)(size)) +// +// #define f_memcmp( str1, str2, length) \ +// memcmp((void *)(str1), (void *)(str2),(size_t)(length)) +// +// #define f_strcat( dest, src) \ +// strcat( (char*)(dest), (char*)(src)) +// +// #define f_strchr( str, value) \ +// strchr( (char*)str, (int)value) +// +// #define f_strcmp( str1, str2) \ +// strcmp( (char*)(str1), (char*)(str2)) +// +// #define f_strcpy( dest, src) \ +// strcpy( (char*)(dest), (char*)(src)) +// +// #define f_strncpy( dest, src, length) \ +// strncpy( (char*)(dest), (char*)(src), (size_t)(length)) +// +// #define f_strlen( str) \ +// strlen( (char*)(str)) +// +// #define f_strncmp( str1, str2, size) \ +// strncmp( (char*)(str1), (char*)(str2), (size_t)(size)) +// +// #define f_strrchr( str, value ) \ +// strrchr( (char*)(str), (int)value) +// +// #define f_strstr( str1, str2) \ +// (char *)strstr( (char*)(str1), (char*)(str2)) +// +// #define f_strncat( str1, str2, n) \ +// strncat( (char *)(str1), (char *)(str2), n) +// +// #define f_strupr( str) \ +// _strupr( (char *)(str)) + + #endif + + /**************************************************************************** + Desc: UNIX + ****************************************************************************/ + #if defined( FLM_UNIX) + + #define FSTATIC static + + #ifdef HAVE_CONFIG_H + #include "config.h" + #endif + + #ifdef FLM_AIX + #ifndef _LARGE_FILES + #define _LARGE_FILES + #endif + #include + #include + #include + #endif + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #ifdef FLM_AIX + #include + #endif + + #ifdef FLM_OSX + #include + #endif + +// #define f_stricmp(str1,str2) \ +// strcasecmp((char *)(str1),(char *)(str2)) +// +// #define f_strnicmp(str1,str2,size_t) \ +// strncasecmp((char *)(str1),(char *)(str2),size_t) +// +// #define f_memmove( dest, src, len) \ +// memmove( (void*)(dest), (void*)(src), len) +// +// #define f_memset( src, chr, size) \ +// memset((void *)(src),(chr),(size_t)(size)) +// +// #define f_memcmp( str1, str2, length) \ +// memcmp((void *)(str1), (void *)(str2),(size_t)(length)) +// +// #define f_strcat( dest, src) \ +// strcat( (char*)(dest), (char*)(src)) +// +// #define f_strchr( str, value) \ +// strchr( (char*)str, (int)value) +// +// #define f_strcmp( str1, str2) \ +// strcmp( (char*)(str1), (char*)(str2)) +// +// #define f_strcpy( dest, src) \ +// strcpy( (char*)(dest), (char*)(src)) +// +// #define f_strncpy( dest, src, length) \ +// strncpy( (char*)(dest), (char*)(src), (size_t)(length)) +// +// #define f_strlen( str) \ +// strlen( (char*)(str)) +// +// #define f_strncmp( str1, str2, size) \ +// strncmp( (char*)(str1), (char*)(str2), (size_t)(size)) +// +// #define f_strrchr( str, value ) \ +// strrchr( (char*)(str), (int)value) +// +// #define f_strstr( str1, str2) \ +// (char *)strstr( (char*)(str1), (char*)(str2)) +// +// #define f_strncat( str1, str2, n) \ +// strncat( (char *)(str1), (char *)(str2), n) +// +// #define f_strupr( str) \ +// strupr( (char *)(str)) + +// #define f_va_list va_list + #define f_va_start va_start + #define f_va_arg va_arg + #define f_va_end va_end + +// typedef pthread_mutex_t * F_MUTEX; +// typedef F_MUTEX * F_MUTEX_p; +// #define F_MUTEX_NULL NULL + + typedef struct + { + pthread_mutex_t lock; + pthread_cond_t cond; + int count; + } sema_t; + + typedef sema_t * F_SEM; + typedef F_SEM * F_SEM_p; + #define F_SEM_NULL NULL + + typedef int SOCKET; + #define INVALID_SOCKET -1 + + #endif + + /**************************************************************************** + Desc: Cross-platform inline functions + ****************************************************************************/ +// FINLINE void f_memcpy( +// void * pvDest, +// const void * pvSrc, +// FLMSIZET iSize) +// { +// if( iSize == 1) +// { +// *((FLMBYTE *)pvDest) = *((FLMBYTE *)pvSrc); +// } +// else +// { +// (void)memcpy( pvDest, pvSrc, iSize); +// } +// } + + #if defined( __va_copy) + #define f_va_copy(to, from) __va_copy(to, from) + #else + #define f_va_copy(to, from) ((to) = (from)) + #endif + + typedef struct IniLine + { + char * pszParamName; + char * pszParamValue; + char * pszComment; + struct IniLine * pPrev; + struct IniLine * pNext; + } INI_LINE; + + /**************************************************************************** + Desc: Internal base class + ****************************************************************************/ + class F_OSBase + { + public: + + F_OSBase() + { + m_refCnt = 1; + } + + virtual ~F_OSBase() + { + } + + FINLINE FLMUINT getRefCount( void) + { + return( m_refCnt); + } + + void * operator new( + FLMSIZET uiSize); + + #ifdef FLM_DEBUG + void * operator new( + FLMSIZET uiSize, + const char * pszFile, + int iLine); + #endif + + void operator delete( + void * ptr); + + void operator delete[]( + void * ptr); + + #if defined( FLM_DEBUG) && !defined( FLM_WATCOM_NLM) && !defined( FLM_SOLARIS) + void operator delete( + void * ptr, + const char *, // file + int); // line + #endif + + #if defined( FLM_DEBUG) && !defined( FLM_WATCOM_NLM) && !defined( FLM_SOLARIS) + void operator delete[]( + void * ptr, + const char *, // file + int); // line + #endif + + virtual FINLINE FLMINT FLMAPI AddRef( void) + { + return( ++m_refCnt); + } + + virtual FINLINE FLMINT FLMAPI Release( void) + { + FLMINT iRefCnt = --m_refCnt; + + if( !iRefCnt) + { + delete this; + } + + return( iRefCnt); + } + + protected: + + FLMATOMIC m_refCnt; + }; + + /**************************************************************************** + Desc: Base class + ****************************************************************************/ + class F_Base + { + public: + + F_Base() + { + } + + virtual ~F_Base() + { + } + + void * operator new( + FLMSIZET uiSize, + const char * pszFile, + int iLine); + + void * operator new[]( + FLMSIZET uiSize, + const char * pszFile, + int iLine); + + void operator delete( + void * ptr, + const char * file, + int line); + + void operator delete[]( + void * ptr, + const char * file, + int line); + }; + + /**************************************************************************** + Desc: This class is used to do pool memory allocations. + ****************************************************************************/ + class F_Pool : public IF_Pool, public F_Base + { + public: + + typedef struct PoolMemoryBlock + { + PoolMemoryBlock * pPrevBlock; + FLMUINT uiBlockSize; + FLMUINT uiFreeOffset; + FLMUINT uiFreeSize; + } MBLK; + + typedef struct + { + FLMUINT uiAllocBytes; + FLMUINT uiCount; + } POOL_STATS; + + F_Pool() + { + m_uiBytesAllocated = 0; + m_pLastBlock = NULL; + m_pPoolStats = NULL; + m_uiBlockSize = 0; + } + + virtual ~F_Pool(); + + FINLINE void FLMAPI poolInit( + FLMUINT uiBlockSize) + { + m_uiBlockSize = uiBlockSize; + } + + void smartPoolInit( + POOL_STATS * pPoolStats); + + RCODE FLMAPI poolAlloc( + FLMUINT uiSize, + void ** ppvPtr); + + RCODE FLMAPI poolCalloc( + FLMUINT uiSize, + void ** ppvPtr); + + void FLMAPI poolFree( void); + + void FLMAPI poolReset( + void * pvMark, + FLMBOOL bReduceFirstBlock = FALSE); + + FINLINE void * FLMAPI poolMark( void) + { + return (void *)(m_pLastBlock + ? (FLMBYTE *)m_pLastBlock + m_pLastBlock->uiFreeOffset + : NULL); + } + + FINLINE FLMUINT FLMAPI getBlockSize( void) + { + return( m_uiBlockSize); + } + + FINLINE FLMUINT FLMAPI getBytesAllocated( void) + { + return( m_uiBytesAllocated); + } + + private: + + FINLINE void updateSmartPoolStats( void) + { + if (m_uiBytesAllocated) + { + if( (m_pPoolStats->uiAllocBytes + m_uiBytesAllocated) >= 0xFFFF0000) + { + m_pPoolStats->uiAllocBytes = + (m_pPoolStats->uiAllocBytes / m_pPoolStats->uiCount) * 100; + m_pPoolStats->uiCount = 100; + } + else + { + m_pPoolStats->uiAllocBytes += m_uiBytesAllocated; + m_pPoolStats->uiCount++; + } + m_uiBytesAllocated = 0; + } + } + + FINLINE void setInitialSmartPoolBlkSize( void) + { + // Determine starting block size: + // 1) average of bytes allocated / # of frees/resets (average size needed) + // 2) add 10% - to minimize extra allocs + + m_uiBlockSize = (m_pPoolStats->uiAllocBytes / m_pPoolStats->uiCount); + m_uiBlockSize += (m_uiBlockSize / 10); + + if (m_uiBlockSize < 512) + { + m_uiBlockSize = 512; + } + } + + void freeToMark( + void * pvMark); + + PoolMemoryBlock * m_pLastBlock; + FLMUINT m_uiBlockSize; + FLMUINT m_uiBytesAllocated; + POOL_STATS * m_pPoolStats; + }; + + /**************************************************************************** + FLAIM's Assert Layer + + This section contains prototypes and macros for FLAIM's assert. This layer + enables FLAIM to redirect assert calls. + ****************************************************************************/ + + #ifdef FLM_DEBUG + + #ifdef FLM_DBG_LOG + void flmDbgLogFlush( void); + #endif + + #if defined( FLM_WIN) + #ifdef FLM_DBG_LOG + #define flmAssert( exp) \ + (void)( (exp) || (flmDbgLogFlush(), DebugBreak(), 0)) + #else + #define flmAssert( exp) \ + (void)( (exp) || (DebugBreak(), 0)) + #endif + + #elif defined( FLM_NLM) + extern "C" void EnterDebugger(void); + + #ifdef FLM_DBG_LOG + #define flmAssert( exp) \ + (void)( (exp) || (flmDbgLogFlush(), EnterDebugger(), 0)) + #else + #define flmAssert( exp) \ + (void)( (exp) || ( EnterDebugger(), 0)) + #endif + + #elif defined( FLM_UNIX) + #ifdef FLM_DBG_LOG + #define flmAssert( exp) \ + (void)( (exp) || (flmDbgLogFlush(), assert(0), 0)) + #else + #define flmAssert( exp) \ + (void)( (exp) || (assert(0), 0)) + #endif + + #else + #define flmAssert( exp) + #endif + + #else + #define flmAssert( exp) + #endif + + FLMUINT f_breakpoint( + FLMUINT uiBreakFlag); + + /**************************************************************************** + Random Numbers + ****************************************************************************/ + + #define MAX_RANDOM 2147483646L + + class F_RandomGenerator : public IF_RandomGenerator, public F_Base + { + public: + + void FLMAPI randomize( void); + + void FLMAPI setSeed( + FLMINT32 i32seed); + + FLMINT32 FLMAPI getInt32( void); + + FLMINT32 FLMAPI getInt32( + FLMINT32 i32Low, + FLMINT32 i32High); + + FLMBOOL FLMAPI getBoolean( void); + + FLMINT32 FLMAPI getSeed( void) + { + return( m_i32Seed); + } + + private: + + FLMINT32 m_i32Seed; + }; + +// /********************************************************************** +// Desc: Atomic Increment, Decrement, Exchange +// Note: Some of this code is derived from the Ximian source code contained +// in that Mono project's atomic.h file. +// **********************************************************************/ +// #ifndef FLM_HAVE_ATOMICS +// #define FLM_HAVE_ATOMICS +// #endif +// +// /********************************************************************** +// Desc: +// **********************************************************************/ +// #if defined( FLM_SOLARIS) && defined( FLM_SPARC) && !defined( FLM_GNUC) +// extern "C" FLMINT32 sparc_atomic_add_32( +// volatile FLMINT32 * piTarget, +// FLMINT32 iDelta); +// #endif +// +// /********************************************************************** +// Desc: +// **********************************************************************/ +// #if defined( FLM_SOLARIS) && defined( FLM_SPARC) && !defined( FLM_GNUC) +// extern "C" FLMINT32 sparc_atomic_xchg_32( +// volatile FLMINT32 * piTarget, +// FLMINT32 iNewValue); +// #endif +// +// /********************************************************************** +// Desc: +// **********************************************************************/ +// #if defined( FLM_AIX) +// FINLINE int aix_atomic_add( +// volatile int * piTarget, +// int iDelta) +// { +// return( fetch_and_add( (int *)piTarget, iDelta) + iDelta); +// } +// #endif +// +// /********************************************************************** +// Desc: +// **********************************************************************/ +// FINLINE FLMINT32 _flmAtomicInc( +// FLMATOMIC * piTarget) +// { +// #if defined( FLM_NLM) +// { +// return( (FLMINT32)nlm_AtomicIncrement( (volatile LONG *)piTarget)); +// } +// #elif defined( FLM_WIN) +// { +// return( (FLMINT32)InterlockedIncrement( (volatile LONG *)piTarget)); +// } +// #elif defined( FLM_AIX) +// { +// return( (FLMINT32)aix_atomic_add( piTarget, 1)); +// } +// #elif defined( FLM_OSX) +// { +// return( (FLMINT32)OSAtomicIncrement32( (int32_t *)piTarget)); +// } +// #elif defined( FLM_SOLARIS) && defined( FLM_SPARC) && !defined( FLM_GNUC) +// { +// return( sparc_atomic_add_32( piTarget, 1)); +// } +// #elif (defined( __i386__) || defined( __x86_64__)) && defined( FLM_GNUC) +// { +// FLMINT32 i32Tmp; +// +// __asm__ __volatile__ ( +// "lock;" +// "xaddl %0, %1" +// : "=r" (i32Tmp), "=m" (*piTarget) +// : "0" (1), "m" (*piTarget)); +// +// return( i32Tmp + 1); +// } +// #else +// #ifdef FLM_HAVE_ATOMICS +// #undef FLM_HAVE_ATOMICS +// #endif +// +// F_UNREFERENCED_PARM( piTarget); +// +// flmAssert( 0); +// return( 0); +// #endif +// } +// +// /********************************************************************** +// Desc: +// **********************************************************************/ +// FINLINE FLMINT32 _flmAtomicDec( +// FLMATOMIC * piTarget) +// { +// #if defined( FLM_NLM) +// { +// return( (FLMINT32)nlm_AtomicDecrement( (volatile LONG *)piTarget)); +// } +// #elif defined( FLM_WIN) +// { +// return( (FLMINT32)InterlockedDecrement( (volatile LONG *)piTarget)); +// } +// #elif defined( FLM_AIX) +// { +// return( (FLMINT32)aix_atomic_add( piTarget, -1)); +// } +// #elif defined( FLM_OSX) +// { +// return( (FLMINT32)OSAtomicDecrement32( (int32_t *)piTarget)); +// } +// #elif defined( FLM_SOLARIS) && defined( FLM_SPARC) && !defined( FLM_GNUC) +// { +// return( sparc_atomic_add_32( piTarget, -1)); +// } +// #elif (defined( __i386__) || defined( __x86_64__)) && defined( FLM_GNUC) +// { +// FLMINT32 i32Tmp; +// +// __asm__ __volatile__ ( +// "lock;" +// "xaddl %0, %1" +// : "=r" (i32Tmp), "=m" (*piTarget) +// : "0" (-1), "m" (*piTarget)); +// +// return( i32Tmp - 1); +// } +// #else +// #ifdef FLM_HAVE_ATOMICS +// #undef FLM_HAVE_ATOMICS +// #endif +// +// F_UNREFERENCED_PARM( piTarget); +// +// flmAssert( 0); +// return( 0); +// #endif +// } +// +// /********************************************************************** +// Desc: +// **********************************************************************/ +// FINLINE FLMINT32 _flmAtomicExchange( +// FLMATOMIC * piTarget, +// FLMINT32 i32NewVal) +// { +// #if defined( FLM_NLM) +// { +// return( (FLMINT32)nlm_AtomicExchange( +// (volatile LONG *)piTarget, i32NewVal)); +// } +// #elif defined( FLM_WIN) +// { +// return( (FLMINT32)InterlockedExchange( (volatile LONG *)piTarget, +// i32NewVal)); +// } +// #elif defined( FLM_AIX) +// { +// int iOldVal; +// +// for( ;;) +// { +// iOldVal = (int)*piTarget; +// +// if( compare_and_swap( (int *)piTarget, &iOldVal, i32NewVal)) +// { +// break; +// } +// } +// +// return( (FLMINT32)iOldVal); +// } +// #elif defined( FLM_OSX) +// { +// int32_t iOldVal; +// +// for( ;;) +// { +// iOldVal = (int32_t)*piTarget; +// +// if( OSAtomicCompareAndSwap32( iOldVal, i32NewVal, +// (int32_t *)piTarget)) +// { +// break; +// } +// } +// +// return( (FLMINT32)iOldVal); +// } +// #elif defined( FLM_SOLARIS) && defined( FLM_SPARC) && !defined( FLM_GNUC) +// { +// return( sparc_atomic_xchg_32( piTarget, i32NewVal)); +// } +// #elif (defined( __i386__) || defined( __x86_64__)) && defined( FLM_GNUC) +// { +// FLMINT32 i32OldVal; +// +// __asm__ __volatile__ ( +// "1: lock;" +// " cmpxchgl %2, %0;" +// " jne 1b" +// : "=m" (*piTarget), "=a" (i32OldVal) +// : "r" (i32NewVal), "m" (*piTarget), "a" (*piTarget)); +// +// return( i32OldVal); +// } +// #else +// #ifdef FLM_HAVE_ATOMICS +// #undef FLM_HAVE_ATOMICS +// #endif +// +// F_UNREFERENCED_PARM( piTarget); +// F_UNREFERENCED_PARM( i32NewVal); +// +// flmAssert( 0); +// return( 0); +// #endif +// } +// +// /********************************************************************** +// Desc: +// **********************************************************************/ +// FINLINE FLMINT32 flmAtomicInc( +// FLMATOMIC * piTarget, +// F_MUTEX hMutex = F_MUTEX_NULL, +// FLMBOOL bMutexAlreadyLocked = FALSE) +// { +// #ifdef FLM_HAVE_ATOMICS +// { +// F_UNREFERENCED_PARM( bMutexAlreadyLocked); +// F_UNREFERENCED_PARM( hMutex); +// +// return( _flmAtomicInc( piTarget)); +// } +// #else +// { +// FLMINT32 i32NewVal; +// +// flmAssert( hMutex != F_MUTEX_NULL); +// +// if( !bMutexAlreadyLocked) +// { +// f_mutexLock( hMutex); +// } +// +// i32NewVal = (FLMINT32)(++(*piTarget)); +// +// if( !bMutexAlreadyLocked) +// { +// f_mutexUnlock( hMutex); +// } +// +// return( i32NewVal); +// } +// #endif +// } +// +// /********************************************************************** +// Desc: +// **********************************************************************/ +// FINLINE FLMINT32 flmAtomicDec( +// FLMATOMIC * piTarget, +// F_MUTEX hMutex = F_MUTEX_NULL, +// FLMBOOL bMutexAlreadyLocked = FALSE) +// { +// #ifdef FLM_HAVE_ATOMICS +// { +// F_UNREFERENCED_PARM( bMutexAlreadyLocked); +// F_UNREFERENCED_PARM( hMutex); +// +// return( _flmAtomicDec( piTarget)); +// } +// #else +// { +// FLMINT32 i32NewVal; +// +// flmAssert( hMutex != F_MUTEX_NULL); +// +// if( !bMutexAlreadyLocked) +// { +// f_mutexLock( hMutex); +// } +// +// i32NewVal = (FLMINT32)(--(*piTarget)); +// +// if( !bMutexAlreadyLocked) +// { +// f_mutexUnlock( hMutex); +// } +// +// return( i32NewVal); +// } +// #endif +// } +// +// /********************************************************************** +// Desc: +// **********************************************************************/ +// FINLINE FLMINT32 flmAtomicExchange( +// FLMATOMIC * piTarget, +// FLMINT32 i32NewVal, +// F_MUTEX hMutex = F_MUTEX_NULL, +// FLMBOOL bMutexAlreadyLocked = FALSE) +// { +// #ifdef FLM_HAVE_ATOMICS +// { +// F_UNREFERENCED_PARM( bMutexAlreadyLocked); +// F_UNREFERENCED_PARM( hMutex); +// +// return( _flmAtomicExchange( piTarget, i32NewVal)); +// } +// #else +// { +// FLMINT32 i32OldVal; +// +// flmAssert( hMutex != F_MUTEX_NULL); +// +// if( !bMutexAlreadyLocked) +// { +// f_mutexLock( hMutex); +// } +// +// i32OldVal = (FLMINT32)*piTarget; +// *piTarget = i32NewVal; +// +// if( !bMutexAlreadyLocked) +// { +// f_mutexUnlock( hMutex); +// } +// +// return( i32OldVal); +// } +// #endif +// } + + /**************************************************************************** + Desc: Mutex and semaphore routines + ****************************************************************************/ +// #ifdef FLM_NLM +// FINLINE RCODE f_mutexCreate( +// F_MUTEX * phMutex) +// { +// if( (*phMutex = (F_MUTEX)kMutexAlloc( (BYTE *)"NOVDB")) == F_MUTEX_NULL) +// { +// return RC_SET( NE_FLM_MEM); +// } +// +// return NE_FLM_OK; +// } +// +// FINLINE void f_mutexDestroy( +// F_MUTEX * phMutex) +// { +// if (*phMutex != F_MUTEX_NULL) +// { +// if( kMutexFree( (MUTEX)(*phMutex))) +// { +// flmAssert( 0); +// } +// +// *phMutex = F_MUTEX_NULL; +// } +// } +// +// FINLINE void f_mutexLock( +// F_MUTEX hMutex) +// { +// (void)kMutexLock( (MUTEX)hMutex); +// } +// +// FINLINE void f_mutexUnlock( +// F_MUTEX hMutex) +// { +// (void)kMutexUnlock( (MUTEX)hMutex); +// } +// +// FINLINE void f_assertMutexLocked( +// F_MUTEX) +// { +// } +// +// typedef SEMAPHORE F_SEM; +// typedef SEMAPHORE * F_SEM_p; +// #define F_SEM_NULL 0 +// +// FINLINE RCODE f_semCreate( +// F_SEM * phSem) +// { +// if( (*phSem = (F_SEM)kSemaphoreAlloc( (BYTE *)"NOVDB", 0)) == F_SEM_NULL) +// { +// return RC_SET( NE_FLM_MEM); +// } +// +// return NE_FLM_OK; +// } +// +// FINLINE void f_semDestroy( +// F_SEM * phSem) +// { +// if (*phSem != F_SEM_NULL) +// { +// (void)kSemaphoreFree( (SEMAPHORE)(*phSem)); +// *phSem = F_SEM_NULL; +// } +// } +// +// FINLINE RCODE f_semWait( +// F_SEM hSem, +// FLMUINT uiTimeout) +// { +// RCODE rc = NE_FLM_OK; +// +// if( uiTimeout == F_SEM_WAITFOREVER) +// { +// if( kSemaphoreWait( (SEMAPHORE)hSem) != 0) +// { +// rc = RC_SET( NE_FLM_ERROR_WAITING_ON_SEMPAHORE); +// } +// } +// else +// { +// if( kSemaphoreTimedWait( (SEMAPHORE)hSem, (UINT)uiTimeout) != 0) +// { +// rc = RC_SET( NE_FLM_ERROR_WAITING_ON_SEMPAHORE); +// } +// } +// +// return( rc); +// } +// +// FINLINE void f_semSignal( +// F_SEM hSem) +// { +// (void)kSemaphoreSignal( (SEMAPHORE)hSem); +// } +// +// #elif defined( FLM_WIN) +// +// RCODE f_mutexCreate( +// F_MUTEX * phMutex); +// +// void f_mutexDestroy( +// F_MUTEX * phMutex); +// +// FINLINE void f_mutexLock( +// F_MUTEX hMutex) +// { +// while( flmAtomicExchange( +// &(((F_INTERLOCK *)hMutex)->locked), 1) != 0) +// { +// #ifdef FLM_DEBUG +// flmAtomicInc( &(((F_INTERLOCK *)hMutex)->waitCount)); +// #endif +// Sleep( 0); +// } +// +// #ifdef FLM_DEBUG +// flmAssert( ((F_INTERLOCK *)hMutex)->uiThreadId == 0); +// ((F_INTERLOCK *)hMutex)->uiThreadId = _threadid; +// flmAtomicInc( &(((F_INTERLOCK *)hMutex)->lockedCount)); +// #endif +// } +// +// FINLINE void f_mutexUnlock( +// F_MUTEX hMutex) +// { +// flmAssert( ((F_INTERLOCK *)hMutex)->locked == 1); +// #ifdef FLM_DEBUG +// flmAssert( ((F_INTERLOCK *)hMutex)->uiThreadId == _threadid); +// ((F_INTERLOCK *)hMutex)->uiThreadId = 0; +// #endif +// flmAtomicExchange( &(((F_INTERLOCK *)hMutex)->locked), 0); +// } +// +// FINLINE void f_assertMutexLocked( +// F_MUTEX hMutex) +// { +// #ifdef FLM_DEBUG +// flmAssert( ((F_INTERLOCK *)hMutex)->locked == 1); +// flmAssert( ((F_INTERLOCK *)hMutex)->uiThreadId == _threadid); +// #else +// F_UNREFERENCED_PARM( hMutex); +// #endif +// } +// +// FINLINE RCODE f_semCreate( +// F_SEM * phSem) +// { +// if( (*phSem = CreateSemaphore( (LPSECURITY_ATTRIBUTES)NULL, +// 0, 10000, NULL )) == NULL) +// { +// return( RC_SET( NE_FLM_COULD_NOT_CREATE_SEMAPHORE)); +// } +// +// return NE_FLM_OK; +// } +// +// FINLINE void f_semDestroy( +// F_SEM * phSem) +// { +// if (*phSem != F_SEM_NULL) +// { +// CloseHandle( *phSem); +// *phSem = F_SEM_NULL; +// } +// } +// +// FINLINE RCODE f_semWait( +// F_SEM hSem, +// FLMUINT uiTimeout) +// { +// if( WaitForSingleObject( hSem, uiTimeout ) == WAIT_OBJECT_0) +// { +// return( NE_FLM_OK); +// } +// else +// { +// return( RC_SET( NE_FLM_ERROR_WAITING_ON_SEMPAHORE)); +// } +// } +// +// FINLINE void f_semSignal( +// F_SEM hSem) +// { +// (void)ReleaseSemaphore( hSem, 1, NULL); +// } +// #elif defined( FLM_UNIX) +// RCODE f_mutexCreate( +// F_MUTEX * phMutex); +// +// void f_mutexDestroy( +// F_MUTEX * phMutex); +// +// FINLINE void f_mutexLock( +// F_MUTEX hMutex) +// { +// (void)pthread_mutex_lock( hMutex); +// } +// +// FINLINE void f_mutexUnlock( +// F_MUTEX hMutex) +// { +// (void)pthread_mutex_unlock( hMutex); +// } +// +// FINLINE void f_assertMutexLocked( +// F_MUTEX) +// { +// } +// +// int sema_signal( +// sema_t * sem); +// +// void f_semDestroy( +// F_SEM * phSem); +// +// RCODE f_semCreate( +// F_SEM * phSem); +// +// RCODE f_semWait( +// F_SEM hSem, +// FLMUINT uiTimeout); +// +// FINLINE void f_semSignal( +// F_SEM hSem) +// { +// (void)sema_signal( hSem); +// } +// +// #endif + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_IStream : public IF_IStream, public F_Base + { + public: + + F_IStream(); + + virtual ~F_IStream(); + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_OStream : public IF_OStream, public F_Base + { + public: + + F_OStream(); + + virtual ~F_OStream(); + + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_PosIStream : public IF_PosIStream, public F_Base + { + public: + + F_PosIStream(); + + virtual ~F_PosIStream(); + + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_BufferIStream : public F_PosIStream + { + public: + + F_BufferIStream() + { + m_pucBuffer = NULL; + m_uiBufferLen = 0; + m_uiOffset = 0; + m_bAllocatedBuffer = FALSE; + m_bIsOpen = FALSE; + } + + virtual ~F_BufferIStream(); + + RCODE FLMAPI open( + const FLMBYTE * pucBuffer, + FLMUINT uiLength, + FLMBYTE ** ppucAllocatedBuffer = NULL); + + FINLINE FLMUINT64 FLMAPI totalSize( void) + { + flmAssert( m_bIsOpen); + return( m_uiBufferLen); + } + + FINLINE FLMUINT64 FLMAPI remainingSize( void) + { + flmAssert( m_bIsOpen); + return( m_uiBufferLen - m_uiOffset); + } + + RCODE FLMAPI close( void); + + FINLINE RCODE FLMAPI positionTo( + FLMUINT64 ui64Position) + { + flmAssert( m_bIsOpen); + + if( ui64Position < m_uiBufferLen) + { + m_uiOffset = (FLMUINT)ui64Position; + } + else + { + m_uiOffset = m_uiBufferLen; + } + + return( NE_FLM_OK); + } + + FINLINE FLMUINT64 FLMAPI getCurrPosition( void) + { + flmAssert( m_bIsOpen); + return( m_uiOffset); + } + + RCODE FLMAPI read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead); + + FINLINE const FLMBYTE * getBuffer( void) + { + flmAssert( m_bIsOpen); + return( m_pucBuffer); + } + + FINLINE const FLMBYTE * getBufferAtCurrentOffset( void) + { + flmAssert( m_bIsOpen); + return( m_pucBuffer ? &m_pucBuffer[ m_uiOffset] : NULL); + } + + FINLINE void truncate( + FLMUINT uiOffset) + { + flmAssert( m_bIsOpen); + flmAssert( uiOffset >= m_uiOffset); + flmAssert( uiOffset <= m_uiBufferLen); + + m_uiBufferLen = uiOffset; + } + + FINLINE FLMBOOL isOpen( void) + { + return( m_bIsOpen); + } + + private: + + const FLMBYTE * m_pucBuffer; + FLMUINT m_uiBufferLen; + FLMUINT m_uiOffset; + FLMBOOL m_bAllocatedBuffer; + FLMBOOL m_bIsOpen; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_FileIStream : public F_PosIStream + { + public: + + F_FileIStream() + { + m_pFileHdl = NULL; + m_ui64FileOffset = 0; + } + + virtual ~F_FileIStream() + { + if( m_pFileHdl) + { + m_pFileHdl->Release(); + } + } + + RCODE FLMAPI open( + const char * pszPath); + + RCODE FLMAPI close( void); + + RCODE FLMAPI positionTo( + FLMUINT64 ui64Position); + + FLMUINT64 FLMAPI totalSize( void); + + FLMUINT64 FLMAPI remainingSize( void); + + FLMUINT64 FLMAPI getCurrPosition( void); + + RCODE FLMAPI read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead); + + private: + + IF_FileHdl * m_pFileHdl; + FLMUINT64 m_ui64FileOffset; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_BufferedIStream : public F_PosIStream + { + public: + + F_BufferedIStream() + { + m_pIStream = NULL; + m_pucBuffer = NULL; + } + + virtual ~F_BufferedIStream() + { + close(); + } + + RCODE FLMAPI open( + IF_IStream * pIStream, + FLMUINT uiBufferSize); + + RCODE FLMAPI read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead); + + RCODE FLMAPI close( void); + + FINLINE FLMUINT64 FLMAPI totalSize( void) + { + if (!m_pIStream) + { + flmAssert( 0); + return( 0); + } + + return( m_uiBytesAvail); + } + + FINLINE FLMUINT64 FLMAPI remainingSize( void) + { + if( !m_pIStream) + { + flmAssert( 0); + return( 0); + } + + return( m_uiBytesAvail - m_uiBufferOffset); + } + + FINLINE RCODE FLMAPI positionTo( + FLMUINT64 ui64Position) + { + if( !m_pIStream) + { + flmAssert( 0); + return( RC_SET( NE_FLM_ILLEGAL_OP)); + } + + if( ui64Position < m_uiBytesAvail) + { + m_uiBufferOffset = (FLMUINT)ui64Position; + } + else + { + m_uiBufferOffset = m_uiBytesAvail; + } + + return( NE_FLM_OK); + } + + FINLINE FLMUINT64 FLMAPI getCurrPosition( void) + { + if( !m_pIStream) + { + flmAssert( 0); + return( 0); + } + + return( m_uiBufferOffset); + } + + private: + + IF_IStream * m_pIStream; + FLMBYTE * m_pucBuffer; + FLMUINT m_uiBufferSize; + FLMUINT m_uiBufferOffset; + FLMUINT m_uiBytesAvail; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_BufferedOStream : public F_OStream + { + public: + + F_BufferedOStream() + { + m_pOStream = NULL; + m_pucBuffer = NULL; + } + + virtual ~F_BufferedOStream() + { + close(); + } + + RCODE FLMAPI open( + IF_OStream * pOStream, + FLMUINT uiBufferSize); + + RCODE FLMAPI write( + const void * pvBuffer, + FLMUINT uiBytesToWrite, + FLMUINT * puiBytesWritten); + + RCODE FLMAPI close( void); + + RCODE FLMAPI flush( void); + + private: + + IF_OStream * m_pOStream; + FLMBYTE * m_pucBuffer; + FLMUINT m_uiBufferSize; + FLMUINT m_uiBufferOffset; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_FileOStream : public F_OStream + { + public: + + F_FileOStream() + { + m_pFileHdl = NULL; + } + + virtual ~F_FileOStream() + { + close(); + } + + RCODE FLMAPI open( + const char * pszFilePath, + FLMBOOL bTruncateIfExists); + + RCODE FLMAPI write( + const void * pvBuffer, + FLMUINT uiBytesToWrite, + FLMUINT * puiBytesWritten); + + RCODE FLMAPI close( void); + + private: + + IF_FileHdl * m_pFileHdl; + FLMUINT64 m_ui64FileOffset; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_MultiFileIStream : public F_IStream + { + public: + + F_MultiFileIStream() + { + m_pIStream = NULL; + m_bOpen = FALSE; + } + + virtual ~F_MultiFileIStream() + { + close(); + } + + RCODE FLMAPI open( + const char * pszDirectory, + const char * pszBaseName); + + RCODE FLMAPI read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead); + + RCODE FLMAPI close( void); + + private: + + RCODE rollToNextFile( void); + + IF_IStream * m_pIStream; + FLMBOOL m_bOpen; + FLMBOOL m_bEndOfStream; + FLMUINT m_uiFileNum; + FLMUINT64 m_ui64FileOffset; + char m_szDirectory[ F_PATH_MAX_SIZE + 1]; + char m_szBaseName[ F_PATH_MAX_SIZE + 1]; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_MultiFileOStream : public F_OStream + { + public: + + F_MultiFileOStream() + { + m_pOStream = NULL; + m_bOpen = FALSE; + } + + virtual ~F_MultiFileOStream() + { + close(); + } + + RCODE create( + const char * pszDirectory, + const char * pszBaseName, + FLMUINT uiMaxFileSize, + FLMBOOL bOkToOverwrite); + + RCODE FLMAPI write( + const void * pvBuffer, + FLMUINT uiBytesToWrite, + FLMUINT * puiBytesWritten); + + RCODE FLMAPI close( void); + + private: + + RCODE rollToNextFile( void); + + RCODE processDirectory( + const char * pszDirectory, + const char * pszBaseName, + FLMBOOL bOkToDelete); + + F_OStream * m_pOStream; + FLMBOOL m_bOpen; + FLMUINT m_uiFileNum; + FLMUINT64 m_ui64MaxFileSize; + FLMUINT64 m_ui64FileOffset; + char m_szDirectory[ F_PATH_MAX_SIZE + 1]; + char m_szBaseName[ F_PATH_MAX_SIZE + 1]; + + friend class F_DbSystem; + }; + + /**************************************************************************** + Desc: Decodes an ASCII base64 stream to binary + ****************************************************************************/ + class F_Base64DecoderIStream : public F_IStream + { + public: + + F_Base64DecoderIStream() + { + m_pIStream = NULL; + m_uiBufOffset = 0; + m_uiAvailBytes = 0; + } + + virtual ~F_Base64DecoderIStream() + { + close(); + } + + RCODE FLMAPI open( + IF_IStream * pIStream); + + RCODE FLMAPI read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead); + + FINLINE RCODE FLMAPI close( void) + { + RCODE rc = NE_FLM_OK; + + if( m_pIStream) + { + if( m_pIStream->getRefCount() == 1) + { + rc = m_pIStream->close(); + } + + m_pIStream->Release(); + m_pIStream = NULL; + } + + m_uiAvailBytes = 0; + m_uiBufOffset = 0; + + return( rc); + } + + private: + + IF_IStream * m_pIStream; + FLMUINT m_uiBufOffset; + FLMUINT m_uiAvailBytes; + FLMBYTE m_ucBuffer[ 8]; + static FLMBYTE m_ucDecodeTable[ 256]; + }; + + /**************************************************************************** + Desc: Encodes a binary input stream into ASCII base64. + ****************************************************************************/ + class F_Base64EncoderIStream : public F_IStream + { + public: + + F_Base64EncoderIStream() + { + m_pIStream = NULL; + } + + virtual ~F_Base64EncoderIStream() + { + close(); + } + + RCODE FLMAPI open( + IF_IStream * pIStream, + FLMBOOL bLineBreaks); + + RCODE FLMAPI read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead); + + FINLINE RCODE FLMAPI close( void) + { + RCODE rc = NE_FLM_OK; + + if( m_pIStream) + { + if( m_pIStream->getRefCount() == 1) + { + rc = m_pIStream->close(); + } + + m_pIStream->Release(); + m_pIStream = NULL; + } + + return( rc); + } + + private: + + IF_IStream * m_pIStream; + FLMBOOL m_bInputExhausted; + FLMBOOL m_bLineBreaks; + FLMBOOL m_bPriorLineEnd; + FLMUINT m_uiBase64Count; + FLMUINT m_uiBufOffset; + FLMUINT m_uiAvailBytes; + FLMBYTE m_ucBuffer[ 8]; + static FLMBYTE m_ucEncodeTable[ 64]; + }; + + typedef struct LZWODictItem + { + LZWODictItem * pNext; + FLMUINT16 ui16Code; + FLMUINT16 ui16ParentCode; + FLMBYTE ucChar; + } LZWODictItem; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_CompressingOStream : public F_OStream + { + public: + + F_CompressingOStream() + { + m_pOStream = NULL; + m_ppHashTbl = NULL; + m_pool.poolInit( 64 * 1024); + } + + virtual ~F_CompressingOStream() + { + close(); + } + + RCODE FLMAPI open( + IF_OStream * pOStream); + + RCODE FLMAPI write( + const void * pvBuffer, + FLMUINT uiBytesToWrite, + FLMUINT * puiBytesWritten); + + RCODE FLMAPI close( void); + + private: + + FINLINE FLMUINT getHashBucket( + FLMUINT16 ui16CurrentCode, + FLMBYTE ucChar) + { + return( ((((FLMUINT)ui16CurrentCode) << 8) | + ((FLMUINT)ucChar)) % m_uiHashTblSize); + } + + LZWODictItem * findDictEntry( + FLMUINT16 ui16CurrentCode, + FLMBYTE ucChar); + + IF_OStream * m_pOStream; + LZWODictItem ** m_ppHashTbl; + FLMUINT m_uiHashTblSize; + FLMUINT m_uiLastRatio; + FLMUINT m_uiBestRatio; + FLMUINT m_uiCurrentBytesIn; + FLMUINT m_uiTotalBytesIn; + FLMUINT m_uiCurrentBytesOut; + FLMUINT m_uiTotalBytesOut; + FLMBOOL m_bStopCompression; + FLMUINT16 m_ui16CurrentCode; + FLMUINT16 m_ui16FreeCode; + F_Pool m_pool; + }; + + typedef struct LZWIDictItem + { + LZWODictItem * pNext; + FLMUINT16 ui16ParentCode; + FLMBYTE ucChar; + } LZWIDictItem; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_UncompressingIStream : public F_IStream + { + public: + + F_UncompressingIStream() + { + m_pIStream = NULL; + m_pDict = NULL; + m_pucDecodeBuffer = NULL; + } + + virtual ~F_UncompressingIStream() + { + close(); + } + + RCODE FLMAPI open( + IF_IStream * pIStream); + + RCODE FLMAPI read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead); + + RCODE FLMAPI close( void); + + private: + + RCODE readCode( + FLMUINT16 * pui16Code); + + RCODE decodeToBuffer( + FLMUINT16 ui16Code); + + IF_IStream * m_pIStream; + LZWIDictItem * m_pDict; + FLMBYTE * m_pucDecodeBuffer; + FLMUINT m_uiDecodeBufferSize; + FLMUINT m_uiDecodeBufferOffset; + FLMUINT16 m_ui16FreeCode; + FLMUINT16 m_ui16LastCode; + FLMBOOL m_bStopCompression; + FLMBOOL m_bEndOfStream; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_TCPStream : public F_IStream, public F_OStream + { + public: + + F_TCPStream( void); + + virtual ~F_TCPStream( void); + + RCODE openConnection( + const char * pucHostAddress, + FLMUINT uiPort, + FLMUINT uiConnectTimeout = 3, + FLMUINT uiDataTimeout = 15); + + RCODE FLMAPI read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead); + + RCODE FLMAPI write( + const void * pvBuffer, + FLMUINT uiBytesToWrite, + FLMUINT * puiBytesWritten); + + FINLINE RCODE socketPeekWrite( + FLMINT iTimeOut) + { + return( socketPeek( iTimeOut, FALSE)); + } + + FINLINE RCODE socketPeekRead( + FLMINT iTimeOut) + { + return( socketPeek( iTimeOut, TRUE)); + }; + + FINLINE const char * getName( void) + { + getLocalInfo(); + return( (const char *)m_pszName); + }; + + FINLINE const char * getAddr( void) + { + getLocalInfo(); + return( (const char *)m_pszIp); + }; + + FINLINE const char * getPeerName( void) + { + getRemoteInfo(); + return( (const char *)m_pszPeerName); + }; + + FINLINE const char * getPeerAddr( void) + { + getRemoteInfo(); + return( (const char *)m_pszPeerIp); + }; + + RCODE readNoWait( + void * pvBuffer, + FLMUINT uiCount, + FLMUINT * puiReadRead); + + RCODE readAll( + void * pvBuffer, + FLMUINT uiCount, + FLMUINT * puiBytesRead); + + RCODE setTcpDelay( + FLMBOOL bOn); + + RCODE FLMAPI close( void); + + private: + + RCODE getLocalInfo( void); + + RCODE getRemoteInfo( void); + + RCODE socketPeek( + FLMINT iTimoutVal, + FLMBOOL bPeekRead); + + #ifndef FLM_UNIX + WSADATA m_wsaData; + #endif + FLMBOOL m_bInitialized; + SOCKET m_iSocket; + FLMUINT m_uiIOTimeout; + FLMBOOL m_bConnected; + char m_pszIp[ 256]; + char m_pszName[ 256]; + char m_pszPeerIp[ 256]; + char m_pszPeerName[ 256]; + unsigned long m_ulRemoteAddr; + }; + + /**************************************************************************** + Misc. + ****************************************************************************/ + + FINLINE FLMBOOL f_isHexChar( + FLMBYTE ucChar) + { + if( (ucChar >= '0' && ucChar <= '9') || + (ucChar >= 'A' && ucChar <= 'F') || + (ucChar >= 'a' && ucChar <= 'f')) + { + return( TRUE); + } + + return( FALSE); + } + + FINLINE FLMBOOL f_isHexChar( + FLMUNICODE uChar) + { + if( uChar > 127) + { + return( FALSE); + } + + return( f_isHexChar( f_tonative( (FLMBYTE)uChar))); + } + + FINLINE FLMBYTE f_getHexVal( + FLMBYTE ucChar) + { + if( ucChar >= '0' && ucChar <= '9') + { + return( (FLMBYTE)(ucChar - '0')); + } + else if( ucChar >= 'A' && ucChar <= 'F') + { + return( (FLMBYTE)((ucChar - 'A') + 10)); + } + else if( ucChar >= 'a' && ucChar <= 'f') + { + return( (FLMBYTE)((ucChar - 'a') + 10)); + } + + return( 0); + } + + FINLINE FLMBYTE f_getHexVal( + FLMUNICODE uChar) + { + return( f_getHexVal( f_tonative( (FLMBYTE)uChar))); + } + + FINLINE FLMBOOL f_isValidHexNum( + const FLMBYTE * pszString) + { + if( *pszString == 0) + { + return( FALSE); + } + + while( *pszString) + { + if( !f_isHexChar( *pszString)) + { + return( TRUE); + } + + pszString++; + } + + return( TRUE); + } + + /**************************************************************************** + Process ID Functions + ****************************************************************************/ + + #if defined( FLM_WIN) + + FINLINE FLMUINT f_getpid( void) + { + return _getpid(); + } + + #elif defined( FLM_UNIX) + + pid_t getpid( void); + + FINLINE FLMUINT f_getpid( void) + { + return getpid(); + } + + #elif defined( FLM_NLM) + + FINLINE FLMUINT f_getpid() + { + return( f_getNLMHandle()); + } + + #else + #error "Unsupported Platform" + #endif + + /**************************************************************************** + f_sprintf + ****************************************************************************/ + + typedef struct + { + FLMBYTE * pszDestStr; + } F_SPRINTF_INFO; + + // Percent formating prefixes + + #define FLM_PREFIX_NONE 0 + #define FLM_PREFIX_MINUS 1 + #define FLM_PREFIX_PLUS 2 + #define FLM_PREFIX_POUND 3 + + // Width and Precision flags + + #define FLM_PRINTF_MINUS_FLAG 0x0001 + #define FLM_PRINTF_PLUS_FLAG 0x0002 + #define FLM_PRINTF_SPACE_FLAG 0x0004 + #define FLM_PRINTF_POUND_FLAG 0x0008 + #define FLM_PRINTF_ZERO_FLAG 0x0010 + #define FLM_PRINTF_SHORT_FLAG 0x0020 + #define FLM_PRINTF_LONG_FLAG 0x0040 + #define FLM_PRINTF_DOUBLE_FLAG 0x0080 + #define FLM_PRINTF_INT64_FLAG 0x0100 + #define FLM_PRINTF_COMMA_FLAG 0x0200 + + void f_sprintfProcessFieldInfo( + FLMBYTE ** ppszFormat, + FLMUINT * puiWidth, + FLMUINT * puiPrecision, + FLMUINT * puiFlags, + f_va_list * args); + + void f_sprintfStringFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + F_SPRINTF_INFO * pInfo, + f_va_list * args); + + void f_sprintfCharFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + F_SPRINTF_INFO * pInfo, + f_va_list * args); + + void f_sprintfErrorFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + F_SPRINTF_INFO * pInfo, + f_va_list * args); + + void f_sprintfNotHandledFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + F_SPRINTF_INFO * pInfo, + f_va_list * args); + + void f_sprintfNumberFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + F_SPRINTF_INFO * pInfo, + f_va_list * args); + + #if defined( FLM_WIN) + + typedef struct + { + HANDLE findHandle; + WIN32_FIND_DATA findBuffer; + char szSearchPath[ F_PATH_MAX_SIZE]; + FLMUINT uiSearchAttrib; + } F_IO_FIND_DATA; + + #define F_IO_FA_NORMAL FILE_ATTRIBUTE_NORMAL // Normal file + #define F_IO_FA_RDONLY FILE_ATTRIBUTE_READONLY // Read only attribute + #define F_IO_FA_HIDDEN FILE_ATTRIBUTE_HIDDEN // Hidden file + #define F_IO_FA_SYSTEM FILE_ATTRIBUTE_SYSTEM // System file + #define F_IO_FA_VOLUME FILE_ATTRIBUTE_VOLUME // Volume label + #define F_IO_FA_DIRECTORY FILE_ATTRIBUTE_DIRECTORY // Directory + #define F_IO_FA_ARCHIVE FILE_ATTRIBUTE_ARCHIVE // Archive + + #elif defined( FLM_UNIX) || defined( FLM_NLM) + + typedef struct _DirInfo + { + mode_t mode_flag; + struct stat FileStat; + char name[ F_PATH_MAX_SIZE+1]; + char search_path[ F_PATH_MAX_SIZE+1]; + char full_path[ F_PATH_MAX_SIZE]; + char pattern_str[ F_PATH_MAX_SIZE]; + char dirpath[ F_PATH_MAX_SIZE]; + glob_t globbuf; + } F_IO_FIND_DATA; + + #define F_IO_FA_NORMAL 0x01 // Normal file, no attributes + #define F_IO_FA_RDONLY 0x02 // Read only attribute + #define F_IO_FA_HIDDEN 0x04 // Hidden file + #define F_IO_FA_SYSTEM 0x08 // System file + #define F_IO_FA_VOLUME 0x10 // Volume label + #define F_IO_FA_DIRECTORY 0x20 // Directory + #define F_IO_FA_ARCHIVE 0x40 // Archive + + #else + #error Platform not supported + #endif + + RCODE f_fileFindFirst( + char * pszSearchPath, + FLMUINT uiSearchAttrib, + F_IO_FIND_DATA * find_data, + char * pszFoundPath, + FLMUINT * puiFoundAttrib); + + RCODE f_fileFindNext( + F_IO_FIND_DATA * pFindData, + char * pszFoundPath, + FLMUINT * puiFoundAttrib); + + void f_fileFindClose( + F_IO_FIND_DATA * pFindData); + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_IOBufferMgr : public IF_IOBufferMgr, public F_Base + { + public: + + F_IOBufferMgr(); + + virtual ~F_IOBufferMgr(); + + RCODE FLMAPI waitForAllPendingIO( void); + + FINLINE void FLMAPI setMaxBuffers( + FLMUINT uiMaxBuffers) + { + m_uiMaxBuffers = uiMaxBuffers; + } + + FINLINE void FLMAPI setMaxBytes( + FLMUINT uiMaxBytes) + { + m_uiMaxBufferBytesToUse = uiMaxBytes; + } + + FINLINE void FLMAPI enableKeepBuffer( void) + { + m_bKeepBuffers = TRUE; + } + + RCODE FLMAPI getBuffer( + IF_IOBuffer ** ppIOBuffer, + FLMUINT uiBufferSize, + FLMUINT uiBlockSize); + + FINLINE FLMBOOL FLMAPI havePendingIO( void) + { + return( m_pFirstPending ? TRUE : FALSE); + } + + FINLINE FLMBOOL FLMAPI haveUsed( void) + { + return( m_pFirstUsed ? TRUE : FALSE); + } + + private: + + // Private methods and variables + + F_IOBuffer * m_pFirstPending; + F_IOBuffer * m_pFirstAvail; + F_IOBuffer * m_pFirstUsed; + FLMUINT m_uiMaxBuffers; + FLMUINT m_uiMaxBufferBytesToUse; + FLMUINT m_uiBufferBytesInUse; + FLMUINT m_uiBuffersInUse; + RCODE m_completionRc; + FLMBOOL m_bKeepBuffers; + + void linkToList( + F_IOBuffer ** ppListHead, + F_IOBuffer * pIOBuffer); + + void unlinkFromList( + F_IOBuffer * pIOBuffer); + + friend class F_IOBuffer; + + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_IOBuffer : public IF_IOBuffer, public F_Base + { + #define MAX_BUFFER_BLOCKS 16 + public: + + F_IOBuffer(); + + virtual ~F_IOBuffer(); + + RCODE FLMAPI setupBuffer( + FLMUINT uiBufferSize, + FLMUINT uiBlockSize); + + FINLINE FLMBYTE * FLMAPI getBuffer( void) + { + return( m_pucBuffer); + } + + FINLINE FLMUINT FLMAPI getBufferSize( void) + { + return( m_uiBufferSize); + } + + FINLINE FLMUINT FLMAPI getBlockSize( void) + { + return( m_uiBlockSize); + } + + void FLMAPI notifyComplete( + RCODE rc); + + FINLINE void FLMAPI setCompletionCallback( + WRITE_COMPLETION_CB fnCompletion) + { + m_fnCompletion = fnCompletion; + } + + FINLINE void FLMAPI setCompletionCallbackData( + FLMUINT uiBlockNumber, + void * pvData) + { + flmAssert( uiBlockNumber < MAX_BUFFER_BLOCKS); + m_UserData [uiBlockNumber] = pvData; + } + + FINLINE void * FLMAPI getCompletionCallbackData( + FLMUINT uiBlockNumber) + { + flmAssert( uiBlockNumber < MAX_BUFFER_BLOCKS); + return( m_UserData [uiBlockNumber]); + } + + FINLINE RCODE FLMAPI getCompletionCode( void) + { + return( m_completionRc); + } + + FINLINE eBufferMgrList FLMAPI getList( void) + { + return( m_eList); + } + + void FLMAPI makePending( void); + + #ifdef FLM_WIN + FINLINE OVERLAPPED * getOverlapped( void) + { + return( &m_Overlapped); + } + + FINLINE void setFileHandle( + HANDLE FileHandle) + { + m_FileHandle = FileHandle; + } + #endif + + #if defined( FLM_LINUX) || defined( FLM_SOLARIS) || defined( FLM_OSX) + FINLINE struct aiocb * getAIOStruct( void) + { + return( &m_aio); + } + #endif + + #ifdef FLM_NLM + void signalComplete( + RCODE rc); + #endif + + private: + + // Only called by the buffer manager + + RCODE setupIOBuffer( + F_IOBufferMgr * pIOBufferMgr); + + FLMBOOL isIOComplete( void); + + RCODE waitToComplete( void); + + // Private methods and variables + + F_IOBufferMgr * m_pIOBufferMgr; + FLMBYTE * m_pucBuffer; + void * m_UserData [MAX_BUFFER_BLOCKS]; + FLMUINT m_uiBufferSize; + FLMUINT m_uiBlockSize; + eBufferMgrList m_eList; + FLMBOOL m_bDeleteOnNotify; + #ifdef FLM_WIN + HANDLE m_FileHandle; + OVERLAPPED m_Overlapped; + #endif + #if defined( FLM_LINUX) || defined( FLM_SOLARIS) || defined( FLM_OSX) + struct aiocb m_aio; + #endif + #ifdef FLM_NLM + F_SEM m_hSem; + #endif + F_IOBuffer * m_pNext; + F_IOBuffer * m_pPrev; + WRITE_COMPLETION_CB m_fnCompletion; + RCODE m_completionRc; + F_TMSTAMP m_StartTime; + FLMUINT64 m_ui64ElapMilli; + + friend class F_IOBufferMgr; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_DirHdl : public IF_DirHdl, public F_Base + { + public: + + F_DirHdl(); + + virtual ~F_DirHdl() + { + if( m_bFindOpen) + { + f_fileFindClose( &m_FindData); + } + } + + RCODE openDir( + const char * pszDirName, + const char * pszPattern); + + RCODE createDir( + const char * pszDirName); + + RCODE removeDir( + const char * pszDirPath); + + RCODE FLMAPI next( void); + + const char * FLMAPI currentItemName( void); + + void FLMAPI currentItemPath( + char * pszPath); + + FLMUINT64 FLMAPI currentItemSize( void); + + FLMBOOL FLMAPI currentItemIsDir( void); + + private: + + char m_szDirectoryPath[ F_PATH_MAX_SIZE]; + char m_szPattern[ F_PATH_MAX_SIZE]; + FLMUINT32 m_ui32RefCount; + RCODE m_rc; + FLMBOOL m_bFirstTime; + FLMBOOL m_bFindOpen; + FLMBOOL m_EOF; + char m_szFileName[ F_PATH_MAX_SIZE]; + FLMUINT m_uiAttrib; + F_IO_FIND_DATA m_FindData; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_FileSystem : public IF_FileSystem, public F_Base + { + public: + + F_FileSystem() + { + } + + virtual ~F_FileSystem() + { + } + + RCODE FLMAPI createFile( + const char * pszFileName, + FLMUINT uiIoFlags, + IF_FileHdl ** ppFile); + + RCODE FLMAPI createBlockFile( + const char * pszFileName, + FLMUINT uiIoFlags, + FLMUINT uiBlockSize, + IF_FileHdl ** ppFile); + + RCODE FLMAPI createUniqueFile( + const char * pszDirName, + const char * pszFileExtension, + FLMUINT uiIoFlags, + IF_FileHdl ** ppFile); + + RCODE FLMAPI openFile( + const char * pszFileName, + FLMUINT uiIoFlags, + IF_FileHdl ** ppFile); + + RCODE FLMAPI openBlockFile( + const char * pszFileName, + FLMUINT uiIoFlags, + FLMUINT uiBlockSize, + IF_FileHdl ** ppFile); + + RCODE FLMAPI openDir( + const char * pszDirName, + const char * pszPattern, + IF_DirHdl ** ppDir); + + RCODE FLMAPI createDir( + const char * pszDirName); + + RCODE FLMAPI removeDir( + const char * pszDirName, + FLMBOOL bClear = FALSE); + + RCODE FLMAPI doesFileExist( + const char * pszFileName); + + FLMBOOL FLMAPI isDir( + const char * pszFileName); + + RCODE FLMAPI getFileTimeStamp( + const char * pszFileName, + FLMUINT * puiTimeStamp); + + RCODE FLMAPI deleteFile( + const char * pszFileName); + + RCODE FLMAPI copyFile( + const char * pszSrcFileName, + const char * pszDestFileName, + FLMBOOL bOverwrite, + FLMUINT64 * pui64BytesCopied); + + RCODE FLMAPI renameFile( + const char * pszFileName, + const char * pszNewFileName); + + void FLMAPI pathParse( + const char * pszPath, + char * pszServer, + char * pszVolume, + char * pszDirPath, + char * pszFileName); + + RCODE FLMAPI pathReduce( + const char * pszSourcePath, + char * pszDestPath, + char * pszString); + + RCODE FLMAPI pathAppend( + char * pszPath, + const char * pszPathComponent); + + RCODE FLMAPI pathToStorageString( + const char * pszPath, + char * pszString); + + void FLMAPI pathCreateUniqueName( + FLMUINT * puiTime, + char * pszFileName, + const char * pszFileExt, + FLMBYTE * pHighChars, + FLMBOOL bModext); + + FLMBOOL FLMAPI doesFileMatch( + const char * pszFileName, + const char * pszTemplate); + + RCODE FLMAPI getSectorSize( + const char * pszFileName, + FLMUINT * puiSectorSize); + + RCODE setReadOnly( + const char * pszFileName, + FLMBOOL bReadOnly); + + private: + + #if defined( FLM_UNIX) + RCODE unix_RenameSafe( + const char * pszSrcFile, + const char * pszDestFile); + + RCODE unix_TargetIsDir( + const char * tpath, + FLMBOOL * isdir); + #endif + }; + + /*************************************************************************** + Desc: + ***************************************************************************/ + #ifdef FLM_WIN + class F_FileHdl : public IF_FileHdl, public F_Base + { + public: + + F_FileHdl(); + + virtual ~F_FileHdl(); + + RCODE FLMAPI close( void); + + RCODE FLMAPI create( + const char * pszFileName, + FLMUINT uiIoFlags); + + RCODE FLMAPI createUnique( + const char * pszDirName, + const char * pszFileExtension, + FLMUINT uiIoFlags); + + RCODE FLMAPI open( + const char * pszFileName, + FLMUINT uiIoFlags); + + RCODE FLMAPI flush( void); + + RCODE FLMAPI read( + FLMUINT64 ui64Offset, + FLMUINT uiLength, + void * pvBuffer, + FLMUINT * puiBytesRead); + + RCODE FLMAPI seek( + FLMUINT64 ui64Offset, + FLMINT iWhence, + FLMUINT64 * pui64NewOffset); + + RCODE FLMAPI size( + FLMUINT64 * pui64Size); + + RCODE FLMAPI tell( + FLMUINT64 * pui64Offset); + + RCODE FLMAPI truncate( + FLMUINT64 ui64Size); + + RCODE FLMAPI write( + FLMUINT64 ui64Offset, + FLMUINT uiLength, + const void * pvBuffer, + FLMUINT * puiBytesWritten); + + FINLINE RCODE FLMAPI sectorRead( + FLMUINT64 ui64ReadOffset, + FLMUINT uiBytesToRead, + void * pvBuffer, + FLMUINT * puiBytesReadRV) + { + if (m_bDoDirectIO) + { + return( directRead( ui64ReadOffset, uiBytesToRead, + pvBuffer, TRUE, puiBytesReadRV)); + } + else + { + return( read( ui64ReadOffset, uiBytesToRead, pvBuffer, puiBytesReadRV)); + } + } + + FINLINE RCODE FLMAPI sectorWrite( + FLMUINT64 ui64WriteOffset, + FLMUINT uiBytesToWrite, + const void * pvBuffer, + FLMUINT uiBufferSize, + void * pvBufferObj, + FLMUINT * puiBytesWrittenRV, + FLMBOOL bZeroFill = TRUE) + { + F_UNREFERENCED_PARM( uiBufferSize); + + if (m_bDoDirectIO) + { + return( directWrite( ui64WriteOffset, uiBytesToWrite, + pvBuffer, (F_IOBuffer *)pvBufferObj, TRUE, + bZeroFill, puiBytesWrittenRV)); + } + else + { + flmAssert( pvBufferObj == NULL); + return( write( ui64WriteOffset, uiBytesToWrite, pvBuffer, + puiBytesWrittenRV)); + } + } + + FINLINE FLMBOOL FLMAPI canDoAsync( void) + { + return( m_bCanDoAsync); + } + + FINLINE void FLMAPI setExtendSize( + FLMUINT uiExtendSize) + { + m_uiExtendSize = uiExtendSize; + } + + FINLINE void FLMAPI setMaxAutoExtendSize( + FLMUINT uiMaxAutoExtendSize) + { + m_uiMaxAutoExtendSize = uiMaxAutoExtendSize; + } + + FINLINE void setupFileHdl( + FLMUINT uiFileId, + FLMBOOL bDeleteOnRelease) + { + m_uiFileId = uiFileId; + m_bDeleteOnRelease = bDeleteOnRelease; + } + + FINLINE FLMUINT getFileId( void) + { + return( m_uiFileId); + } + + FINLINE FLMUINT getSectorSize( void) + { + return( m_uiBytesPerSector); + } + + FINLINE void setBlockSize( + FLMUINT uiBlockSize) + { + m_uiBlockSize = uiBlockSize; + } + + FINLINE HANDLE getFileHandle( void) + { + return m_FileHandle; + } + + private: + + RCODE openOrCreate( + const char * pszFileName, + FLMUINT uiAccess, + FLMBOOL bCreateFlag); + + RCODE allocAlignBuffer( void); + + RCODE doOneRead( + FLMUINT64 ui64Offset, + FLMUINT uiLength, + void * pvBuffer, + FLMUINT * puiBytesRead); + + RCODE directRead( + FLMUINT64 uiOffset, + FLMUINT uiLength, + void * pvBuffer, + FLMBOOL bBuffHasFullSectors, + FLMUINT * puiBytesRead); + + RCODE directWrite( + FLMUINT64 uiOffset, + FLMUINT uiLength, + const void * pvBuffer, + F_IOBuffer * pBufferObj, + FLMBOOL bBuffHasFullSectors, + FLMBOOL bZeroFill, + FLMUINT * puiBytesWritten); + + FINLINE FLMUINT64 roundToNextSector( + FLMUINT64 ui64Bytes) + { + return( (ui64Bytes + m_ui64NotOnSectorBoundMask) & + m_ui64GetSectorBoundMask); + } + + FINLINE FLMUINT64 truncateToPrevSector( + FLMUINT64 ui64Offset) + { + return( ui64Offset & m_ui64GetSectorBoundMask); + } + + RCODE extendFile( + FLMUINT64 ui64EndOfLastWrite, + FLMUINT uiMaxBytesToExtend, + FLMBOOL bFlush); + + F_FileHdl * m_pNext; + F_FileHdl * m_pPrev; + FLMBOOL m_bInList; + FLMBOOL m_bFileOpened; + FLMUINT m_uiAvailTime; + FLMUINT m_uiFileId; + FLMBOOL m_bDeleteOnRelease; + FLMBOOL m_bOpenedReadOnly; + FLMBOOL m_bOpenedExclusive; + char * m_pszFileName; + HANDLE m_FileHandle; + FLMUINT m_uiBlockSize; + FLMUINT m_uiBytesPerSector; + FLMUINT64 m_ui64NotOnSectorBoundMask; + FLMUINT64 m_ui64GetSectorBoundMask; + FLMBOOL m_bDoDirectIO; + FLMUINT m_uiExtendSize; + FLMUINT m_uiMaxAutoExtendSize; + FLMBYTE * m_pucAlignedBuff; + FLMUINT m_uiAlignedBuffSize; + FLMUINT64 m_ui64CurrentPos; + FLMBOOL m_bCanDoAsync; + OVERLAPPED m_Overlapped; + + friend class F_FileHdlMgr; + friend class F_FileHdlMgr; + }; + #endif + + /*************************************************************************** + Desc: + ***************************************************************************/ + #ifdef FLM_UNIX + class F_FileHdl : public IF_FileHdl, public F_Base + { + public: + + F_FileHdl(); + + ~F_FileHdl(); + + RCODE FLMAPI setup( + FLMUINT uiFileId); + + RCODE FLMAPI close( void); + + RCODE FLMAPI create( + const char * pszFileName, + FLMUINT uiIoFlags); + + RCODE FLMAPI createUnique( + const char * pszDirName, + const char * pszFileExtension, + FLMUINT uiIoFlags); + + RCODE FLMAPI open( + const char * pszFileName, + FLMUINT uiIoFlags); + + RCODE FLMAPI flush( void); + + RCODE FLMAPI read( + FLMUINT64 ui64Offset, + FLMUINT uiLength, + void * pvBuffer, + FLMUINT * puiBytesRead); + + RCODE FLMAPI seek( + FLMUINT64 ui64Offset, + FLMINT iWhence, + FLMUINT64 * pui64NewOffset); + + RCODE FLMAPI size( + FLMUINT64 * pui64Size); + + RCODE FLMAPI tell( + FLMUINT64 * pui64Offset); + + RCODE FLMAPI truncate( + FLMUINT64 ui64Size); + + RCODE FLMAPI write( + FLMUINT64 ui64Offset, + FLMUINT uiLength, + const void * pvBuffer, + FLMUINT * puiBytesWritten); + + RCODE FLMAPI sectorRead( + FLMUINT64 ui64ReadOffset, + FLMUINT uiBytesToRead, + void * pvBuffer, + FLMUINT * puiBytesReadRV); + + RCODE FLMAPI sectorWrite( + FLMUINT64 ui64WriteOffset, + FLMUINT uiBytesToWrite, + const void * pvBuffer, + FLMUINT uiBufferSize, + void * pvBufferObj, + FLMUINT * puiBytesWrittenRV, + FLMBOOL bZeroFill = TRUE) + { + if( m_bDoDirectIO) + { + return( DirectWrite( ui64WriteOffset, uiBytesToWrite, + pvBuffer, uiBufferSize, (F_IOBuffer *)pvBufferObj, + puiBytesWrittenRV, TRUE, bZeroFill)); + } + else + { + return( Write( ui64WriteOffset, uiBytesToWrite, + pvBuffer, puiBytesWrittenRV)); + } + } + + FLMBOOL FLMAPI canDoAsync( void); + + FINLINE FLMBOOL FLMAPI usingDirectIo( void) + { + return( m_bDoDirectIO); + } + + FINLINE void FLMAPI setExtendSize( + FLMUINT uiExtendSize) + { + m_uiExtendSize = uiExtendSize; + } + + FINLINE void FLMAPI setMaxAutoExtendSize( + FLMUINT uiMaxAutoExtendSize) + { + m_uiMaxAutoExtendSize = uiMaxAutoExtendSize; + } + + RCODE FLMAPI lock( void); + + RCODE FLMAPI unlock( void); + + FINLINE void FLMAPI setBlockSize( + FLMUINT uiBlockSize) + { + m_uiBlockSize = uiBlockSize; + } + + FINLINE FLMUINT FLMAPI getSectorSize( void) + { + return( m_uiBytesPerSector); + } + + FINLINE void setupFileHdl( + FLMUINT uiFileId, + FLMBOOL bDeleteOnRelease) + { + m_uiFileId = uiFileId; + m_bDeleteOnRelease = bDeleteOnRelease; + } + + FINLINE FLMUINT getFileId( void) + { + return m_uiFileId; + } + + private: + + RCODE openOrCreate( + const char * pszFileName, + FLMUINT uiAccess, + FLMBOOL bCreateFlag); + + FINLINE FLMUINT64 roundUpToSectorMultiple( + FLMUINT64 ui64Bytes) + { + return( (ui64Bytes + m_ui64NotOnSectorBoundMask) & + m_ui64GetSectorBoundMask); + } + + FINLINE FLMUINT64 getSectorStartOffset( + FLMUINT64 ui64Offset) + { + return( ui64Offset & m_ui64GetSectorBoundMask); + } + + RCODE directRead( + FLMUINT64 ui64ReadOffset, + FLMUINT uiBytesToRead, + void * pvBuffer, + FLMBOOL bBuffHasFullSectors, + FLMUINT * puiBytesRead); + + RCODE directWrite( + FLMUINT64 ui64WriteOffset, + FLMUINT uiBytesToWrite, + const void * pvBuffer, + FLMUINT uiBufferSize, + F_IOBuffer * pBufferObj, + FLMUINT * puiBytesWrittenRV, + FLMBOOL bBuffHasFullSectors, + FLMBOOL bZeroFill); + + RCODE allocAlignBuffer( void); + + F_FileHdl * m_pNext; + F_FileHdl * m_pPrev; + FLMBOOL m_bInList; + FLMBOOL m_bFileOpened; + FLMUINT m_uiAvailTime; + FLMUINT m_uiFileId; + FLMBOOL m_bDeleteOnRelease; + FLMBOOL m_bOpenedReadOnly; + FLMBOOL m_bOpenedExclusive; + char * m_pszFileName; + int m_fd; + FLMUINT m_uiBlockSize; + FLMUINT m_uiBytesPerSector; + FLMUINT64 m_ui64NotOnSectorBoundMask; + FLMUINT64 m_ui64GetSectorBoundMask; + FLMUINT64 m_ui64CurrentPos; + FLMUINT m_uiExtendSize; + FLMUINT m_uiMaxAutoExtendSize; + FLMBOOL m_bCanDoAsync; + FLMBOOL m_bDoDirectIO; + FLMBYTE * m_pucAlignedBuff; + FLMUINT m_uiAlignedBuffSize; + + friend class F_FileHdlMgr; + }; + #endif + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_FileHdlMgr : public F_Base + { + public: + + F_FileHdlMgr(); + + virtual ~F_FileHdlMgr() + { + if( m_hMutex != F_MUTEX_NULL) + { + lockMutex( FALSE); + freeUsedList( TRUE); + freeAvailList( TRUE); + unlockMutex( FALSE); + f_mutexDestroy( &m_hMutex); + } + } + + RCODE setupFileHdlMgr( + FLMUINT uiOpenThreshold = 64, + FLMUINT uiMaxAvailTime = 120); + + FINLINE void setOpenThreshold( + FLMUINT uiOpenThreshold) + { + if (m_bIsSetup) + { + lockMutex( FALSE); + m_uiOpenThreshold = uiOpenThreshold; + unlockMutex( FALSE); + } + } + + FINLINE void setMaxAvailTime( + FLMUINT uiMaxAvailTime) + { + if (m_bIsSetup) + { + lockMutex( FALSE); + m_uiMaxAvailTime = uiMaxAvailTime; + unlockMutex( FALSE); + } + } + + FINLINE FLMUINT getUniqueId( void) + { + FLMUINT uiTemp; + + lockMutex( FALSE); + uiTemp = ++m_uiFileIdCounter; + unlockMutex( FALSE); + return( uiTemp); + } + + void findAvail( + FLMUINT uiFileId, + FLMBOOL bReadOnlyFlag, + F_FileHdl ** ppFileHdl); + + void makeAvailAndRelease( + FLMBOOL bMutexAlreadyLocked, + F_FileHdl * pFileHdl); + + void removeFileHdls( + FLMUINT uiFileId); + + void checkAgedFileHdls( + FLMUINT uiMinSecondsOpened); + + void FINLINE releaseOneAvail( + FLMBOOL bMutexAlreadyLocked) + { + lockMutex( bMutexAlreadyLocked); + if (m_pFirstAvail) + { + removeFromList( TRUE, + m_pFirstAvail, &m_pFirstAvail, &m_pLastAvail, &m_uiNumAvail); + } + unlockMutex( bMutexAlreadyLocked); + } + + FINLINE FLMUINT getOpenThreshold( void) + { + return m_uiOpenThreshold; + } + + FINLINE FLMUINT getOpenedFiles( void) + { + FLMUINT uiTemp; + + lockMutex( FALSE); + uiTemp = m_uiNumUsed + m_uiNumAvail; + unlockMutex( FALSE); + return( uiTemp); + } + + FINLINE FLMUINT getMaxAvailTime( void) + { + return m_uiMaxAvailTime; + } + + void freeAvailList( + FLMBOOL bMutexAlreadyLocked); + + void freeUsedList( + FLMBOOL bMutexAlreadyLocked); + + FINLINE void insertInUsedList( + FLMBOOL bMutexAlreadyLocked, + F_FileHdl * pFileHdl, + FLMBOOL bInsertAtEnd) + { + insertInList( bMutexAlreadyLocked, + pFileHdl, bInsertAtEnd, + &m_pFirstUsed, &m_pLastUsed, &m_uiNumUsed); + } + + private: + + void insertInList( + FLMBOOL bMutexAlreadyLocked, + F_FileHdl * pFileHdl, + FLMBOOL bInsertAtEnd, + F_FileHdl ** ppFirst, + F_FileHdl ** ppLast, + FLMUINT * puiCount); + + void removeFromList( + FLMBOOL bMutexAlreadyLocked, + F_FileHdl * pFileHdl, + F_FileHdl ** ppFirst, + F_FileHdl ** ppLast, + FLMUINT * puiCount); + + FINLINE void lockMutex( + FLMBOOL bMutexAlreadyLocked) + { + if (m_hMutex != F_MUTEX_NULL && !bMutexAlreadyLocked) + { + f_mutexLock( m_hMutex); + } + } + + FINLINE void unlockMutex( + FLMBOOL bMutexAlreadyLocked) + { + if (m_hMutex != F_MUTEX_NULL && !bMutexAlreadyLocked) + { + f_mutexUnlock( m_hMutex); + } + } + + F_MUTEX m_hMutex; + FLMUINT m_uiOpenThreshold; + FLMUINT m_uiMaxAvailTime; + F_FileHdl * m_pFirstUsed; + F_FileHdl * m_pLastUsed; + FLMUINT m_uiNumUsed; + F_FileHdl * m_pFirstAvail; + F_FileHdl * m_pLastAvail; + FLMUINT m_uiNumAvail; + FLMBOOL m_bIsSetup; + FLMUINT m_uiFileIdCounter; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_MultiFileHdl : public IF_MultiFileHdl, public F_Base + { + public: + + F_MultiFileHdl( + FLMUINT uiMaxFileSize = F_MULTI_FHDL_DEFAULT_MAX_FILE_SIZE); + + virtual ~F_MultiFileHdl(); + + void FLMAPI close( + FLMBOOL bDelete = FALSE); + + + RCODE FLMAPI create( + const char * pszPath); + + RCODE FLMAPI createUnique( + const char * pszPath, + const char * pszFileExtension); + + RCODE FLMAPI deleteMultiFile( + const char * pszPath); + + RCODE FLMAPI open( + const char * pszPath); + + RCODE FLMAPI flush( void); + + RCODE FLMAPI read( + FLMUINT64 ui64Offset, + FLMUINT uiLength, + void * pvBuffer, + FLMUINT * puiBytesRead); + + RCODE FLMAPI write( + FLMUINT64 ui64Offset, + FLMUINT uiLength, + void * pvBuffer, + FLMUINT * puiBytesWritten); + + RCODE FLMAPI getPath( + char * pszFilePath); + + FINLINE RCODE FLMAPI size( + FLMUINT64 * pui64FileSize) + { + *pui64FileSize = m_ui64EOF; + return( NE_FLM_OK); + } + + RCODE FLMAPI truncate( + FLMUINT64 ui64NewSize); + + private: + + RCODE getFileHdl( + FLMUINT uiFileNum, + FLMBOOL bGetForWrite, + IF_FileHdl ** ppFileHdl); + + RCODE createLockFile( + const char * pszBasePath); + + FINLINE void releaseLockFile( + const char * pszBasePath, + FLMBOOL bDelete) + { + #ifndef FLM_UNIX + F_UNREFERENCED_PARM( bDelete); + F_UNREFERENCED_PARM( pszBasePath); + #endif + + if( m_pLockFileHdl) + { + + // Release the lock file + + (void)m_pLockFileHdl->close(); + m_pLockFileHdl->Release(); + m_pLockFileHdl = NULL; + + #ifdef FLM_UNIX + if( bDelete) + { + char szTmpPath[ F_PATH_MAX_SIZE]; + + // Delete the lock file + + f_strcpy( szTmpPath, pszBasePath); + gv_pFileSystem->pathAppend( szTmpPath, "64.LCK"); + gv_pFileSystem->Delete( szTmpPath); + } + #endif + } + } + + FINLINE void formatFileNum( + FLMUINT uiFileNum, + char * pszStr) + { + f_sprintf( pszStr, "%08X.64", (unsigned)uiFileNum); + } + + RCODE getFileNum( + const char * pszFileName, + FLMUINT * puiFileNum); + + FINLINE void dataFilePath( + FLMUINT uiFileNum, + char * pszPath) + { + char szFileName[ 13]; + + f_strcpy( pszPath, m_szPath); + formatFileNum( uiFileNum, szFileName); + gv_pFileSystem->pathAppend( pszPath, szFileName); + } + + FINLINE FLMUINT getFileNum( + FLMUINT64 ui64Offset) + { + return( (FLMUINT)(ui64Offset / m_uiMaxFileSize)); + } + + FINLINE FLMUINT getFileOffset( + FLMUINT64 ui64Offset) + { + return( (FLMUINT)(ui64Offset % m_uiMaxFileSize)); + } + + FH_INFO m_pFileHdlList[ F_MULTI_FHDL_LIST_SIZE]; + char m_szPath[ F_PATH_MAX_SIZE]; + FLMBOOL m_bOpen; + FLMUINT64 m_ui64EOF; + FLMUINT m_uiMaxFileSize; + IF_FileHdl * m_pLockFileHdl; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_ThreadMgr : public IF_ThreadMgr, public F_Base + { + public: + + F_ThreadMgr() + { + m_hMutex = F_MUTEX_NULL; + m_pThreadList = NULL; + m_uiNumThreads = 0; + } + + virtual ~F_ThreadMgr(); + + RCODE FLMAPI setupThreadMgr( void); + + void FLMAPI shutdownThreadGroup( + FLMUINT uiThreadGroup); + + void FLMAPI setThreadShutdownFlag( + FLMUINT uiThreadId); + + RCODE FLMAPI findThread( + IF_Thread ** ppThread, + FLMUINT uiThreadGroup, + FLMUINT uiAppId = 0, + FLMBOOL bOkToFindMe = TRUE); + + RCODE FLMAPI getNextGroupThread( + IF_Thread ** ppThread, + FLMUINT uiThreadGroup, + FLMUINT * puiThreadId); + + RCODE FLMAPI getThreadInfo( + IF_Pool * pPool, + F_THREAD_INFO ** ppThreadInfo, + FLMUINT * puiNumThreads); + + FLMUINT FLMAPI getThreadGroupCount( + FLMUINT uiThreadGroup); + + inline void lockMutex( void) + { + f_mutexLock( m_hMutex); + } + + inline void unlockMutex( void) + { + f_mutexUnlock( m_hMutex); + } + + void unlinkThread( + IF_Thread * pThread, + FLMBOOL bMutexLocked); + + private: + + F_MUTEX m_hMutex; + F_Thread * m_pThreadList; + FLMUINT m_uiNumThreads; + + friend class F_Thread; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_Thread : public IF_Thread, public F_Base + { + public: + + F_Thread() + { + m_hMutex = F_MUTEX_NULL; + m_pszThreadName = NULL; + m_pszThreadStatus = NULL; + m_uiStatusBufLen = 0; + m_pPrev = NULL; + m_pNext = NULL; + cleanupThread(); + } + + virtual ~F_Thread() + { + stopThread(); + cleanupThread(); + } + + FLMINT FLMAPI AddRef( void); + + FLMINT FLMAPI Release( void); + + RCODE FLMAPI startThread( + F_THREAD_FUNC fnThread, + const char * pszThreadName = NULL, + FLMUINT uiThreadGroup = 0, + FLMUINT uiAppId = 0, + void * pvParm1 = NULL, + void * pvParm2 = NULL, + FLMUINT uiStackSize = F_THREAD_DEFAULT_STACK_SIZE); + + void FLMAPI stopThread( void); + + FINLINE FLMUINT FLMAPI getThreadId( void) + { + return( m_uiThreadId); + } + + FINLINE FLMBOOL FLMAPI getShutdownFlag( void) + { + return( m_bShutdown); + } + + FINLINE RCODE FLMAPI getExitCode( void) + { + return( m_exitRc); + } + + FINLINE void * FLMAPI getParm1( void) + { + return( m_pvParm1); + } + + FINLINE void FLMAPI setParm1( + void * pvParm) + { + m_pvParm1 = pvParm; + } + + FINLINE void * FLMAPI getParm2( void) + { + return( m_pvParm2); + } + + FINLINE void FLMAPI setParm2( + void * pvParm) + { + m_pvParm2 = pvParm; + } + + FINLINE void FLMAPI setShutdownFlag( void) + { + m_bShutdown = TRUE; + } + + FINLINE FLMBOOL FLMAPI isThreadRunning( void) + { + return( m_bRunning); + } + + void FLMAPI setThreadStatusStr( + const char * pszStatus); + + void FLMAPI setThreadStatus( + const char * pszBuffer, ...); + + void FLMAPI setThreadStatus( + eThreadStatus genericStatus); + + FINLINE void FLMAPI setThreadAppId( + FLMUINT uiAppId) + { + f_mutexLock( m_hMutex); + m_uiAppId = uiAppId; + f_mutexUnlock( m_hMutex); + } + + FINLINE FLMUINT FLMAPI getThreadAppId( void) + { + return( m_uiAppId); + } + + FINLINE FLMUINT FLMAPI getThreadGroup( void) + { + return( m_uiThreadGroup); + } + + void FLMAPI cleanupThread( void); + + F_MUTEX m_hMutex; + F_Thread * m_pPrev; + F_Thread * m_pNext; + char * m_pszThreadName; + char * m_pszThreadStatus; + FLMUINT m_uiStatusBufLen; + FLMBOOL m_bShutdown; + F_THREAD_FUNC m_fnThread; + FLMBOOL m_bRunning; + FLMUINT m_uiStackSize; + void * m_pvParm1; + void * m_pvParm2; + FLMUINT m_uiThreadId; + FLMUINT m_uiThreadGroup; + FLMUINT m_uiAppId; + FLMUINT m_uiStartTime; + RCODE m_exitRc; + + friend class F_ThreadMgr; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_IniFile : public IF_IniFile, public F_Base + { + public: + + F_IniFile(); + + virtual ~F_IniFile(); + + RCODE FLMAPI init( void); + + RCODE FLMAPI read( + const char * pszFileName); + + RCODE FLMAPI write( void); + + FLMBOOL FLMAPI getParam( + const char * pszParamName, + FLMUINT * puiParamVal); + + FLMBOOL FLMAPI getParam( + const char * pszParamName, + FLMBOOL * pbParamVal); + + FLMBOOL FLMAPI getParam( + const char * pszParamName, + char ** ppszParamVal); + + RCODE FLMAPI setParam( + const char * pszParamName, + FLMUINT uiParamVal); + + RCODE FLMAPI setParam( + const char * pszParamName, + FLMBOOL bParamVal); + + RCODE FLMAPI setParam( + const char * pszParamName, + const char * pszParamVal); + + FINLINE FLMBOOL FLMAPI testParam( + const char * pszParamName) + { + if( findParam( pszParamName)) + { + return( TRUE); + } + + return( FALSE); + } + + private: + + RCODE readLine( + char * pucBuf, + FLMUINT * puiBytes, + FLMBOOL * pbMore); + + RCODE parseBuffer( + char * pucBuf, + FLMUINT uiNumButes); + + INI_LINE * findParam( + const char * pszParamName); + + RCODE setParamCommon( + INI_LINE ** ppLine, + const char * pszParamName); + + void fromAscii( + FLMUINT * puiVal, + const char * pszParamValue); + + void fromAscii( + FLMBOOL * pbVal, + const char * pszParamValue); + + RCODE toAscii( + char ** ppszParamValue, + FLMUINT puiVal); + + RCODE toAscii( + char ** ppszParamValue, + FLMBOOL pbVal); + + RCODE toAscii( + char ** ppszParamValue, + const char * pszVal); + + FINLINE FLMBOOL isWhiteSpace( + FLMBYTE ucChar) + { + return( ucChar == 32 || ucChar == 9 ? TRUE : FALSE); + } + + IF_Pool * m_pPool; + IF_FileHdl * m_pFileHdl; + char * m_pszFileName; + INI_LINE * m_pFirstLine; + INI_LINE * m_pLastLine; + FLMBOOL m_bReady; + FLMBOOL m_bModified; + FLMUINT m_uiFileOffset; + }; + + /**************************************************************************** + Desc: + *****************************************************************************/ + class F_DynaBuf : public IF_DynaBuf + { + public: + + F_DynaBuf( + FLMBYTE * pucBuffer, + FLMUINT uiBufferSize) + { + m_pucBuffer = pucBuffer; + m_uiBufferSize = uiBufferSize; + m_uiOffset = 0; + m_bAllocatedBuffer = FALSE; + } + + virtual ~F_DynaBuf() + { + if( m_bAllocatedBuffer) + { + f_free( &m_pucBuffer); + } + } + + FINLINE void FLMAPI truncateData( + FLMUINT uiSize) + { + if( uiSize < m_uiOffset) + { + m_uiOffset = uiSize; + } + } + + FINLINE RCODE FLMAPI allocSpace( + FLMUINT uiSize, + void ** ppvPtr) + { + RCODE rc = NE_FLM_OK; + + if( m_uiOffset + uiSize >= m_uiBufferSize) + { + if( RC_BAD( rc = resizeBuffer( m_uiOffset + uiSize + 512))) + { + goto Exit; + } + } + + *ppvPtr = &m_pucBuffer[ m_uiOffset]; + m_uiOffset += uiSize; + + Exit: + + return( rc); + } + + FINLINE RCODE FLMAPI appendData( + const void * pvData, + FLMUINT uiSize) + { + RCODE rc = NE_FLM_OK; + void * pvTmp; + + if( RC_BAD( rc = allocSpace( uiSize, &pvTmp))) + { + goto Exit; + } + + if( uiSize == 1) + { + *((FLMBYTE *)pvTmp) = *((FLMBYTE *)pvData); + } + else + { + f_memcpy( pvTmp, pvData, uiSize); + } + + Exit: + + return( rc); + } + + FINLINE RCODE FLMAPI appendByte( + FLMBYTE ucChar) + { + RCODE rc = NE_FLM_OK; + FLMBYTE * pucTmp; + + if( RC_BAD( rc = allocSpace( 1, (void **)&pucTmp))) + { + goto Exit; + } + + *pucTmp = ucChar; + + Exit: + + return( rc); + } + + FINLINE RCODE FLMAPI appendUniChar( + FLMUNICODE uChar) + { + RCODE rc = NE_FLM_OK; + FLMUNICODE * puTmp; + + if( RC_BAD( rc = allocSpace( sizeof( FLMUNICODE), (void **)&puTmp))) + { + goto Exit; + } + + *puTmp = uChar; + + Exit: + + return( rc); + } + + FINLINE FLMBYTE * FLMAPI getBufferPtr( void) + { + return( m_pucBuffer); + } + + FINLINE FLMUNICODE * FLMAPI getUnicodePtr( void) + { + if( m_uiOffset >= sizeof( FLMUNICODE)) + { + return( (FLMUNICODE *)m_pucBuffer); + } + + return( NULL); + } + + FINLINE FLMUINT FLMAPI getUnicodeLength( void) + { + if( m_uiOffset <= sizeof( FLMUNICODE)) + { + return( 0); + } + + return( (m_uiOffset >> 1) - 1); + } + + FINLINE FLMUINT FLMAPI getDataLength( void) + { + return( m_uiOffset); + } + + FINLINE RCODE FLMAPI copyFromBuffer( + IF_DynaBuf * pSource) + { + RCODE rc = NE_FLM_OK; + + if( RC_BAD( rc = resizeBuffer( + ((F_DynaBuf *)pSource)->m_uiBufferSize))) + { + goto Exit; + } + + if( (m_uiOffset = ((F_DynaBuf *)pSource)->m_uiOffset) != 0) + { + f_memcpy( m_pucBuffer, ((F_DynaBuf *)pSource)->m_pucBuffer, + ((F_DynaBuf *)pSource)->m_uiOffset); + } + + Exit: + + return( rc); + } + + private: + + FINLINE RCODE resizeBuffer( + FLMUINT uiNewSize) + { + RCODE rc = NE_FLM_OK; + + if( !m_bAllocatedBuffer) + { + if( uiNewSize > m_uiBufferSize) + { + FLMBYTE * pucOriginalBuf = m_pucBuffer; + + if( RC_BAD( rc = f_alloc( uiNewSize, &m_pucBuffer))) + { + m_pucBuffer = pucOriginalBuf; + goto Exit; + } + + m_bAllocatedBuffer = TRUE; + + if( m_uiOffset) + { + f_memcpy( m_pucBuffer, pucOriginalBuf, m_uiOffset); + } + } + } + else + { + if( RC_BAD( rc = f_realloc( uiNewSize, &m_pucBuffer))) + { + goto Exit; + } + + if( uiNewSize < m_uiOffset) + { + m_uiOffset = uiNewSize; + } + } + + m_uiBufferSize = uiNewSize; + + Exit: + + return( rc); + } + + FLMBOOL m_bAllocatedBuffer; + FLMBYTE * m_pucBuffer; + FLMUINT m_uiBufferSize; + FLMUINT m_uiOffset; + }; + + typedef struct + { + FLMUINT32 ui32Offset; + FLMUINT32 ui32Length; + } F_VAR_HEADER; + + typedef struct + { + FLMUINT64 ui64FilePos; + FLMUINT uiEntryCount; + FLMUINT uiBlockSize; + FLMBOOL bFirstBlock; + FLMBOOL bLastBlock; + } F_BLOCK_HEADER; + + #define RSBLK_UNSET_FILE_POS (~((FLMUINT64)0)) + + /**************************************************************************** + Desc: Result set block + ****************************************************************************/ + class F_ResultSetBlk : public F_RefCount, public F_Base + { + public: + + F_ResultSetBlk(); + + FINLINE ~F_ResultSetBlk() + { + if (m_pNext) + { + m_pNext->m_pPrev = m_pPrev; + } + + if( m_pPrev) + { + m_pPrev->m_pNext = m_pNext; + } + + if (m_pCompare) + { + m_pCompare->Release(); + } + } + + void reset( void); + + void setup( + IF_MultiFileHdl ** ppMultiFileHdl, + IF_ResultSetCompare * pCompare, + FLMUINT uiEntrySize, + FLMBOOL bFirstInList, + FLMBOOL bDropDuplicates, + FLMBOOL bEntriesInOrder); + + RCODE setBuffer( + FLMBYTE * pBuffer, + FLMUINT uiBufferSize = RS_BLOCK_SIZE); + + FINLINE FLMUINT bytesUsedInBuffer( void) + { + if (m_bEntriesInOrder) + { + return( m_BlockHeader.uiBlockSize); + } + else + { + return( m_BlockHeader.uiBlockSize - m_uiLengthRemaining); + } + } + + RCODE addEntry( + FLMBYTE * pEntry, + FLMUINT uiEntryLength ); + + RCODE modifyEntry( + FLMBYTE * pEntry, + FLMUINT uiEntryLength = 0); + + FINLINE RCODE finalize( + FLMBOOL bForceWrite) + { + return( flush( TRUE, bForceWrite)); + } + + RCODE flush( + FLMBOOL bLastBlockInList, + FLMBOOL bForceWrite); + + RCODE getCurrent( + FLMBYTE * pBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength); + + FINLINE RCODE getNext( + FLMBYTE * pucBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength) + { + // Are we on the last entry or past the last entry? + + if (m_iEntryPos + 1 >= (FLMINT)m_BlockHeader.uiEntryCount) + { + m_iEntryPos = (FLMINT) m_BlockHeader.uiEntryCount; + return RC_SET( NE_FLM_EOF_HIT); + } + + m_iEntryPos++; + return( copyCurrentEntry( pucBuffer, uiBufferLength, puiReturnLength)); + } + + RCODE getNextPtr( + FLMBYTE ** ppBuffer, + FLMUINT * puiReturnLength); + + RCODE getPrev( + FLMBYTE * pBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength); + + FINLINE FLMUINT64 getPosition( void) + { + return( (!m_bPositioned || + m_iEntryPos == -1 || + m_iEntryPos == (FLMINT)m_BlockHeader.uiEntryCount + ? RS_POSITION_NOT_SET + : m_ui64BlkEntryPosition + (FLMUINT64)m_iEntryPos)); + } + + RCODE setPosition( + FLMUINT64 ui64Position ); + + RCODE findMatch( + FLMBYTE * pMatchEntry, + FLMUINT uiMatchEntryLength, + FLMBYTE * pFoundEntry, + FLMUINT * puiFoundEntryLength, + FLMINT * piCompare); + + void adjustState( + FLMUINT uiBlkBufferSize); + + RCODE truncate( + FLMBYTE * pszPath); + + private: + + RCODE addEntry( + FLMBYTE * pucEntry); + + void squeezeSpace( void); + + RCODE sortAndRemoveDups( void); + + void removeEntry( + FLMBYTE * pucEntry); + + RCODE quickSort( + FLMUINT uiLowerBounds, + FLMUINT uiUpperBounds); + + FINLINE RCODE entryCompare( + FLMBYTE * pucLeftEntry, + FLMBYTE * pucRightEntry, + FLMINT * piCompare) + { + RCODE rc; + + if( m_bFixedEntrySize) + { + rc = m_pCompare->compare( pucLeftEntry, m_uiEntrySize, + pucRightEntry, m_uiEntrySize, piCompare); + } + else + { + rc = m_pCompare->compare( + m_pucBlockBuf + ((F_VAR_HEADER *)pucLeftEntry)->ui32Offset, + ((F_VAR_HEADER *)pucLeftEntry)->ui32Length, + m_pucBlockBuf + ((F_VAR_HEADER *)pucRightEntry)->ui32Offset, + ((F_VAR_HEADER *)pucRightEntry)->ui32Length, + piCompare); + } + if (*piCompare == 0) + { + m_bDuplicateFound = TRUE; + } + + return( rc); + } + + RCODE copyCurrentEntry( + FLMBYTE * pBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength); + + RCODE compareEntry( + FLMBYTE * pMatchEntry, + FLMUINT uiMatchEntryLength, + FLMUINT uiEntryPos, + FLMINT * piCompare); + + RCODE write(); + + RCODE read(); + + F_BLOCK_HEADER m_BlockHeader; + IF_ResultSetCompare * m_pCompare; + FLMBYTE * m_pucBlockBuf; + FLMBYTE * m_pucEndPoint; + F_ResultSetBlk * m_pNext; + F_ResultSetBlk * m_pPrev; + IF_MultiFileHdl ** m_ppMultiFileHdl; + FLMUINT64 m_ui64BlkEntryPosition; + FLMUINT m_uiLengthRemaining; + FLMINT m_iEntryPos; + FLMUINT m_uiEntrySize; + FLMBOOL m_bEntriesInOrder; + FLMBOOL m_bFixedEntrySize; + FLMBOOL m_bPositioned; + FLMBOOL m_bModifiedEntry; + FLMBOOL m_bDuplicateFound; + FLMBOOL m_bDropDuplicates; + + friend class F_ResultSet; + }; + + /***************************************************************************** + Desc: Result set + *****************************************************************************/ + class F_ResultSet : public IF_ResultSet, public F_Base + { + public: + + F_ResultSet(); + + F_ResultSet( + FLMUINT uiBlkSize); + + virtual ~F_ResultSet(); + + RCODE FLMAPI setupResultSet( + const char * pszPath, + IF_ResultSetCompare * pCompare, + FLMUINT uiEntrySize, + FLMBOOL bDropDuplicates = TRUE, + FLMBOOL bEntriesInOrder = FALSE, + const char * pszInputFileName = NULL); + + FINLINE void FLMAPI setSortStatus( + IF_ResultSetSortStatus * pSortStatus) + { + if (m_pSortStatus) + { + m_pSortStatus->Release(); + m_pSortStatus = NULL; + } + + if ((m_pSortStatus = pSortStatus) != NULL) + { + m_pSortStatus->AddRef(); + } + } + + FINLINE FLMUINT64 FLMAPI getTotalEntries( void) + { + F_ResultSetBlk * pBlk = m_pFirstRSBlk; + FLMUINT64 ui64TotalEntries = 0; + + for( pBlk = m_pFirstRSBlk; pBlk; pBlk = pBlk->m_pNext) + { + ui64TotalEntries += pBlk->m_BlockHeader.uiEntryCount; + } + + return( ui64TotalEntries); + } + + RCODE FLMAPI addEntry( + const void * pvEntry, + FLMUINT uiEntryLength = 0); + + RCODE FLMAPI finalizeResultSet( + FLMUINT64 * pui64TotalEntries = NULL); + + RCODE FLMAPI getFirst( + void * pvEntryBuffer, + FLMUINT uiBufferLength = 0, + FLMUINT * puiEntryLength = NULL); + + RCODE FLMAPI getNext( + void * pvEntryBuffer, + FLMUINT uiBufferLength = 0, + FLMUINT * puiEntryLength = NULL); + + RCODE FLMAPI getLast( + void * pvEntryBuffer, + FLMUINT uiBufferLength = 0, + FLMUINT * puiEntryLength = NULL); + + RCODE FLMAPI getPrev( + void * pvEntryBuffer, + FLMUINT uiBufferLength = 0, + FLMUINT * puiEntryLength = NULL); + + RCODE FLMAPI getCurrent( + void * pvEntryBuffer, + FLMUINT uiBufferLength = 0, + FLMUINT * puiEntryLength = NULL); + + FINLINE RCODE FLMAPI modifyCurrent( + const void * pvEntry, + FLMUINT uiEntryLength = 0) + { + return( m_pCurRSBlk->modifyEntry( (FLMBYTE *)pvEntry, uiEntryLength)); + } + + FINLINE RCODE FLMAPI findMatch( + const void * pvMatchEntry, + void * pvFoundEntry) + { + return( findMatch( pvMatchEntry, m_uiEntrySize, + pvFoundEntry, NULL)); + } + + RCODE FLMAPI findMatch( + const void * pvMatchEntry, + FLMUINT uiMatchEntryLength, + void * pvFoundEntry, + FLMUINT * puiFoundEntryLength); + + FINLINE FLMUINT64 FLMAPI getPosition( void) + { + return( (!m_pCurRSBlk + ? RS_POSITION_NOT_SET + : m_pCurRSBlk->getPosition())); + } + + RCODE FLMAPI setPosition( + FLMUINT64 ui64Position); + + RCODE FLMAPI resetResultSet( + FLMBOOL bDelete = TRUE); + + RCODE FLMAPI flushToFile( void); + + private: + + FINLINE FLMUINT64 numberOfBlockChains( void) + { + FLMUINT64 ui64Count = 0; + F_ResultSetBlk * pBlk = m_pFirstRSBlk; + + for (; pBlk ; pBlk = pBlk->m_pNext) + { + if (pBlk->m_BlockHeader.bFirstBlock) + { + ui64Count++; + } + } + + return( ui64Count); + } + + RCODE mergeSort(); + + RCODE getNextPtr( + F_ResultSetBlk ** ppCurBlk, + FLMBYTE * * ppBuffer, + FLMUINT * puiReturnLength); + + RCODE unionBlkLists( + F_ResultSetBlk * pLeftBlk, + F_ResultSetBlk * pRightBlk = NULL); + + RCODE copyRemainingItems( + F_ResultSetBlk * pCurBlk); + + void closeFile( + IF_MultiFileHdl ** ppMultiFileHdl, + FLMBOOL bDelete = TRUE); + + RCODE openFile( + IF_MultiFileHdl ** ppMultiFileHdl); + + F_ResultSetBlk * selectMidpoint( + F_ResultSetBlk * pLowBlk, + F_ResultSetBlk * pHighBlk, + FLMBOOL bPickHighIfNeighbors); + + RCODE setupFromFile( void); + + IF_ResultSetCompare * m_pCompare; + IF_ResultSetSortStatus * m_pSortStatus; + FLMUINT64 m_ui64EstTotalUnits; + FLMUINT64 m_ui64UnitsDone; + FLMUINT m_uiEntrySize; + FLMUINT64 m_ui64TotalEntries; + F_ResultSetBlk * m_pCurRSBlk; + F_ResultSetBlk * m_pFirstRSBlk; + F_ResultSetBlk * m_pLastRSBlk; + char m_szIoDefaultPath[ F_PATH_MAX_SIZE]; + char m_szIoFilePath1[ F_PATH_MAX_SIZE]; + char m_szIoFilePath2[ F_PATH_MAX_SIZE]; + IF_MultiFileHdl * m_pMultiFileHdl1; + IF_MultiFileHdl * m_pMultiFileHdl2; + FLMBYTE * m_pucBlockBuf1; + FLMBYTE * m_pucBlockBuf2; + FLMBYTE * m_pucBlockBuf3; + FLMUINT m_uiBlockBuf1Len; + FLMBOOL m_bFile1Opened; + FLMBOOL m_bFile2Opened; + FLMBOOL m_bOutput2ndFile; + FLMBOOL m_bInitialAdding; + FLMBOOL m_bFinalizeCalled; + FLMBOOL m_bSetupCalled; + FLMBOOL m_bDropDuplicates; + FLMBOOL m_bAppAddsInOrder; + FLMBOOL m_bEntriesInOrder; + FLMUINT m_uiBlkSize; + + friend class F_ResultSetBlk; + }; + + /**************************************************************************** + Desc: Logging + ****************************************************************************/ + + void flmDbgLogInit( void); + void flmDbgLogExit( void); + void flmDbgLogFlush( void); + + /**************************************************************************** + Desc: Timers + ****************************************************************************/ + #if defined( FLM_NLM) + + extern "C" void ConvertTicksToSeconds( + LONG ticks, + LONG * seconds, + LONG * tenthsOfSeconds); + + extern "C" void ConvertSecondsToTicks( + LONG seconds, + LONG tenthsOfSeconds, + LONG * ticks); + + #define FLM_GET_TIMER() \ + (FLMUINT)GetCurrentTime() + + #define FLM_SECS_TO_TIMER_UNITS( uiSeconds, uiTU) \ + ConvertSecondsToTicks( (LONG)(uiSeconds), 0, (LONG *)(&(uiTU))) + + #define FLM_TIMER_UNITS_TO_SECS( uiTU, uiSeconds) \ + { \ + LONG udDummy; \ + ConvertTicksToSeconds( (LONG)(uiTU), (LONG *)(&(uiSeconds)), &udDummy); \ + } + + #define FLM_TIMER_UNITS_TO_MILLI( uiTU, uiMilli) \ + { \ + LONG udTenths; \ + LONG udSeconds; \ + ConvertTicksToSeconds( (LONG)(uiTU), (LONG *)(&(udSeconds)), &udTenths); \ + uiMilli = (FLMUINT)(udSeconds) * 1000 + (FLMUINT)udTenths * 100; \ + } + + #define FLM_MILLI_TO_TIMER_UNITS( uiMilliSeconds, uiTU) \ + { \ + LONG udTenths, udSeconds; \ + udSeconds = ((LONG) uiMilliSeconds) / 1000; \ + udTenths = (((LONG) uiMilliSeconds) % 1000) / 100; \ + ConvertSecondsToTicks( udSeconds, udTenths, (LONG *)(&(uiTU))); \ + } + + #elif defined( FLM_UNIX) + + // gettimeofday() is actually 4 times faster than time() on + // Solaris. gethrtime() is even faster. On Linux time() is the + // fastest; gettimeofday() is 50% slower. clock() is the + // slowest on both Solaris and Linux. We use a new function for + // millisec resolution. The implementation is OS dependent. + + #define FLM_GET_TIMER() \ + (FLMUINT) f_timeGetMilliTime() + + #define FLM_SECS_TO_TIMER_UNITS( uiSeconds, uiTU) \ + ((uiTU) = ((uiSeconds) * 1000)) + + #define FLM_TIMER_UNITS_TO_SECS( uiTU, uiSeconds) \ + ((uiSeconds) = ((uiTU) / 1000)) + + #define FLM_TIMER_UNITS_TO_MILLI( uiTU, uiMilli) \ + ((uiMilli) = (uiTU)) + + #define FLM_MILLI_TO_TIMER_UNITS( uiMilli, uiTU) \ + ((uiTU) = (uiMilli)) + + #elif defined( FLM_WIN) + + #define FLM_GET_TIMER() \ + (FLMUINT)GetTickCount() + + #define FLM_SECS_TO_TIMER_UNITS( uiSeconds, uiTU) \ + ((uiTU) = (uiSeconds) * 1000) + + #define FLM_TIMER_UNITS_TO_SECS( uiTU, uiSeconds) \ + ((uiSeconds) = (uiTU) / 1000) + + #define FLM_TIMER_UNITS_TO_MILLI( uiTU, uiMilli) \ + (uiMilli = (uiTU)) + + #define FLM_MILLI_TO_TIMER_UNITS( uiMilliSeconds, uiTU) \ + (uiTU = (uiMilliSeconds)) + + #endif + + // This macro for calculating elapsed time accounts for the + // possibility of the time wrapping - which it will for some + // of our counters (FLM_WIN is milliseconds and wraps in 49.7 days). + + #define FLM_ELAPSED_TIME(uiLaterTime,uiEarlierTime) \ + (FLMUINT)(((uiLaterTime) >= (uiEarlierTime)) \ + ? (FLMUINT)((uiLaterTime) - (uiEarlierTime)) \ + : (FLMUINT)((0xFFFFFFFF - (uiEarlierTime)) + (uiLaterTime))) + + /**************************************************************************** + Desc: XML + ****************************************************************************/ + class F_XML : public IF_XML, public virtual F_Base + { + public: + + F_XML(); + + virtual ~F_XML(); + + RCODE FLMAPI setup( void); + + FLMBOOL FLMAPI isPubidChar( + FLMUNICODE uChar); + + FLMBOOL FLMAPI isQuoteChar( + FLMUNICODE uChar); + + FLMBOOL FLMAPI isWhitespace( + FLMUNICODE uChar); + + FLMBOOL FLMAPI isExtender( + FLMUNICODE uChar); + + FLMBOOL FLMAPI isCombiningChar( + FLMUNICODE uChar); + + FLMBOOL FLMAPI isNameChar( + FLMUNICODE uChar); + + FLMBOOL FLMAPI isNCNameChar( + FLMUNICODE uChar); + + FLMBOOL FLMAPI isIdeographic( + FLMUNICODE uChar); + + FLMBOOL FLMAPI isBaseChar( + FLMUNICODE uChar); + + FLMBOOL FLMAPI isDigit( + FLMUNICODE uChar); + + FLMBOOL FLMAPI isLetter( + FLMUNICODE uChar); + + FLMBOOL FLMAPI isNameValid( + FLMUNICODE * puzName, + FLMBYTE * pszName); + + private: + + void setCharFlag( + FLMUNICODE uLowChar, + FLMUNICODE uHighChar, + FLMUINT16 ui16Flag); + + XMLCHAR * m_pCharTable; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_XMLNamespace : public F_RefCount, public F_Base + { + public: + + FINLINE F_XMLNamespace() + { + m_puzPrefix = NULL; + m_puzURI = NULL; + m_pNext = NULL; + } + + virtual FINLINE ~F_XMLNamespace() + { + flmAssert( !m_pNext); + + if( m_puzPrefix) + { + f_free( &m_puzPrefix); + } + + if( m_puzURI) + { + f_free( &m_puzURI); + } + } + + RCODE setPrefix( + FLMUNICODE * puzPrefix); + + RCODE setURI( + FLMUNICODE * puzURI); + + RCODE setup( + FLMUNICODE * puzPrefix, + FLMUNICODE * puzURI, + F_XMLNamespace * pNext); + + FINLINE FLMUNICODE * getPrefixPtr( void) + { + return( m_puzPrefix); + } + + FINLINE FLMUNICODE * getURIPtr( void) + { + return( m_puzURI); + } + + private: + + FLMUNICODE * m_puzPrefix; + FLMUNICODE * m_puzURI; + F_XMLNamespace * m_pNext; + + friend class F_XMLNamespaceMgr; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_XMLNamespaceMgr : public F_RefCount, public virtual F_Base + { + public: + + F_XMLNamespaceMgr(); + + virtual ~F_XMLNamespaceMgr(); + + RCODE findNamespace( + FLMUNICODE * puzPrefix, + F_XMLNamespace ** ppNamespace, + FLMUINT uiMaxSearchSize = ~((FLMUINT)0)); + + RCODE pushNamespace( + FLMUNICODE * puzPrefix, + FLMUNICODE * puzNamespaceURI); + + RCODE pushNamespace( + F_XMLNamespace * pNamespace); + + void popNamespaces( + FLMUINT uiCount); + + FLMUINT getNamespaceCount( void) + { + return( m_uiNamespaceCount); + } + + private: + + F_XMLNamespace * m_pFirstNamespace; + FLMUINT m_uiNamespaceCount; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_XMLParser : public F_XML, public F_XMLNamespaceMgr + { + public: + + F_XMLParser(); + + ~F_XMLParser(); + + RCODE FLMAPI setup( void); + + void FLMAPI reset( void); + + RCODE FLMAPI import( + IF_IStream * pStream, + FLMUINT uiFlags, + IF_DOMNode * pNodeToLinkTo, + eNodeInsertLoc eInsertLoc, + IF_DOMNode ** ppNewNode, + FLM_IMPORT_STATS * pImportStats); + + FINLINE void FLMAPI setStatusCallback( + XML_STATUS_HOOK fnStatus, + void * pvUserData) + { + m_fnStatus = fnStatus; + m_pvCallbackData = pvUserData; + } + + private: + + #define F_DEFAULT_NS_DECL 0x01 + #define F_PREFIXED_NS_DECL 0x02 + + typedef struct xmlattr + { + FLMUINT uiLineNum; + FLMUINT uiLineOffset; + FLMUINT uiLineFilePos; + FLMUINT uiLineBytes; + FLMUINT uiValueLineNum; + FLMUINT uiValueLineOffset; + FLMUNICODE * puzPrefix; + FLMUNICODE * puzLocalName; + FLMUNICODE * puzVal; + FLMUINT uiFlags; + xmlattr * pPrev; + xmlattr * pNext; + } XML_ATTR; + + // Methods + + RCODE getFieldTagAndType( + FLMUNICODE * puzName, + FLMBOOL bOkToAdd, + FLMUINT * puiTagNum, + FLMUINT * puiDataType); + + RCODE getByte( + FLMBYTE * pucByte); + + FINLINE void ungetByte( + FLMBYTE ucByte) + { + // Can only unget a single byte. + + flmAssert( !m_ucUngetByte); + m_ucUngetByte = ucByte; + m_importStats.uiChars--; + } + + RCODE getLine( void); + + FINLINE FLMUNICODE getChar( void) + { + if (m_uiCurrLineOffset == m_uiCurrLineNumChars) + { + return( (FLMUNICODE)0); + } + else + { + return( m_puzCurrLineBuf [m_uiCurrLineOffset++]); + } + } + + FINLINE FLMUNICODE peekChar( void) + { + if (m_uiCurrLineOffset == m_uiCurrLineNumChars) + { + return( (FLMUNICODE)0); + } + else + { + return( m_puzCurrLineBuf [m_uiCurrLineOffset]); + } + } + + FINLINE void ungetChar( void) + { + flmAssert( m_uiCurrLineOffset); + m_uiCurrLineOffset--; + } + + RCODE getName( + FLMUINT * puiChars); + + RCODE getQualifiedName( + FLMUINT * puiChars, + FLMUNICODE ** ppuzPrefix, + FLMUNICODE ** ppuzLocal, + FLMBOOL * pbNamespaceDecl, + FLMBOOL * pbDefaultNamespaceDecl); + + void getNmtoken( + FLMUINT * puiChars); + + RCODE getPubidLiteral( void); + + RCODE getSystemLiteral( void); + + RCODE getElementValue( + FLMUNICODE * puBuf, + FLMUINT * puiMaxChars, + FLMBOOL * pbEntity); + + RCODE processEntityValue( void); + + RCODE getEntity( + FLMUNICODE * puBuf, + FLMUINT * puiChars, + FLMBOOL * pbTranslated, + FLMUNICODE * puTransChar); + + RCODE processReference( + FLMUNICODE * puChar = NULL); + + RCODE processCDATA( + IF_DOMNode * pParent, + FLMUINT uiSavedLineNum, + FLMUINT uiSavedOffset, + FLMUINT uiSavedFilePos, + FLMUINT uiSavedLineBytes); + + RCODE processAttributeList( void); + + RCODE processComment( + IF_DOMNode * pParent, + FLMUINT uiSavedLineNum, + FLMUINT uiSavedOffset, + FLMUINT uiSavedFilePos, + FLMUINT uiSavedLineBytes); + + RCODE processProlog( void); + + RCODE processXMLDecl( void); + + RCODE processVersion( void); + + RCODE processEncodingDecl( void); + + RCODE processSDDecl( void); + + RCODE processMisc( void); + + RCODE processDocTypeDecl( void); + + RCODE processPI( + IF_DOMNode * pParent, + FLMUINT uiSavedLineNum, + FLMUINT uiSavedOffset, + FLMUINT uiSavedFilePos, + FLMUINT uiSavedLineBytes); + + RCODE processElement( + IF_DOMNode * pNodeToLinkTo, + eNodeInsertLoc eInsertLoc, + IF_DOMNode ** ppNewNode); + + RCODE unicodeToNumber64( + FLMUNICODE * puzVal, + FLMUINT64 * pui64Val, + FLMBOOL * pbNeg); + + RCODE flushElementValue( + IF_DOMNode * pParent, + FLMBYTE * pucValue, + FLMUINT uiValueLen); + + RCODE getBinaryVal( + FLMUINT * puiLength); + + RCODE fixNamingTag( + IF_DOMNode * pNode); + + FLMBOOL lineHasToken( + const char * pszToken); + + RCODE processMarkupDecl( void); + + RCODE processPERef( void); + + RCODE processElementDecl( void); + + RCODE processEntityDecl( void); + + RCODE processNotationDecl( void); + + RCODE processAttListDecl( void); + + RCODE processContentSpec( void); + + RCODE processMixedContent( void); + + RCODE processChildContent( void); + + RCODE processAttDef( void); + + RCODE processAttType( void); + + RCODE processAttValue( + XML_ATTR * pAttr); + + RCODE processDefaultDecl( void); + + RCODE processID( + FLMBOOL bPublicId); + + RCODE processSTag( + IF_DOMNode * pNodeToLinkTo, + eNodeInsertLoc eInsertLoc, + FLMBOOL * pbHasContent, + IF_DOMNode ** ppElement); + + RCODE skipWhitespace( + FLMBOOL bRequired); + + RCODE resizeValBuffer( + FLMUINT uiSize); + + // Attribute management + + void resetAttrList( void) + { + m_pFirstAttr = NULL; + m_pLastAttr = NULL; + m_attrPool.poolReset( NULL); + } + + RCODE allocAttribute( + XML_ATTR ** ppAttr) + { + XML_ATTR * pAttr = NULL; + RCODE rc = NE_FLM_OK; + + if( RC_BAD( rc = m_attrPool.poolCalloc( + sizeof( XML_ATTR), (void **)&pAttr))) + { + goto Exit; + } + + if( (pAttr->pPrev = m_pLastAttr) == NULL) + { + m_pFirstAttr = pAttr; + } + else + { + m_pLastAttr->pNext = pAttr; + } + + m_pLastAttr = pAttr; + + Exit: + + *ppAttr = pAttr; + return( rc); + } + + RCODE setPrefix( + XML_ATTR * pAttr, + FLMUNICODE * puzPrefix) + { + RCODE rc = NE_FLM_OK; + FLMUINT uiStrLen; + + if( !puzPrefix) + { + pAttr->puzPrefix = NULL; + goto Exit; + } + + uiStrLen = f_unilen( puzPrefix); + + if( RC_BAD( rc = m_attrPool.poolAlloc( + sizeof( FLMUNICODE) * (uiStrLen + 1), (void **)&pAttr->puzPrefix))) + { + goto Exit; + } + + f_memcpy( pAttr->puzPrefix, puzPrefix, + sizeof( FLMUNICODE) * (uiStrLen + 1)); + + Exit: + + return( rc); + } + + RCODE setLocalName( + XML_ATTR * pAttr, + FLMUNICODE * puzLocalName) + { + RCODE rc = NE_FLM_OK; + FLMUINT uiStrLen; + + if( !puzLocalName) + { + pAttr->puzLocalName = NULL; + goto Exit; + } + + uiStrLen = f_unilen( puzLocalName); + + if( RC_BAD( rc = m_attrPool.poolAlloc( + sizeof( FLMUNICODE) * (uiStrLen + 1), + (void **)&pAttr->puzLocalName))) + { + goto Exit; + } + + f_memcpy( pAttr->puzLocalName, puzLocalName, + sizeof( FLMUNICODE) * (uiStrLen + 1)); + + Exit: + + return( rc); + } + + RCODE setUnicode( + XML_ATTR * pAttr, + FLMUNICODE * puzUnicode) + { + RCODE rc = NE_FLM_OK; + FLMUINT uiStrLen; + + if( !puzUnicode) + { + pAttr->puzVal = NULL; + goto Exit; + } + + uiStrLen = f_unilen( puzUnicode); + + if( RC_BAD( rc = m_attrPool.poolAlloc( + sizeof( FLMUNICODE) * (uiStrLen + 1), + (void **)&pAttr->puzVal))) + { + goto Exit; + } + + f_memcpy( pAttr->puzVal, puzUnicode, + sizeof( FLMUNICODE) * (uiStrLen + 1)); + + Exit: + + return( rc); + } + + RCODE addAttributesToElement( + IF_DOMNode * pElement); + + FINLINE void setErrInfo( + FLMUINT uiErrLineNum, + FLMUINT uiErrLineOffset, + XMLParseError eErrorType, + FLMUINT uiErrLineFilePos, + FLMUINT uiErrLineBytes) + { + m_importStats.uiErrLineNum = uiErrLineNum; + m_importStats.uiErrLineOffset = uiErrLineOffset; + m_importStats.eErrorType = eErrorType; + m_importStats.uiErrLineFilePos = uiErrLineFilePos; + m_importStats.uiErrLineBytes = uiErrLineBytes; + } + + FLMBYTE m_ucUngetByte; + FLMUNICODE * m_puzCurrLineBuf; + FLMUINT m_uiCurrLineBufMaxChars; + FLMUINT m_uiCurrLineNumChars; + FLMUINT m_uiCurrLineOffset; + FLMUINT m_uiCurrLineNum; + FLMUINT m_uiCurrLineFilePos; + FLMUINT m_uiCurrLineBytes; + #define FLM_XML_MAX_CHARS 128 + FLMUNICODE m_uChars[ FLM_XML_MAX_CHARS]; + FLMBOOL m_bSetup; + IF_IStream * m_pStream; + FLMBYTE * m_pucValBuf; + FLMUINT m_uiValBufSize; // Number of Unicode characters + FLMUINT m_uiFlags; + FLMBOOL m_bExtendDictionary; + XMLEncoding m_eXMLEncoding; + XML_STATUS_HOOK m_fnStatus; + void * m_pvCallbackData; + FLM_IMPORT_STATS m_importStats; + F_Pool m_tmpPool; + XML_ATTR * m_pFirstAttr; + XML_ATTR * m_pLastAttr; + F_Pool m_attrPool; + }; + + #define FLM_XML_EXTEND_DICT_FLAG 0x00000001 + #define FLM_XML_COMPRESS_WHITESPACE_FLAG 0x00000002 + #define FLM_XML_TRANSLATE_ESC_FLAG 0x00000004 + + FINLINE FLMBOOL isXMLNS( + FLMUNICODE * puzName) + { + return( (puzName [0] == FLM_UNICODE_x || puzName [0] == FLM_UNICODE_X) && + (puzName [1] == FLM_UNICODE_m || puzName [1] == FLM_UNICODE_M) && + (puzName [2] == FLM_UNICODE_l || puzName [2] == FLM_UNICODE_L) && + (puzName [3] == FLM_UNICODE_n || puzName [3] == FLM_UNICODE_N) && + (puzName [4] == FLM_UNICODE_s || puzName [4] == FLM_UNICODE_S) + ? TRUE + : FALSE); + } + + /**************************************************************************** + Stuff for F_NameTable class + ****************************************************************************/ + + typedef struct FlmTagInfoTag + { + FLMUINT uiType; + FLMUNICODE * puzTagName; + FLMUINT uiTagNum; + FLMUINT uiDataType; + FLMUNICODE * puzNamespace; + } FLM_TAG_INFO; + + /**************************************************************************** + Desc: Class for name/number lookup. + ****************************************************************************/ + class F_NameTable : public IF_NameTable, public F_Base + { + public: + + F_NameTable(); + + virtual ~F_NameTable(); + + RCODE FLMAPI setupNameTable( void); + + void FLMAPI clearTable( + FLMUINT uiPoolBlkSize); + + RCODE FLMAPI getNextTagTypeAndNumOrder( + FLMUINT uiType, + FLMUINT * puiNextPos, + FLMUNICODE * puzTagName = NULL, + char * pszTagName = NULL, + FLMUINT uiNameBufSize = 0, + FLMUINT * puiTagNum = NULL, + FLMUINT * puiDataType = NULL, + FLMUNICODE * puzNamespace = NULL, + FLMUINT uiNamespaceBufSize = 0, + FLMBOOL bTruncatedNamesOk = TRUE); + + RCODE FLMAPI getNextTagTypeAndNameOrder( + FLMUINT uiType, + FLMUINT * puiNextPos, + FLMUNICODE * puzTagName = NULL, + char * pszTagName = NULL, + FLMUINT uiNameBufSize = 0, + FLMUINT * puiTagNum = NULL, + FLMUINT * puiDataType = NULL, + FLMUNICODE * puzNamespace = NULL, + FLMUINT uiNamespaceBufSize = 0, + FLMBOOL bTruncatedNamesOk = TRUE); + + RCODE FLMAPI getFromTagTypeAndName( + FLMUINT uiType, + const FLMUNICODE * puzTagName, + const char * pszTagName, + FLMBOOL bMatchNamespace, + const FLMUNICODE * puzNamespace = NULL, + FLMUINT * puiTagNum = NULL, + FLMUINT * puiDataType = NULL); + + RCODE FLMAPI getFromTagTypeAndNum( + FLMUINT uiType, + FLMUINT uiTagNum, + FLMUNICODE * puzTagName = NULL, + char * pszTagName = NULL, + FLMUINT * puiNameBufSize = NULL, + FLMUINT * puiDataType = NULL, + FLMUNICODE * puzNamespace = NULL, + char * pszNamespace = NULL, + FLMUINT * puiNamespaceBufSize = NULL, + FLMBOOL bTruncatedNamesOk = TRUE); + + RCODE FLMAPI addTag( + FLMUINT uiType, + FLMUNICODE * puzTagName, + const char * pszTagName, + FLMUINT uiTagNum, + FLMUINT uiDataType = 0, + FLMUNICODE * puzNamespace = NULL, + FLMBOOL bCheckDuplicates = TRUE); + + void FLMAPI removeTag( + FLMUINT uiType, + FLMUINT uiTagNum); + + RCODE FLMAPI cloneNameTable( + IF_NameTable * pSrcNameTable); + + RCODE FLMAPI importFromNameTable( + IF_NameTable * pSrcNameTable); + + FLMINT FLMAPI AddRef( void); + + FLMINT FLMAPI Release( void); + + private: + + void sortTags( void); + + RCODE allocTag( + FLMUINT uiType, + FLMUNICODE * puzTagName, + const char * pszTagName, + FLMUINT uiTagNum, + FLMUINT uiDataType, + FLMUNICODE * puzNamespace, + FLM_TAG_INFO ** ppTagInfo); + + RCODE reallocSortTables( + FLMUINT uiNewTblSize); + + RCODE copyTagName( + FLMUNICODE * puzDestTagName, + char * pszDestTagName, + FLMUINT * puiDestBufSize, + FLMUNICODE * puzSrcTagName, + FLMBOOL bTruncatedNamesOk); + + FLM_TAG_INFO * findTagByTypeAndNum( + FLMUINT uiType, + FLMUINT uiTagNum, + FLMUINT * puiInsertPos = NULL); + + FLM_TAG_INFO * findTagByTypeAndName( + FLMUINT uiType, + const FLMUNICODE * puzTagName, + const char * pszTagName, + FLMBOOL bMatchNamespace, + const FLMUNICODE * puzNamespace, + FLMBOOL * pbAmbiguous, + FLMUINT * puiInsertPos = NULL); + + RCODE insertTagInTables( + FLM_TAG_INFO * pTagInfo, + FLMUINT uiTagTypeAndNameTblInsertPos, + FLMUINT uiTagTypeAndNumTblInsertPos); + + FLMUNICODE * findNamespace( + FLMUNICODE * puzNamespace, + FLMUINT * puiInsertPos); + + RCODE insertNamespace( + FLMUNICODE * puzNamespace, + FLMUINT uiInsertPos); + + F_Pool m_pool; + FLMUINT m_uiMemoryAllocated; + FLM_TAG_INFO ** m_ppSortedByTagTypeAndName; + FLM_TAG_INFO ** m_ppSortedByTagTypeAndNum; + FLMUINT m_uiTblSize; + FLMUINT m_uiNumTags; + FLMBOOL m_bTablesSorted; + FLMUNICODE ** m_ppuzNamespaces; + FLMUINT m_uiNamespaceTblSize; + FLMUINT m_uiNumNamespaces; + F_MUTEX m_hRefMutex; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_Btree : public F_RefCount, public F_Base + { + public: + + F_Btree( void); + + virtual ~F_Btree( void); + + RCODE btCreate( + IF_BlockMgr * pBlockMgr, + FLMUINT16 ui16BtreeId, + FLMBOOL bCounts, + FLMBOOL bData, + FLMUINT * puiRootBlkAddr); + + + RCODE btOpen( + IF_BlockMgr * pBlockMgr, + FLMUINT uiRootBlkAddr, + FLMBOOL bCounts, + FLMBOOL bData, + IF_ResultSetCompare * pCompare = NULL); + + void btClose( void); + + RCODE btDeleteTree( + IF_DeleteStatus * ifpDeleteStatus); + + RCODE btGetBlockChains( + FLMUINT * puiBlockChains, + FLMUINT * puiNumLevels); + + RCODE btRemoveEntry( + const FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT uiKeyLen); + + RCODE btInsertEntry( + const FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT uiKeyLen, + const FLMBYTE * pucData, + FLMUINT uiDataLen, + FLMBOOL bFirst, + FLMBOOL bLast, + FLMUINT32 * pui32BlkAddr = NULL, + FLMUINT * puiOffsetIndex = NULL); + + RCODE btReplaceEntry( + const FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT uiKeyLen, + const FLMBYTE * pucData, + FLMUINT uiDataLen, + FLMBOOL bFirst, + FLMBOOL bLast, + FLMBOOL bTruncate = TRUE, + FLMUINT32 * pui32BlkAddr = NULL, + FLMUINT * puiOffsetIndex = NULL); + + RCODE btLocateEntry( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMUINT uiMatch, + FLMUINT * puiPosition = NULL, + FLMUINT * puiDataLength = NULL, + FLMUINT32 * pui32BlkAddr = NULL, + FLMUINT * puiOffsetIndex = NULL); + + RCODE btGetEntry( + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMBYTE * pucData, + FLMUINT uiDataBufSize, + FLMUINT * puiDataLen); + + RCODE btNextEntry( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMUINT * puiDataLength = NULL, + FLMUINT32 * pui32BlkAddr = NULL, + FLMUINT * puiOffsetIndex = NULL); + + RCODE btPrevEntry( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMUINT * puiDataLength = NULL, + FLMUINT32 * pui32BlkAddr = NULL, + FLMUINT * puiOffsetIndex = NULL); + + RCODE btFirstEntry( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMUINT * puiDataLength = NULL, + FLMUINT32 * pui32BlkAddr = NULL, + FLMUINT * puiOffsetIndex = NULL); + + RCODE btLastEntry( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMUINT * puiDataLength = NULL, + FLMUINT32 * pui32BlkAddr = NULL, + FLMUINT * puiOffsetIndex = NULL); + + RCODE btSetReadPosition( + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMUINT uiPosition); + + RCODE btGetReadPosition( + FLMUINT * puiPosition); + + RCODE btPositionTo( + FLMUINT uiPosition, + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen); + + RCODE btGetPosition( + FLMUINT * puiPosition); + + RCODE btCheck( + BTREE_ERR_STRUCT * pErrStruct); + + RCODE btRewind( void); + + FINLINE void btRelease( void) + { + releaseBlocks( TRUE); + } + + FINLINE void btResetBtree( void) + { + releaseBlocks( TRUE); + m_bSetupForRead = FALSE; + m_bSetupForWrite = FALSE; + m_bSetupForReplace = FALSE; + m_bOrigInDOBlocks = FALSE; + m_bDataOnlyBlock = FALSE; + m_ui32PrimaryBlkAddr = 0; + m_ui32CurBlkAddr = 0; + m_uiPrimaryOffset = 0; + m_uiCurOffset = 0; + m_uiDataLength = 0; + m_uiPrimaryDataLen = 0; + m_uiOADataLength = 0; + m_uiDataRemaining = 0; + m_uiOADataRemaining = 0; + m_uiOffsetAtStart = 0; + m_uiSearchLevel = BH_MAX_LEVELS; + } + + RCODE btComputeCounts( + F_Btree * pUntilBtree, + FLMUINT * puiBlkCount, + FLMUINT * puiKeyCount, + FLMBOOL * pbTotalsEstimated, + FLMUINT uiAvgBlkFullness); + + FINLINE void btSetSearchLevel( + FLMUINT uiSearchLevel) + { + flmAssert( uiSearchLevel <= BH_MAX_LEVELS); + + btResetBtree(); + + m_uiSearchLevel = uiSearchLevel; + } + + RCODE btMoveBlock( + FLMUINT32 ui32FromBlkAddr, + FLMUINT32 ui32ToBlkAddr); + + FINLINE FLMBOOL btHasCounts( void) + { + return( m_bCounts); + } + + FINLINE FLMBOOL btHasData( void) + { + return( m_bData); + } + + FINLINE FLMBOOL btDbIsOpen( void) + { + return( m_bOpened); + } + + FINLINE FLMBOOL btIsSetupForRead( void) + { + return( m_bSetupForRead); + } + + FINLINE FLMBOOL btIsSetupForWrite( void) + { + return( m_bSetupForWrite); + } + + FINLINE FLMBOOL btIsSetupForReplace( void) + { + return( m_bSetupForReplace); + } + + private: + + RCODE btFreeBlockChain( + FLMUINT uiStartAddr, + FLMUINT uiBlocksToFree, + FLMUINT * puiBlocksFreed, + FLMUINT * puiEndAddr, + IF_DeleteStatus * ifpDeleteStatus); + + FINLINE FLMUINT calcEntrySize( + FLMUINT uiBlkType, + FLMUINT uiFlags, + FLMUINT uiKeyLen, + FLMUINT uiDataLen, + FLMUINT uiOADataLen) + { + switch( uiBlkType) + { + case BT_LEAF: + { + return( uiKeyLen + 2); + } + + case BT_LEAF_DATA: + { + return( 1 + // Flags + (uiKeyLen > ONE_BYTE_SIZE ? 2 : 1) + // KeyLen + (uiDataLen > ONE_BYTE_SIZE ? 2 : 1) + // DataLen + (uiOADataLen && // OA DataLen + (uiFlags & BTE_FLAG_FIRST_ELEMENT) ? 4 : 0) + + uiKeyLen + uiDataLen); + } + + case BT_NON_LEAF: + case BT_NON_LEAF_COUNTS: + { + return( 4 + // Child block address + (uiBlkType == BT_NON_LEAF_COUNTS ? 4 : 0) + // Counts + 2 + // Key length + uiKeyLen); + } + } + + return( 0); + } + + RCODE computeCounts( + F_BTSK * pFromStack, + F_BTSK * pUntilStack, + FLMUINT * puiBlockCount, + FLMUINT * puiKeyCount, + FLMBOOL * pbTotalsEstimated, + FLMUINT uiAvgBlkFullness); + + RCODE blockCounts( + F_BTSK * pStack, + FLMUINT uiFirstOffset, + FLMUINT uiLastOffset, + FLMUINT * puiKeyCount, + FLMUINT * puiElementCount); + + RCODE getStoredCounts( + F_BTSK * pFromStack, + F_BTSK * pUntilStack, + FLMUINT * puiBlockCount, + FLMUINT * puiKeyCount, + FLMBOOL * pbTotalsEstimated, + FLMUINT uiAvgBlkFullness); + + RCODE getCacheBlocks( + F_BTSK * pStack1, + F_BTSK * pStack2); + + FINLINE FLMUINT getAvgKeyCount( + F_BTSK * pFromStack, + F_BTSK * pUntilStack, + FLMUINT uiAvgBlkFullness); + + FINLINE FLMUINT getBlkEntryCount( + FLMBYTE * pBlk) + { + return ((F_BTREE_BLK_HDR *)pBlk)->ui16NumKeys; + } + + FINLINE FLMUINT getBlkAvailSpace( + FLMBYTE * pBlk) + { + return ((F_BLK_HDR *)pBlk)->ui16BlkBytesAvail; + } + + FLMUINT getEntryKeyLength( + FLMBYTE * pucEntry, + FLMUINT uiBlockType, + const FLMBYTE ** ppucKeyRV); + + FLMUINT getEntrySize( + FLMBYTE * pBlk, + FLMUINT uiOffset, + FLMBYTE ** ppucEntry = NULL); + + RCODE calcNewEntrySize( + FLMUINT uiKeyLen, + FLMUINT uiDataLen, + FLMUINT * puiEntrySize, + FLMBOOL * pbHaveRoom, + FLMBOOL * pbDefragBlk); + + RCODE extractEntryData( + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMBYTE * pucBuffer, + FLMUINT uiBufSiz, + FLMUINT * puiDataLen); + + RCODE updateEntry( + const FLMBYTE * pucKey, + FLMUINT uiKeyLen, + const FLMBYTE * pucValue, + FLMUINT uiLen, + F_ELM_UPD_ACTION eAction, + FLMBOOL bTruncate = TRUE); + + RCODE insertEntry( + const FLMBYTE ** ppucKey, + FLMUINT * puiKeyLen, + const FLMBYTE * pucValue, + FLMUINT uiLen, + FLMUINT uiFlags, + FLMUINT * puiChildBlkAddr, + FLMUINT * puiCounts, + const FLMBYTE ** ppucRemainingValue, + FLMUINT * puiRemainingLen, + F_ELM_UPD_ACTION * peAction); + + RCODE storeEntry( + const FLMBYTE * pucKey, + FLMUINT uiKeyLen, + const FLMBYTE * pucValue, + FLMUINT uiLen, + FLMUINT uiFlags, + FLMUINT uiOADataLen, + FLMUINT uiChildBlkAddr, + FLMUINT uiCounts, + FLMUINT uiEntrySize, + FLMBOOL * pbLastEntry); + + RCODE removeEntry( + const FLMBYTE ** ppucKey, + FLMUINT * puiKeyLen, + FLMUINT * puiChildBlkAddr, + FLMUINT * puiCounts, + FLMBOOL * pbMoreToRemove, + F_ELM_UPD_ACTION * peAction); + + RCODE remove( + FLMBOOL bDeleteDOBlocks); + + RCODE removeRange( + FLMUINT uiStartElm, + FLMUINT uiEndElm, + FLMBOOL bDeleteDOBlocks); + + RCODE findEntry( + const FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMUINT uiMatch, + FLMUINT * puiPosition = NULL, + FLMUINT32 * pui32BlkAddr = NULL, + FLMUINT * puiOffsetIndex = NULL); + + RCODE findInBlock( + const FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMUINT uiMatch, + FLMUINT * uiPosition, + FLMUINT32 * ui32BlkAddr, + FLMUINT * uiOffsetIndex); + + RCODE scanBlock( + F_BTSK * pStack, + FLMUINT uiMatch); + + RCODE compareKeys( + const FLMBYTE * pucKey1, + FLMUINT uiKeyLen1, + const FLMBYTE * pucKey2, + FLMUINT uiKeyLen2, + FLMINT * piCompare); + + FINLINE RCODE compareBlkKeys( + const FLMBYTE * pucBlockKey, + FLMUINT uiBlockKeyLen, + const FLMBYTE * pucTargetKey, + FLMUINT uiTargetKeyLen, + FLMINT * piCompare) + { + flmAssert( uiBlockKeyLen); + + if( !m_pCompare && uiBlockKeyLen == uiTargetKeyLen) + { + *piCompare = f_memcmp( pucBlockKey, pucTargetKey, uiBlockKeyLen); + + return( NE_FLM_OK); + } + + return( compareKeys( pucBlockKey, uiBlockKeyLen, + pucTargetKey, uiTargetKeyLen, piCompare)); + } + + RCODE positionToEntry( + FLMUINT uiPosition); + + RCODE searchBlock( + F_BTREE_BLK_HDR * pBlkHdr, + FLMUINT * puiPrevCounts, + FLMUINT uiPosition, + FLMUINT * puiOffset); + + RCODE defragmentBlock( + IF_Block ** ppBlock); + + RCODE advanceToNextElement( + FLMBOOL bAdvanceStack); + + RCODE backupToPrevElement( + FLMBOOL bBackupStack); + + RCODE replaceEntry( + const FLMBYTE ** ppucKey, + FLMUINT * puiKeyLen, + const FLMBYTE * pucValue, + FLMUINT uiLen, + FLMUINT uiFlags, + FLMUINT * puiChildBlkAddr, + FLMUINT * puiCounts, + const FLMBYTE ** ppucRemainingValue, + FLMUINT * puiRemainingLen, + F_ELM_UPD_ACTION * peAction, + FLMBOOL bTruncate = TRUE); + + RCODE replaceOldEntry( + const FLMBYTE ** ppucKey, + FLMUINT * puiKeyLen, + const FLMBYTE * pucValue, + FLMUINT uiLen, + FLMUINT uiFlags, + FLMUINT uiOADataLen, + FLMUINT * puiChildBlkAddr, + FLMUINT * puiCounts, + const FLMBYTE ** ppucRemainingValue, + FLMUINT * puiRemainingLen, + F_ELM_UPD_ACTION * peAction, + FLMBOOL bTruncate = TRUE); + + RCODE replaceByInsert( + const FLMBYTE ** ppucKey, + FLMUINT * puiKeyLen, + const FLMBYTE * pucDataValue, + FLMUINT uiDataLen, + FLMUINT uiOADataLen, + FLMUINT uiFlags, + FLMUINT * puiChildBlkAddr, + FLMUINT * puiCounts, + const FLMBYTE ** ppucRemainingValue, + FLMUINT * puiRemainingLen, + F_ELM_UPD_ACTION * peAction); + + RCODE replace( + FLMBYTE * pucEntry, + FLMUINT uiEntrySize, + FLMBOOL * pbLastEntry); + + RCODE buildAndStoreEntry( + FLMUINT uiBlkType, + FLMUINT uiFlags, + const FLMBYTE * pucKey, + FLMUINT uiKeyLen, + const FLMBYTE * pucData, + FLMUINT uiDataLen, + FLMUINT uiOADataLen, + FLMUINT uiChildBlkAddr, + FLMUINT uiCounts, + FLMBYTE * pucBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiEntrySize); + + RCODE moveEntriesToPrevBlk( + FLMUINT uiNewEntrySize, + IF_Block ** ppPrevBlock, + FLMBOOL * pbEntriesWereMoved); + + RCODE moveEntriesToNextBlk( + FLMUINT uiEntrySize, + FLMBOOL * pbEntriesWereMoved); + + RCODE splitBlock( + const FLMBYTE * pucKey, + FLMUINT uiKeyLen, + const FLMBYTE * pucValue, + FLMUINT uiLen, + FLMUINT uiFlags, + FLMUINT uiOADataLen, + FLMUINT uiChildBlkAddr, + FLMUINT uiCounts, + const FLMBYTE ** ppucRemainingValue, + FLMUINT * puiRemainingLen, + FLMBOOL * pbBlockSplit); + + RCODE createNewLevel( void); + + RCODE storeDataOnlyBlocks( + const FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMBOOL bSaveKey, + const FLMBYTE * pucData, + FLMUINT uiDataLen); + + RCODE replaceDataOnlyBlocks( + const FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMBOOL bSaveKey, + const FLMBYTE * pucData, + FLMUINT uiDataLen, + FLMBOOL bLast, + FLMBOOL bTruncate = TRUE); + + RCODE moveToPrev( + FLMUINT uiStart, + FLMUINT uiFinish, + IF_Block ** ppPrevBlock); + + RCODE moveToNext( + FLMUINT uiStart, + FLMUINT uiFinish, + IF_Block ** ppNextBlock); + + RCODE updateParentCounts( + IF_Block * pChildBlock, + IF_Block ** ppParentBlock, + FLMUINT uiParentElm); + + FLMUINT countKeys( + FLMBYTE * pBlk); + + FLMUINT countRangeOfKeys( + F_BTSK * pFromStack, + FLMUINT uiFromOffset, + FLMUINT uiUntilOffset); + + RCODE moveStackToPrev( + IF_Block * pPrevBlock); + + RCODE moveStackToNext( + IF_Block * pBlock, + FLMBOOL bReleaseCurrent = TRUE); + + RCODE calcOptimalDataLength( + FLMUINT uiKeyLen, + FLMUINT uiDataLen, + FLMUINT uiBytesAvail, + FLMUINT * puiNewDataLen); + + // Performs an integrity check on a chain of data-only blocks + + RCODE verifyDOBlkChain( + FLMUINT uiDOAddr, + FLMUINT uiDataLength, + BTREE_ERR_STRUCT * localErrStruct); + + // Performs a check to verify that the counts in the DB match. + RCODE verifyCounts( + BTREE_ERR_STRUCT * pErrStruct); + + void releaseBlocks( + FLMBOOL bResetStack); + + void releaseBtree( void); + + RCODE saveReplaceInfo( + const FLMBYTE * pucKey, + FLMUINT uiKeyLen); + + RCODE restoreReplaceInfo( + const FLMBYTE ** ppucKey, + FLMUINT * puiKeyLen, + FLMUINT * puiChildBlkAddr, + FLMUINT * puiCounts); + + FINLINE RCODE setReturnKey( + FLMBYTE * pucEntry, + FLMUINT uiBlockType, + FLMBYTE * pucKey, + FLMUINT * puiKeyLen, + FLMUINT uiKeyBufSize); + + RCODE setupReadState( + F_BLK_HDR * pBlkHdr, + FLMBYTE * pucEntry); + + RCODE removeRemainingEntries( + const FLMBYTE * pucKey, + FLMUINT uiKeyLen); + + RCODE deleteEmptyBlock( void); + + RCODE removeDOBlocks( + FLMUINT32 ui32OrigDOAddr); + + RCODE replaceMultiples( + const FLMBYTE ** ppucKey, + FLMUINT * puiKeyLen, + const FLMBYTE * pucDataValue, + FLMUINT uiLen, + FLMUINT uiFlags, + FLMUINT * puiChildBlkAddr, + FLMUINT * puiCounts, + const FLMBYTE ** ppucRemainingValue, + FLMUINT * puiRemainingLen, + F_ELM_UPD_ACTION * peAction); + + RCODE replaceMultiNoTruncate( + const FLMBYTE ** ppucKey, + FLMUINT * puiKeyLen, + const FLMBYTE * pucDataValue, + FLMUINT uiLen, + FLMUINT uiFlags, + FLMUINT * puiChildBlkAddr, + FLMUINT * puiCounts, + const FLMBYTE ** ppucRemainingValue, + FLMUINT * puiRemainingLen, + F_ELM_UPD_ACTION * peAction); + + FINLINE RCODE getNextBlock( + IF_Block ** ppBlock); + + FINLINE RCODE getPrevBlock( + IF_Block ** ppBlock); + + FLMBOOL checkContinuedEntry( + const FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMBOOL * pbLastElement, + FLMBYTE * pucEntry, + FLMUINT uiBlkType); + + RCODE updateCounts( void); + + RCODE storePartialEntry( + const FLMBYTE * pucKey, + FLMUINT uiKeyLen, + const FLMBYTE * pucValue, + FLMUINT uiLen, + FLMUINT uiFlags, + FLMUINT uiChildBlkAddr, + FLMUINT uiCounts, + const FLMBYTE ** ppucRemainingValue, + FLMUINT * puiRemainingLen, + FLMBOOL bNewBlock = FALSE); + + RCODE mergeBlocks( + FLMBOOL bLastEntry, + FLMBOOL * pbMergedWithPrev, + FLMBOOL * pbMergedWithNext, + F_ELM_UPD_ACTION * peAction); + + RCODE merge( + IF_Block ** ppFromBlock, + IF_Block ** ppToBlock); + + RCODE checkDownLinks( void); + + RCODE verifyChildLinks( + IF_Block * pParentBlock); + + RCODE combineEntries( + F_BTREE_BLK_HDR * pSrcBlkHdr, + FLMUINT uiSrcOffset, + F_BTREE_BLK_HDR * pDstBlkHdr, + FLMUINT uiDstOffset, + FLMBOOL * pbEntriesCombined, + FLMUINT * puiEntrySize, + FLMBYTE * pucTempBlk); + + RCODE moveBtreeBlock( + FLMUINT32 ui32FromBlkAddr, + FLMUINT32 ui32ToBlkAddr); + + RCODE moveDOBlock( + FLMUINT32 ui32FromBlkAddr, + FLMUINT32 ui32ToBlkAddr); + + IF_BlockMgr * m_pBlockMgr; + F_Pool * m_pPool; + FLMUINT m_uiRootBlkAddr; + FLMBOOL m_bCounts; + FLMBOOL m_bData; + FLMBOOL m_bSetupForRead; + FLMBOOL m_bSetupForWrite; + FLMBOOL m_bSetupForReplace; + FLMBOOL m_bOpened; + FLMBOOL m_bDataOnlyBlock; + FLMBOOL m_bOrigInDOBlocks; + FLMBOOL m_bFirstRead; + FLMBOOL m_bStackSetup; + F_BTSK * m_pStack; + BTREE_REPLACE_STRUCT * m_pReplaceInfo; + BTREE_REPLACE_STRUCT * m_pReplaceStruct; + const FLMBYTE * m_pucDataPtr; + IF_Block * m_pBlock; + F_BTREE_BLK_HDR * m_pBlkHdr; + FLMUINT m_uiBlockSize; + FLMUINT m_uiDefragThreshold; + FLMUINT m_uiOverflowThreshold; + FLMUINT m_uiStackLevels; + FLMUINT m_uiRootLevel; + FLMUINT m_uiReplaceLevels; + FLMUINT m_uiDataLength; + FLMUINT m_uiPrimaryDataLen; + FLMUINT m_uiOADataLength; + FLMUINT m_uiDataRemaining; + FLMUINT m_uiOADataRemaining; + FLMUINT m_uiPrimaryOffset; + FLMUINT m_uiCurOffset; + FLMUINT m_uiSearchLevel; + FLMUINT m_uiOffsetAtStart; + FLMUINT32 m_ui32PrimaryBlkAddr; + FLMUINT32 m_ui32DOBlkAddr; + FLMUINT32 m_ui32CurBlkAddr; + F_BTSK m_Stack[ BH_MAX_LEVELS]; + IF_ResultSetCompare * m_pCompare; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_SlabManager : public IF_SlabManager, public F_Base + { + public: + + F_SlabManager(); + + virtual ~F_SlabManager(); + + RCODE FLMAPI setup( + FLMUINT uiPreallocSize); + + RCODE FLMAPI allocSlab( + void ** ppSlab, + FLMBOOL bMutexLocked); + + void FLMAPI freeSlab( + void ** ppSlab, + FLMBOOL bMutexLocked); + + RCODE FLMAPI resize( + FLMUINT uiNumBytes, + FLMUINT * puiActualSize = NULL, + FLMBOOL bMutexLocked = FALSE); + + FINLINE void FLMAPI incrementTotalBytesAllocated( + FLMUINT uiCount, + FLMBOOL bMutexLocked) + { + if( !bMutexLocked) + { + lockMutex(); + } + + m_uiTotalBytesAllocated += uiCount; + + if( !bMutexLocked) + { + unlockMutex(); + } + } + + FINLINE void FLMAPI decrementTotalBytesAllocated( + FLMUINT uiCount, + FLMBOOL bMutexLocked) + { + if( !bMutexLocked) + { + lockMutex(); + } + + flmAssert( m_uiTotalBytesAllocated >= uiCount); + m_uiTotalBytesAllocated -= uiCount; + + if( !bMutexLocked) + { + unlockMutex(); + } + } + + FINLINE FLMUINT FLMAPI getSlabSize( void) + { + return( m_uiSlabSize); + } + + FINLINE FLMUINT FLMAPI getTotalSlabs( void) + { + return( m_uiTotalSlabs); + } + + FINLINE void FLMAPI lockMutex( void) + { + f_mutexLock( m_hMutex); + } + + FINLINE void FLMAPI unlockMutex( void) + { + f_mutexUnlock( m_hMutex); + } + + FINLINE FLMUINT FLMAPI totalBytesAllocated( void) + { + return( m_uiTotalBytesAllocated); + } + + FINLINE FLMUINT FLMAPI availSlabs( void) + { + return( m_uiAvailSlabs); + } + + void FLMAPI protectSlab( + void * pSlab); + + void FLMAPI unprotectSlab( + void * pSlab); + + private: + + void freeAllSlabs( void); + + void * allocSlabFromSystem( void); + + void releaseSlabToSystem( + void * pSlab); + + RCODE sortSlabList( void); + + typedef struct + { + void * pPrev; + void * pNext; + } SLABHEADER; + + static FLMINT FLMAPI slabAddrCompareFunc( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2); + + static void FLMAPI slabAddrSwapFunc( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2); + + F_MUTEX m_hMutex; + FLMUINT m_uiTotalBytesAllocated; + void * m_pFirstInSlabList; + void * m_pLastInSlabList; + FLMUINT m_uiSlabSize; + FLMUINT m_uiTotalSlabs; + FLMUINT m_uiAvailSlabs; + FLMUINT m_uiInUseSlabs; + FLMUINT m_uiPreallocSlabs; + #ifdef FLM_SOLARIS + int m_DevZero; + #endif + + friend class F_FixedAlloc; + }; + + /**************************************************************************** + Desc: Class to provide an efficient means of providing many allocations + of a fixed size. + ****************************************************************************/ + class F_FixedAlloc : public IF_FixedAlloc, public F_Base + { + public: + + F_FixedAlloc(); + + virtual ~F_FixedAlloc(); + + RCODE FLMAPI setup( + IF_Relocator * pRelocator, + IF_SlabManager * pSlabManager, + FLMBOOL bMemProtect, + FLMUINT uiCellSize, + FLM_SLAB_USAGE * pUsageStats); + + FINLINE void * FLMAPI allocCell( + IF_Relocator * pRelocator, + void * pvInitialData = NULL, + FLMUINT uiDataSize = 0, + FLMBOOL bMutexLocked = FALSE) + { + void * pvCell; + + flmAssert( pRelocator); + + if( !bMutexLocked) + { + m_pSlabManager->lockMutex(); + } + + if( (pvCell = getCell( pRelocator)) == NULL) + { + goto Exit; + } + + if( uiDataSize == sizeof( FLMUINT *)) + { + *((FLMUINT *)pvCell) = *((FLMUINT *)pvInitialData); + } + else if( uiDataSize) + { + f_memcpy( pvCell, pvInitialData, uiDataSize); + } + + Exit: + + if( !bMutexLocked) + { + m_pSlabManager->unlockMutex(); + } + + return( pvCell); + } + + FINLINE void FLMAPI freeCell( + void * ptr, + FLMBOOL bMutexLocked) + { + freeCell( ptr, bMutexLocked, FALSE, NULL); + } + + void FLMAPI freeUnused( void); + + void FLMAPI freeAll( void); + + FINLINE FLMUINT FLMAPI getCellSize( void) + { + return( m_uiCellSize); + } + + void FLMAPI defragmentMemory( void); + + void FLMAPI protectCell( + void * pvCell); + + void FLMAPI unprotectCell( + void * pvCell); + + private: + + typedef struct Slab + { + void * pvAllocator; + Slab * pNext; + Slab * pPrev; + Slab * pNextSlabWithAvailCells; + Slab * pPrevSlabWithAvailCells; + FLMBYTE * pLocalAvailCellListHead; + FLMUINT16 ui16NextNeverUsedCell; + FLMUINT16 ui16AvailCellCount; + FLMUINT16 ui16AllocatedCells; + #ifdef FLM_CACHE_PROTECT + FLMUINT32 ui16UnprotectCount; + #endif + } SLAB; + + typedef struct CELLHEADER + { + SLAB * pContainingSlab; + #ifdef FLM_DEBUG + FLMUINT * puiStack; + #endif + } CELLHEADER; + + typedef struct CELLHEADER2 + { + CELLHEADER cellHeader; + IF_Relocator * pRelocator; + } CELLHEADER2; + + typedef struct CellAvailNext + { + FLMBYTE * pNextInList; + #ifdef FLM_DEBUG + FLMBYTE szDebugPattern[ 8]; + #endif + } CELLAVAILNEXT; + + #ifdef FLM_CACHE_PROTECT + void protectSlab( + SLAB * pSlab, + FLMBOOL bMutexLocked); + + void unprotectSlab( + SLAB * pSlab, + FLMBOOL bMutexLocked); + #endif + + void * getCell( + IF_Relocator * pRelocator); + + SLAB * getAnotherSlab( void); + + static FINLINE FLMUINT getAllocAlignedSize( + FLMUINT uiAskedForSize) + { + return( (uiAskedForSize + FLM_ALLOC_ALIGN) & (~FLM_ALLOC_ALIGN)); + } + + void freeSlab( + SLAB * pSlab); + + void freeCell( + void * pCell, + FLMBOOL bMutexLocked, + FLMBOOL bFreeIfEmpty, + FLMBOOL * pbFreedSlab); + + #ifdef FLM_DEBUG + void testForLeaks( void); + #endif + + FINLINE static FLMINT FLMAPI slabAddrCompareFunc( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2) + { + SLAB * pSlab1 = (((SLAB **)pvBuffer)[ uiPos1]); + SLAB * pSlab2 = (((SLAB **)pvBuffer)[ uiPos2]); + + flmAssert( pSlab1 != pSlab2); + + if( pSlab1 < pSlab2) + { + return( -1); + } + + return( 1); + } + + FINLINE static void FLMAPI slabAddrSwapFunc( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2) + { + SLAB ** ppSlab1 = &(((SLAB **)pvBuffer)[ uiPos1]); + SLAB ** ppSlab2 = &(((SLAB **)pvBuffer)[ uiPos2]); + SLAB * pTmp; + + pTmp = *ppSlab1; + *ppSlab1 = *ppSlab2; + *ppSlab2 = pTmp; + } + + IF_SlabManager * m_pSlabManager; + SLAB * m_pFirstSlab; + SLAB * m_pLastSlab; + SLAB * m_pFirstSlabWithAvailCells; + SLAB * m_pLastSlabWithAvailCells; + IF_Relocator * m_pRelocator; + FLMBOOL m_bAvailListSorted; + FLMUINT m_uiSlabsWithAvailCells; + FLMUINT m_uiSlabHeaderSize; + FLMUINT m_uiCellHeaderSize; + FLMUINT m_uiCellSize; + FLMUINT m_uiSizeOfCellAndHeader; + FLMUINT m_uiTotalFreeCells; + FLMUINT m_uiCellsPerSlab; + FLMUINT m_uiSlabSize; + FLM_SLAB_USAGE * m_pUsageStats; + #ifdef FLM_CACHE_PROTECT + FLMBOOL m_bMemProtectionEnabled; + #endif + + friend class F_BufferAlloc; + friend class F_MultiAlloc; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_BufferAlloc : public IF_BufferAlloc, public F_Base + { + public: + + F_BufferAlloc() + { + f_memset( m_ppAllocators, 0, sizeof( m_ppAllocators)); + m_pSlabManager = NULL; + } + + virtual ~F_BufferAlloc(); + + RCODE FLMAPI setup( + IF_SlabManager * pSlabManager, + FLMBOOL bMemProtect, + FLM_SLAB_USAGE * pUsageStats); + + RCODE FLMAPI allocBuf( + IF_Relocator * pRelocator, + FLMUINT uiSize, + void * pvInitialData, + FLMUINT uiDataSize, + FLMBYTE ** ppucBuffer, + FLMBOOL * pbAllocatedOnHeap = NULL); + + RCODE FLMAPI reallocBuf( + IF_Relocator * pRelocator, + FLMUINT uiOldSize, + FLMUINT uiNewSize, + void * pvInitialData, + FLMUINT uiDataSize, + FLMBYTE ** ppucBuffer, + FLMBOOL * pbAllocatedOnHeap = NULL); + + void FLMAPI freeBuf( + FLMUINT uiSize, + FLMBYTE ** ppucBuffer); + + FLMUINT FLMAPI getTrueSize( + FLMUINT uiSize, + FLMBYTE * pucBuffer); + + void FLMAPI defragmentMemory( void); + + private: + + IF_FixedAlloc * getAllocator( + FLMUINT uiSize); + + IF_SlabManager * m_pSlabManager; + IF_FixedAlloc * m_ppAllocators[ NUM_BUF_ALLOCATORS]; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_MultiAlloc : public IF_MultiAlloc, public F_Base + { + public: + + F_MultiAlloc() + { + m_pSlabManager = NULL; + m_puiCellSizes = NULL; + m_ppAllocators = NULL; + } + + ~F_MultiAlloc() + { + cleanup(); + } + + RCODE FLMAPI setup( + F_SlabManager * pSlabManager, + FLMBOOL bMemProtect, + FLMUINT * puiCellSizes, + FLM_SLAB_USAGE * pUsageStats); + + RCODE FLMAPI allocBuf( + IF_Relocator * pRelocator, + FLMUINT uiSize, + FLMBYTE ** ppucBuffer, + FLMBOOL bMutexLocked = FALSE); + + RCODE FLMAPI reallocBuf( + IF_Relocator * pRelocator, + FLMUINT uiNewSize, + FLMBYTE ** ppucBuffer, + FLMBOOL bMutexLocked = FALSE); + + FINLINE void FLMAPI freeBuf( + FLMBYTE ** ppucBuffer) + { + if( ppucBuffer && *ppucBuffer) + { + getAllocator( *ppucBuffer)->freeCell( *ppucBuffer, FALSE); + *ppucBuffer = NULL; + } + } + + void FLMAPI defragmentMemory( void); + + FINLINE FLMUINT FLMAPI getTrueSize( + FLMBYTE * pucBuffer) + { + return( getAllocator( pucBuffer)->getCellSize()); + } + + void FLMAPI protectBuffer( + void * pvBuffer, + FLMBOOL bMutexLocked = FALSE); + + void FLMAPI unprotectBuffer( + void * pvBuffer, + FLMBOOL bMutexLocked = FALSE); + + FINLINE void FLMAPI lockMutex( void) + { + m_pSlabManager->lockMutex(); + } + + FINLINE void FLMAPI unlockMutex( void) + { + m_pSlabManager->unlockMutex(); + } + + private: + + IF_FixedAlloc * getAllocator( + FLMUINT uiSize); + + IF_FixedAlloc * getAllocator( + FLMBYTE * pucBuffer); + + void cleanup( void); + + IF_SlabManager * m_pSlabManager; + FLMUINT * m_puiCellSizes; + IF_FixedAlloc ** m_ppAllocators; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + FLMUINT flmGetFSBlockSize( + FLMBYTE * pszFileName); + + #if defined( FLM_LINUX) + void flmGetLinuxKernelVersion( + FLMUINT * puiMajor, + FLMUINT * puiMinor, + FLMUINT * puiRevision); + + FLMUINT flmGetLinuxMaxFileSize( void); + + void flmGetLinuxMemInfo( + FLMUINT64 * pui64TotalMem, + FLMUINT64 * pui64AvailMem); + #endif + +#endif // FTKSYS_H diff --git a/ftk/src/ftktext.cpp b/ftk/src/ftktext.cpp new file mode 100644 index 0000000..e892b78 --- /dev/null +++ b/ftk/src/ftktext.cpp @@ -0,0 +1,7288 @@ +//------------------------------------------------------------------------------ +// Desc: Routines to support NATIVE to/from internal numeric types and +// string comparision/shift routines. +// +// Tabs: 3 +// +// Copyright (c) 1991-1993, 1996-1998, 2000, 2002-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +FSTATIC FLMBOOL f_addMetaphone( + const char * pszStr, + const char * pszAltStr, + FLMBYTE * pszMeta, + FLMUINT * puiMetaOffset, + FLMBYTE * pszAltMeta, + FLMUINT * puiAltMetaOffset); + +FSTATIC void f_metaStrToNum( + FLMBYTE * pszMeta, + FLMUINT * puiMeta); + +#ifdef FLM_DEBUG + + typedef struct + { + const char * pszWord; + FLMUINT uiMeta; + FLMUINT uiAltMeta; + } METAPHONE_MAPPING; + + static METAPHONE_MAPPING gv_MetaTestTable[] = + { + { "ghislane", 0x4680, 0x4680 }, + { "ghiradelli", 0x4AC6, 0x4AC6 }, + { "hugh", 0x3000, 0x3000 }, + { "san francisco", 0xB82A, 0xB82A }, + { "van wagner", 0x2858, 0x2858 }, + { "vanwagner", 0x2858, 0x2858 }, + { "gnome", 0x8700, 0x8700 }, + { "write", 0xAC00, 0xAC00 }, + { "dumb", 0xC700, 0xC700 }, + { "caesar", 0xBBA0, 0xBBA0 }, + { "chianti", 0x58C0, 0x58C0 }, + { "michael", 0x7560, 0x7D60 }, + { "chemistry", 0x57BC, 0x57BC }, + { "chorus", 0x5AB0, 0x5AB0 }, + { "mchugh", 0x7500, 0x7500 }, + { "czerny", 0xBA80, 0xDA80 }, + { "focaccia", 0x25D0, 0x25D0 }, + { "mcclellan", 0x7566, 0x7566 }, + { "bellocchio", 0x96D0, 0x96D0 }, + { "bacchus", 0x95B0, 0x95B0 }, + { "accident", 0x15BC, 0x15BC }, + { "accede", 0x15BC, 0x15BC }, + { "succeed", 0xB5BC, 0xB5BC }, + { "bacci", 0x9D00, 0x9D00 }, + { "mac caffrey", 0x752A, 0x752A }, + { "edge", 0x1400, 0x1400 }, + { "edgar", 0x1C5A, 0x1C5A }, + { "laugh", 0x6200, 0x6200 }, + { "caugh", 0x5200, 0x5200 }, + { "cagney", 0x5580, 0x5580 }, + { "tagliaro", 0xC56A, 0xC6A0 }, + { "biaggi", 0x9400, 0x9500 }, + { "jose", 0x3B00, 0x3B00 }, + { "yankelovich", 0x1856, 0x1856 }, + { "bajador", 0x94CA, 0x93CA }, + { "cabrillo", 0x59A6, 0x59A0 }, + { "campbell", 0x5796, 0x5796 }, + { "rogier", 0xA400, 0xA4A0 }, + { "hochmeier", 0x357A, 0x357A }, + { "island", 0x168C, 0x168C }, + { "isle", 0x1600, 0x1600 }, + { "sugar", 0xD5A0, 0xB5A0 }, + { "herb", 0x3A90, 0x3A90 }, + { "mannheim", 0x7870, 0x7870 }, + { "snider", 0xB8CA, 0xD8CA }, + { "schneider", 0xD8CA, 0xB8CA }, + { "smith", 0xB700, 0xD7C0 }, + { "schmidt", 0xD7C0, 0xB7C0 }, + { "school", 0xB560, 0xB560 }, + { "schenker", 0xD85A, 0xB585 }, + { "resnais", 0xAB80, 0xAB8B }, + { "artois", 0x1AC0, 0x1ACB }, + { "celebration", 0xB69A, 0xB69A }, + { "thomas", 0xC7B0, 0xC7B0 }, + { "uomo", 0x1700, 0x1700 }, + { "womo", 0x1700, 0x2700 }, + { "arnow", 0x1A80, 0x1A82 }, + { "arnoff", 0x1A82, 0x1A82 }, + { "filipowicz", 0x269C, 0x2692 }, + { "breaux", 0x9A00, 0x9A00 }, + { "zhao", 0x4000, 0x4000 }, + { NULL, 0x0000, 0x0000 } + }; +#endif + +#define UNICODE_DECIMAL_DIGIT_MASK 0x08 +#define UNICODE_ALPHABETIC_MASK 0x04 +#define UNICODE_UPPERCASE_MASK 0x02 +#define UNICODE_LOWERCASE_MASK 0x01 + +const unsigned char UnicodeProperties[ 32768] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 136, 136, 136, 136, 136, 0, 0, 0, + 6, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 96, 0, 0, + 5, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 80, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 5, 0, 0, 80, 0, 0, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 96, 102, 102, 102, 101, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 80, 85, 85, 85, 85, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 86, 86, 86, 86, + 86, 86, 86, 86, 85, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 102, 86, 86, 85, + 86, 101, 101, 102, 86, 102, 85, 102, 102, 86, 101, 102, 101, 85, 102, 86, + 101, 101, 101, 102, 86, 85, 101, 102, 86, 102, 86, 86, 101, 84, 101, 85, + 68, 68, 100, 86, 69, 100, 86, 86, 86, 86, 86, 86, 86, 86, 85, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 86, 69, 101, 102, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 85, 80, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 84, 68, 68, 68, + 85, 0, 0, 68, 68, 68, 68, 68, 68, 0, 0, 0, 0, 0, 0, 0, + 85, 85, 80, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, + 0, 0, 0, 96, 102, 96, 96, 102, 86, 102, 102, 102, 102, 102, 102, 102, + 102, 6, 102, 102, 102, 102, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 80, 85, 102, 101, 85, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 85, 85, 101, 6, 86, 101, 0, 0, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 0, 0, 0, 0, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 102, 86, 86, 86, 86, 86, 86, 80, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 0, 101, 0, 0, 0, + 101, 101, 101, 101, 101, 101, 101, 101, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 6, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 96, 4, 0, 0, 0, + 5, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 68, 68, 68, 68, 68, 4, 68, 4, + 4, 64, 64, 0, 0, 0, 0, 0, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 64, 0, 0, 68, 64, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 68, 68, 68, 0, 0, 0, 0, 0, + 4, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 64, 0, 0, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 0, 0, 0, 0, + 136, 136, 136, 136, 136, 0, 0, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 4, 68, 68, 68, 64, 0, + 4, 68, 68, 68, 64, 0, 4, 68, 136, 136, 136, 136, 136, 68, 64, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 0, 0, 0, 0, 0, 0, 4, 68, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 0, 4, 68, + 68, 68, 68, 68, 68, 68, 64, 0, 64, 0, 0, 0, 68, 68, 68, 68, + 68, 68, 0, 136, 136, 136, 136, 136, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 68, 4, 68, 68, 68, 64, 4, 64, 4, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 64, 68, 68, 68, 64, 64, 0, 68, 68, 0, 4, 68, + 68, 68, 64, 4, 64, 4, 64, 0, 0, 0, 0, 4, 0, 0, 68, 4, + 68, 68, 0, 136, 136, 136, 136, 136, 68, 0, 0, 0, 0, 0, 0, 0, + 4, 68, 4, 68, 68, 64, 0, 4, 64, 4, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 64, 68, 68, 68, 64, 68, 4, 64, 68, 0, 0, 68, + 68, 64, 0, 4, 64, 4, 64, 0, 0, 0, 0, 0, 4, 68, 64, 64, + 0, 0, 0, 136, 136, 136, 136, 136, 68, 68, 64, 0, 0, 0, 0, 0, + 4, 68, 4, 68, 68, 68, 68, 4, 68, 4, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 64, 68, 68, 68, 64, 68, 4, 68, 68, 0, 4, 68, + 68, 68, 68, 4, 68, 4, 64, 0, 64, 0, 0, 0, 0, 0, 0, 0, + 68, 68, 0, 136, 136, 136, 136, 136, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 68, 4, 68, 68, 68, 64, 4, 64, 4, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 64, 68, 68, 68, 64, 68, 4, 68, 68, 0, 4, 68, + 68, 68, 0, 4, 64, 4, 64, 0, 0, 0, 0, 68, 0, 0, 68, 4, + 68, 0, 0, 136, 136, 136, 136, 136, 4, 0, 0, 0, 0, 0, 0, 0, + 0, 68, 4, 68, 68, 64, 0, 68, 64, 68, 68, 0, 4, 64, 64, 68, + 0, 4, 64, 0, 68, 64, 0, 68, 68, 68, 68, 4, 68, 0, 0, 68, + 68, 64, 0, 68, 64, 68, 64, 0, 0, 0, 0, 4, 0, 0, 0, 0, + 0, 0, 0, 8, 136, 136, 136, 136, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 68, 4, 68, 68, 68, 64, 68, 64, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 64, 68, 68, 68, 68, 68, 4, 68, 68, 0, 0, 68, + 68, 68, 64, 68, 64, 68, 64, 0, 0, 0, 4, 64, 0, 0, 0, 0, + 68, 0, 0, 136, 136, 136, 136, 136, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 68, 4, 68, 68, 68, 64, 68, 64, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 64, 68, 68, 68, 68, 68, 4, 68, 68, 0, 4, 68, + 68, 68, 64, 68, 64, 68, 64, 0, 0, 0, 4, 64, 0, 0, 0, 64, + 68, 0, 0, 136, 136, 136, 136, 136, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 68, 4, 68, 68, 68, 64, 68, 64, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 64, 68, 68, 68, 68, 68, 68, 68, 68, 0, 0, 68, + 68, 68, 0, 68, 64, 68, 64, 0, 0, 0, 0, 4, 0, 0, 0, 0, + 68, 0, 0, 136, 136, 136, 136, 136, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 68, 4, 68, 68, 68, 68, 68, 68, 68, 68, 64, 0, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 4, 68, 68, 68, 68, 4, 0, + 68, 68, 68, 64, 0, 0, 0, 4, 68, 68, 64, 64, 68, 68, 68, 68, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, + 4, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 64, 0, 0, + 68, 68, 68, 64, 0, 0, 4, 0, 136, 136, 136, 136, 136, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 64, 64, 4, 64, 64, 4, 0, 0, 0, 68, 68, 4, 68, 68, 68, + 4, 68, 4, 4, 0, 68, 4, 68, 68, 68, 68, 68, 68, 4, 68, 0, + 68, 68, 64, 64, 0, 0, 4, 0, 136, 136, 136, 136, 136, 0, 68, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 136, 136, 136, 136, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 68, 68, 68, 68, 4, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 64, 0, 0, 4, 68, 68, 68, 68, 68, 68, 68, + 68, 0, 0, 0, 68, 68, 0, 0, 68, 68, 68, 68, 4, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 64, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 4, 68, 68, 4, 64, 68, 68, 68, 64, 0, 64, 64, 0, 0, 0, + 136, 136, 136, 136, 136, 0, 0, 0, 68, 68, 68, 68, 68, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, + 102, 102, 102, 0, 0, 0, 0, 0, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 64, 0, 0, 0, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 0, 0, 4, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 64, 0, 0, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 0, 0, 0, + 68, 68, 68, 64, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 64, 64, 68, 68, 0, 68, 68, 68, 64, 64, 68, 68, 0, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 64, 64, 68, 68, 0, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 64, 64, 68, 68, 0, 68, 68, 68, 64, + 64, 68, 68, 0, 68, 68, 68, 64, 68, 68, 68, 64, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 64, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 64, 64, 68, 68, 0, 68, 68, 68, 64, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 64, 68, 68, 68, 68, 68, 68, 68, 68, 68, 64, 0, 0, + 0, 0, 0, 0, 8, 136, 136, 136, 136, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 64, 0, 0, 0, 0, 0, + 4, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 64, 4, 68, 68, 68, 64, 0, 0, 0, 0, + 4, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 64, 0, 0, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 64, 0, 68, 64, 0, 0, 0, 0, 0, 0, 0, + 68, 68, 68, 68, 68, 68, 64, 68, 68, 68, 0, 0, 0, 0, 0, 0, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 0, 0, 0, 0, 0, 0, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 0, 0, 0, 0, 0, 0, + 68, 68, 68, 68, 68, 68, 64, 68, 64, 68, 0, 0, 0, 0, 0, 0, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 0, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 64, 0, 0, 0, 0, 0, 0, 4, 0, 0, 64, 0, + 136, 136, 136, 136, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 136, 136, 136, 136, 136, 0, 0, 0, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 0, 0, 0, 0, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 64, 0, + 68, 68, 68, 68, 68, 68, 0, 0, 68, 68, 68, 68, 64, 0, 0, 0, + 0, 0, 0, 136, 136, 136, 136, 136, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 0, 68, 68, 64, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 85, 85, 85, 0, 0, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 0, 0, 0, + 85, 85, 85, 85, 102, 102, 102, 102, 85, 85, 85, 0, 102, 102, 102, 0, + 85, 85, 85, 85, 102, 102, 102, 102, 85, 85, 85, 85, 102, 102, 102, 102, + 85, 85, 85, 0, 102, 102, 102, 0, 85, 85, 85, 85, 6, 6, 6, 6, + 85, 85, 85, 85, 102, 102, 102, 102, 85, 85, 85, 85, 85, 85, 85, 0, + 85, 85, 85, 85, 68, 68, 68, 68, 85, 85, 85, 85, 68, 68, 68, 68, + 85, 85, 85, 85, 68, 68, 68, 68, 85, 85, 80, 85, 102, 102, 64, 80, + 0, 85, 80, 85, 102, 102, 64, 0, 85, 85, 0, 85, 102, 102, 0, 0, + 85, 85, 85, 85, 102, 102, 96, 0, 0, 85, 80, 85, 102, 102, 64, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 96, 0, 6, 0, 86, 102, 85, 102, 101, 6, 0, 6, 102, 102, 0, + 0, 0, 96, 96, 96, 102, 102, 5, 102, 6, 84, 68, 69, 0, 5, 102, + 0, 0, 6, 85, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 102, 102, 102, 102, 102, 102, 102, 102, 85, 85, 85, 85, 85, 85, 85, 85, + 68, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 68, 68, 68, 68, 0, 0, 0, 4, 68, 68, 0, 68, 68, 64, 0, + 4, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 64, 0, 0, 4, 68, + 4, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 64, 68, 68, + 0, 0, 4, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 64, 0, 4, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 64, 0, 0, 0, 0, 0, 0, 0, 0, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 68, 68, 68, 68, 68, 68, 68, 68, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 0, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 85, 85, 85, 80, 0, 0, 0, 0, 0, 5, 85, 85, 0, 0, 4, 68, + 68, 68, 68, 68, 64, 68, 68, 68, 68, 68, 68, 64, 68, 68, 64, 64, + 68, 4, 64, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 0, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 68, 68, 68, 68, 68, 68, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 68, 68, 64, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 64, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 136, 136, 136, 136, 136, 0, 0, 0, + 6, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 96, 0, 0, + 5, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 80, 0, 0, + 0, 0, 0, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 64, + 0, 68, 68, 68, 0, 68, 68, 68, 0, 68, 68, 68, 0, 68, 64, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +#define HANDLE_NEGATIVE \ + if( value < 0) \ + { *ptr++ = '-'; \ + absValue = (FLMUINT)(-(value)); \ + } \ + else absValue = (FLMUINT)value; + +#define HANDLE_DNEGATIVE \ + if( value < 0) \ + { *ptr++ = '-'; \ + absValue = (FLMUINT)(-(value)); \ + } \ + else absValue = (FLMUINT)value; + +#define PUSH_DIGITS( v) \ + {register FLMUINT reg = v; \ + do{ *sp++ = (char)((reg % 10) + '0'); \ + } while( reg /= 10); \ + } + +#define POP_DIGITS \ + while( stack < sp--) \ + *ptr++ = *sp; \ + *ptr = '\0'; + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE FLMBOOL f_isWhitespace( + FLMUNICODE uzChar) +{ + return( (uzChar == ' ' || uzChar == '\t' || + uzChar == '\n' || uzChar == '\r') ? TRUE : FALSE); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE FLMUNICODE f_convertChar( + FLMUNICODE uzChar, + FLMUINT uiCompareRules) +{ + if (uzChar == ASCII_SPACE || + (uzChar == ASCII_UNDERSCORE && + (uiCompareRules & FLM_COMP_NO_UNDERSCORES)) || + (f_isWhitespace( uzChar) && + (uiCompareRules & FLM_COMP_WHITESPACE_AS_SPACE))) + { + return( (FLMUNICODE)((uiCompareRules & + (FLM_COMP_NO_WHITESPACE | + FLM_COMP_IGNORE_LEADING_SPACE)) + ? (FLMUNICODE)0 + : (FLMUNICODE)ASCII_SPACE)); + } + else if (uzChar == ASCII_DASH && (uiCompareRules & FLM_COMP_NO_DASHES)) + { + return( (FLMUNICODE)0); + } + else + { + return( uzChar); + } +} + +/**************************************************************************** +Desc: Unsigned word to NATIVE value - null terminate the native string +Return: char pointer to the NULL byte in the native string +Notes: Radix not defined because it is not needed +****************************************************************************/ +char * FLMAPI f_uwtoa( + FLMUINT16 value, + char * ptr) +{ + char stack[ 10]; + char * sp = stack; + + PUSH_DIGITS( value); + + POP_DIGITS; + + return( ptr); +} + +/**************************************************************************** +Desc: Native to UDWORD value. Supports 0x codes. Non digits NOT ALLOWED + NO LEADING SPACES ALLOWED ! ! ! No checks for overflow over 4 bytes! +Return: UDWORD value of what is being pointed to +Notes: This algorithm is NOT standard, assumes UNSIGNED char arithmetic + so (20 - 30) should be 245 and NOT -10. +****************************************************************************/ +FLMUINT FLMAPI f_atoud( + const char * pszBuf, + FLMBOOL bAllowUnprefixedHex) +{ + FLMUINT uiValue; + FLMBOOL bAllowHex = FALSE; + + if( *pszBuf == NATIVE_ZERO && + (*(pszBuf + 1) == NATIVE_LOWER_X || *(pszBuf + 1) == NATIVE_UPPER_X)) + { + pszBuf += 2; + bAllowHex = TRUE; + } + else if( bAllowUnprefixedHex) + { + bAllowHex = TRUE; + } + + uiValue = 0; + while( *pszBuf) + { + if( *pszBuf >= '0' && *pszBuf <= '9') + { + if( !bAllowHex) + { + uiValue *= 10; + } + else + { + uiValue <<= 4; + } + + uiValue += (FLMUINT)(*pszBuf - '0'); + } + else if( bAllowHex) + { + if( *pszBuf >= 'A' && *pszBuf <= 'F') + { + uiValue <<= 4; + uiValue += (FLMUINT)(*pszBuf - 'A') + 10; + } + else if( *pszBuf >= 'a' && *pszBuf <= 'f') + { + uiValue <<= 4; + uiValue += (FLMUINT)(*pszBuf - 'a') + 10; + } + else + { + break; + } + } + else + { + break; + } + pszBuf++; + } + + return( uiValue); +} + +/**************************************************************************** +Desc: Native to FLMUINT64 value. Supports 0x codes. Non digits + NOT ALLOWED NO LEADING SPACES ALLOWED! No checks for overflow + over 4 bytes! +Return: FLMUINT64 value of what is being pointed to +Notes: This algorithm is NOT standard, assumes UNSIGNED char arithmetic + so (20 - 30) should be 245 and NOT -10. +****************************************************************************/ +FLMUINT64 FLMAPI f_atou64( + const char * pszBuf) +{ + FLMUINT64 ui64Value; + FLMBOOL bAllowHex = FALSE; + + if( *pszBuf == NATIVE_ZERO && + (*(pszBuf + 1) == NATIVE_LOWER_X || *(pszBuf + 1) == NATIVE_UPPER_X)) + { + pszBuf += 2; + bAllowHex = TRUE; + } + + ui64Value = 0; + while( *pszBuf) + { + if( *pszBuf >= '0' && *pszBuf <= '9') + { + if( !bAllowHex) + { + ui64Value *= 10; + } + else + { + ui64Value <<= 4; + } + + ui64Value += (FLMUINT64)(*pszBuf - '0'); + } + else if( bAllowHex) + { + if( *pszBuf >= 'A' && *pszBuf <= 'F') + { + ui64Value <<= 4; + ui64Value += (FLMUINT64)(*pszBuf - 'A') + 10; + } + else if( *pszBuf >= 'a' && *pszBuf <= 'f') + { + ui64Value <<= 4; + ui64Value += (FLMUINT64)(*pszBuf - 'a') + 10; + } + else + { + break; + } + } + else + { + break; + } + pszBuf++; + } + + return( ui64Value); +} + +/**************************************************************************** +Desc: Unsigned double (4 byte) number to native value & null terminate +Return: char pointer to the NULL byte in the native string +Notes: Radix not defined. +****************************************************************************/ +char * FLMAPI f_udtoa( + FLMUINT value, + char * ptr) +{ + char stack[ 10]; + char * sp = stack; + + PUSH_DIGITS( value); + + POP_DIGITS; + + return( ptr); +} + +/**************************************************************************** +Desc: Word to native value - null terminate the native string +Return: char pointer to the NULL byte in the native string +Notes: Radix not defined because it is not needed +****************************************************************************/ +char * FLMAPI f_wtoa( + FLMINT16 value, + char * ptr) +{ + char stack[ 10]; + char * sp = stack; + FLMUINT absValue; + + HANDLE_NEGATIVE; + + PUSH_DIGITS( absValue); + + POP_DIGITS; + + return( ptr); +} + +/**************************************************************************** +Desc: Double (4 byte) number to native value - null terminate the string +Return: char pointer to the NULL byte in the native string +****************************************************************************/ +char * FLMAPI f_dtoa( + FLMINT value, + char * ptr) +{ + char stack[ 10]; + char * sp = stack; + FLMUINT absValue; + + HANDLE_DNEGATIVE; + + PUSH_DIGITS( absValue); + + POP_DIGITS; + + return( ptr); +} + +/***************************************************************************** +Desc: Convert unsigned 64 bit value to ASCII. +*****************************************************************************/ +char * FLMAPI f_ui64toa( + FLMUINT64 ui64Value, + char * pszAscii) +{ + char szStack [30]; + char * pszStack = &szStack [0]; + + do + { + *pszStack++ = (char)((ui64Value % 10) + '0'); + } + while ((ui64Value /= 10) > 0); + + pszStack--; + for (;;) + { + *pszAscii++ = *pszStack; + if (pszStack == &szStack [0]) + { + break; + } + pszStack--; + } + *pszAscii = 0; + + // Return pointer to terminating null character + + return( pszAscii); +} + +/***************************************************************************** +Desc: Convert signed 64 bit value to ASCII. +*****************************************************************************/ +char * FLMAPI f_i64toa( + FLMINT64 i64Value, + char * pszAscii) +{ + if (i64Value < 0) + { + *pszAscii++ = '-'; + i64Value = -i64Value; + } + return( f_ui64toa( (FLMUINT64)i64Value, pszAscii)); +} + +/**************************************************************************** +Desc: Ascii to integer +****************************************************************************/ +FLMINT FLMAPI f_atoi( + const char * pszStr) +{ + return( f_atod( pszStr)); +} + +/**************************************************************************** +Desc: native to long +****************************************************************************/ +FLMINT FLMAPI f_atol( + const char * pszStr) +{ + return( f_atod( pszStr)); +} + +/**************************************************************************** +Desc: native to DWORD value. Supports 0x codes. Non digits NOT ALLOWED + NO LEADING SPACES ALLOWED ! ! ! No checks for overflow over 4 bytes! +Return: DWORD value of what is being pointed to +Notes: This algorithm is NOT standard! Assumes UNSIGNED char arithmetic + so (20 - 30) should be 245 and NOT -10. +****************************************************************************/ +FLMINT FLMAPI f_atod( + const char * pszBuf) +{ + FLMINT iValue; + FLMBOOL bNeg = FALSE; + + if( *pszBuf == '-') + { + bNeg = TRUE; + pszBuf++; + } + else if( *pszBuf == '+') + { + pszBuf++; + } + + iValue = (FLMINT)f_atoud( pszBuf); + return( bNeg ? -iValue : iValue); +} + +/**************************************************************************** +Desc: Utility function to return the maximum size of a hex number + represented as a string. +****************************************************************************/ +FINLINE FLMUINT maxHexSize( + FLMUINT uiSizeOfPtr) +{ + return uiSizeOfPtr * 2; +} + +/**************************************************************************** +Desc: Utility function to return the maximum size of a decimal number + represented as a string. +****************************************************************************/ +FINLINE FLMUINT maxDecimalSize( + FLMUINT uiSizeOfPtr) +{ + switch (uiSizeOfPtr) + { + case 4: + return 10; + case 8: + return 20; + default: + flmAssert( 0); + return 0; + } +} + +/**************************************************************************** +Desc: Returns the length of a unicode string +****************************************************************************/ +FLMUINT FLMAPI f_unilen( + const FLMUNICODE * puzStr) +{ + FLMUINT uiLen = 0; + + if( !puzStr) + { + goto Exit; + } + + while( *puzStr) + { + puzStr++; + uiLen++; + } + +Exit: + + return( uiLen); +} + +/**************************************************************************** +Desc: Copies a unicode string +****************************************************************************/ +FLMUNICODE * FLMAPI f_unicpy( + FLMUNICODE * puzDestStr, + const FLMUNICODE * puzSrcStr) +{ + const FLMUNICODE * puzSrc = puzSrcStr; + FLMUNICODE * puzDest = puzDestStr; + + while( *puzSrc) + { + *puzDest++ = *puzSrc++; + } + + *puzDest = 0; + return( puzDestStr); +} + +/**************************************************************************** +Desc: A rather trivial unicode monocase function. +****************************************************************************/ +FLMUNICODE FLMAPI f_unitolower( + FLMUNICODE uChar) +{ + static const FLMUNICODE basicAlpha[ 0x600] = + { + 0x0000, /* Monocases to self */ + 0x0001, /* Monocases to self */ + 0x0002, /* Monocases to self */ + 0x0003, /* Monocases to self */ + 0x0004, /* Monocases to self */ + 0x0005, /* Monocases to self */ + 0x0006, /* Monocases to self */ + 0x0007, /* Monocases to self */ + 0x0008, /* Monocases to self */ + 0x0009, /* Monocases to self */ + 0x000A, /* Monocases to self */ + 0x000B, /* Monocases to self */ + 0x000C, /* Monocases to self */ + 0x000D, /* Monocases to self */ + 0x000E, /* Monocases to self */ + 0x000F, /* Monocases to self */ + 0x0010, /* Monocases to self */ + 0x0011, /* Monocases to self */ + 0x0012, /* Monocases to self */ + 0x0013, /* Monocases to self */ + 0x0014, /* Monocases to self */ + 0x0015, /* Monocases to self */ + 0x0016, /* Monocases to self */ + 0x0017, /* Monocases to self */ + 0x0018, /* Monocases to self */ + 0x0019, /* Monocases to self */ + 0x001A, /* Monocases to self */ + 0x001B, /* Monocases to self */ + 0x001C, /* Monocases to self */ + 0x001D, /* Monocases to self */ + 0x001E, /* Monocases to self */ + 0x001F, /* Monocases to self */ + 0x0020, /* Monocases to self */ + 0x0021, /* Monocases to self */ + 0x0022, /* Monocases to self */ + 0x0023, /* Monocases to self */ + 0x0024, /* Monocases to self */ + 0x0025, /* Monocases to self */ + 0x0026, /* Monocases to self */ + 0x0027, /* Monocases to self */ + 0x0028, /* Monocases to self */ + 0x0029, /* Monocases to self */ + 0x002A, /* Monocases to self */ + 0x002B, /* Monocases to self */ + 0x002C, /* Monocases to self */ + 0x002D, /* Monocases to self */ + 0x002E, /* Monocases to self */ + 0x002F, /* Monocases to self */ + 0x0030, /* Monocases to self */ + 0x0031, /* Monocases to self */ + 0x0032, /* Monocases to self */ + 0x0033, /* Monocases to self */ + 0x0034, /* Monocases to self */ + 0x0035, /* Monocases to self */ + 0x0036, /* Monocases to self */ + 0x0037, /* Monocases to self */ + 0x0038, /* Monocases to self */ + 0x0039, /* Monocases to self */ + 0x003A, /* Monocases to self */ + 0x003B, /* Monocases to self */ + 0x003C, /* Monocases to self */ + 0x003D, /* Monocases to self */ + 0x003E, /* Monocases to self */ + 0x003F, /* Monocases to self */ + 0x0040, /* Monocases to self */ + 0x0061, /* LATIN LETTER A */ + 0x0062, /* LATIN LETTER B */ + 0x0063, /* LATIN LETTER C */ + 0x0064, /* LATIN LETTER D */ + 0x0065, /* LATIN LETTER E */ + 0x0066, /* LATIN LETTER F */ + 0x0067, /* LATIN LETTER G */ + 0x0068, /* LATIN LETTER H */ + 0x0069, /* LATIN LETTER I */ + 0x006A, /* LATIN LETTER J */ + 0x006B, /* LATIN LETTER K */ + 0x006C, /* LATIN LETTER L */ + 0x006D, /* LATIN LETTER M */ + 0x006E, /* LATIN LETTER N */ + 0x006F, /* LATIN LETTER O */ + 0x0070, /* LATIN LETTER P */ + 0x0071, /* LATIN LETTER Q */ + 0x0072, /* LATIN LETTER R */ + 0x0073, /* LATIN LETTER S */ + 0x0074, /* LATIN LETTER T */ + 0x0075, /* LATIN LETTER U */ + 0x0076, /* LATIN LETTER V */ + 0x0077, /* LATIN LETTER W */ + 0x0078, /* LATIN LETTER X */ + 0x0079, /* LATIN LETTER Y */ + 0x007A, /* LATIN LETTER Z */ + 0x005B, /* Monocases to self */ + 0x005C, /* Monocases to self */ + 0x005D, /* Monocases to self */ + 0x005E, /* Monocases to self */ + 0x005F, /* Monocases to self */ + 0x0060, /* Monocases to self */ + 0x0061, /* Monocases to self */ + 0x0062, /* Monocases to self */ + 0x0063, /* Monocases to self */ + 0x0064, /* Monocases to self */ + 0x0065, /* Monocases to self */ + 0x0066, /* Monocases to self */ + 0x0067, /* Monocases to self */ + 0x0068, /* Monocases to self */ + 0x0069, /* Monocases to self */ + 0x006A, /* Monocases to self */ + 0x006B, /* Monocases to self */ + 0x006C, /* Monocases to self */ + 0x006D, /* Monocases to self */ + 0x006E, /* Monocases to self */ + 0x006F, /* Monocases to self */ + 0x0070, /* Monocases to self */ + 0x0071, /* Monocases to self */ + 0x0072, /* Monocases to self */ + 0x0073, /* Monocases to self */ + 0x0074, /* Monocases to self */ + 0x0075, /* Monocases to self */ + 0x0076, /* Monocases to self */ + 0x0077, /* Monocases to self */ + 0x0078, /* Monocases to self */ + 0x0079, /* Monocases to self */ + 0x007A, /* Monocases to self */ + 0x007B, /* Monocases to self */ + 0x007C, /* Monocases to self */ + 0x007D, /* Monocases to self */ + 0x007E, /* Monocases to self */ + 0x007F, /* Monocases to self */ + 0x0080, /* Monocases to self */ + 0x0081, /* Monocases to self */ + 0x0082, /* Monocases to self */ + 0x0083, /* Monocases to self */ + 0x0084, /* Monocases to self */ + 0x0085, /* Monocases to self */ + 0x0086, /* Monocases to self */ + 0x0087, /* Monocases to self */ + 0x0088, /* Monocases to self */ + 0x0089, /* Monocases to self */ + 0x008A, /* Monocases to self */ + 0x008B, /* Monocases to self */ + 0x008C, /* Monocases to self */ + 0x008D, /* Monocases to self */ + 0x008E, /* Monocases to self */ + 0x008F, /* Monocases to self */ + 0x0090, /* Monocases to self */ + 0x0091, /* Monocases to self */ + 0x0092, /* Monocases to self */ + 0x0093, /* Monocases to self */ + 0x0094, /* Monocases to self */ + 0x0095, /* Monocases to self */ + 0x0096, /* Monocases to self */ + 0x0097, /* Monocases to self */ + 0x0098, /* Monocases to self */ + 0x0099, /* Monocases to self */ + 0x009A, /* Monocases to self */ + 0x009B, /* Monocases to self */ + 0x009C, /* Monocases to self */ + 0x009D, /* Monocases to self */ + 0x009E, /* Monocases to self */ + 0x009F, /* Monocases to self */ + 0x00A0, /* Monocases to self */ + 0x00A1, /* Monocases to self */ + 0x00A2, /* Monocases to self */ + 0x00A3, /* Monocases to self */ + 0x00A4, /* Monocases to self */ + 0x00A5, /* Monocases to self */ + 0x00A6, /* Monocases to self */ + 0x00A7, /* Monocases to self */ + 0x00A8, /* Monocases to self */ + 0x00A9, /* Monocases to self */ + 0x00AA, /* Monocases to self */ + 0x00AB, /* Monocases to self */ + 0x00AC, /* Monocases to self */ + 0x00AD, /* Monocases to self */ + 0x00AE, /* Monocases to self */ + 0x00AF, /* Monocases to self */ + 0x00B0, /* Monocases to self */ + 0x00B1, /* Monocases to self */ + 0x00B2, /* Monocases to self */ + 0x00B3, /* Monocases to self */ + 0x00B4, /* Monocases to self */ + 0x00B5, /* Monocases to self */ + 0x00B6, /* Monocases to self */ + 0x00B7, /* Monocases to self */ + 0x00B8, /* Monocases to self */ + 0x00B9, /* Monocases to self */ + 0x00BA, /* Monocases to self */ + 0x00BB, /* Monocases to self */ + 0x00BC, /* Monocases to self */ + 0x00BD, /* Monocases to self */ + 0x00BE, /* Monocases to self */ + 0x00BF, /* Monocases to self */ + 0x00E0, /* LATIN LETTER A GRAVE */ + 0x00E1, /* LATIN LETTER A ACUTE */ + 0x00E2, /* LATIN LETTER A CIRCUMFLEX */ + 0x00E3, /* LATIN LETTER A TILDE */ + 0x00E4, /* LATIN LETTER A DIAERESIS */ + 0x00E5, /* LATIN LETTER A RING */ + 0x00E6, /* LATIN LETTER A E */ + 0x00E7, /* LATIN LETTER C CEDILLA */ + 0x00E8, /* LATIN LETTER E GRAVE */ + 0x00E9, /* LATIN LETTER E ACUTE */ + 0x00EA, /* LATIN LETTER E CIRCUMFLEX */ + 0x00EB, /* LATIN LETTER E DIAERESIS */ + 0x00EC, /* LATIN LETTER I GRAVE */ + 0x00ED, /* LATIN LETTER I ACUTE */ + 0x00EE, /* LATIN LETTER I CIRCUMFLEX */ + 0x00EF, /* LATIN LETTER I DIAERESIS */ + 0x00F0, /* LATIN LETTER ETH */ + 0x00F1, /* LATIN LETTER N TILDE */ + 0x00F2, /* LATIN LETTER O GRAVE */ + 0x00F3, /* LATIN LETTER O ACUTE */ + 0x00F4, /* LATIN LETTER O CIRCUMFLEX */ + 0x00F5, /* LATIN LETTER O TILDE */ + 0x00F6, /* LATIN LETTER O DIAERESIS */ + 0x00D7, /* Monocases to self */ + 0x00F8, /* LATIN LETTER O SLASH */ + 0x00F9, /* LATIN LETTER U GRAVE */ + 0x00FA, /* LATIN LETTER U ACUTE */ + 0x00FB, /* LATIN LETTER U CIRCUMFLEX */ + 0x00FC, /* LATIN LETTER U DIAERESIS */ + 0x00FD, /* LATIN LETTER Y ACUTE */ + 0x00FE, /* LATIN LETTER THORN */ + 0x00DF, /* Monocases to self */ + 0x00E0, /* Monocases to self */ + 0x00E1, /* Monocases to self */ + 0x00E2, /* Monocases to self */ + 0x00E3, /* Monocases to self */ + 0x00E4, /* Monocases to self */ + 0x00E5, /* Monocases to self */ + 0x00E6, /* Monocases to self */ + 0x00E7, /* Monocases to self */ + 0x00E8, /* Monocases to self */ + 0x00E9, /* Monocases to self */ + 0x00EA, /* Monocases to self */ + 0x00EB, /* Monocases to self */ + 0x00EC, /* Monocases to self */ + 0x00ED, /* Monocases to self */ + 0x00EE, /* Monocases to self */ + 0x00EF, /* Monocases to self */ + 0x00F0, /* Monocases to self */ + 0x00F1, /* Monocases to self */ + 0x00F2, /* Monocases to self */ + 0x00F3, /* Monocases to self */ + 0x00F4, /* Monocases to self */ + 0x00F5, /* Monocases to self */ + 0x00F6, /* Monocases to self */ + 0x00F7, /* Monocases to self */ + 0x00F8, /* Monocases to self */ + 0x00F9, /* Monocases to self */ + 0x00FA, /* Monocases to self */ + 0x00FB, /* Monocases to self */ + 0x00FC, /* Monocases to self */ + 0x00FD, /* Monocases to self */ + 0x00FE, /* Monocases to self */ + 0x00FF, /* Monocases to self */ + 0x0101, /* LATIN LETTER A MACRON */ + 0x0101, /* Monocases to self */ + 0x0103, /* LATIN LETTER A BREVE */ + 0x0103, /* Monocases to self */ + 0x0105, /* LATIN LETTER A OGONEK */ + 0x0105, /* Monocases to self */ + 0x0107, /* LATIN LETTER C ACUTE */ + 0x0107, /* Monocases to self */ + 0x0109, /* LATIN LETTER C CIRCUMFLEX */ + 0x0109, /* Monocases to self */ + 0x010B, /* LATIN LETTER C DOT */ + 0x010B, /* Monocases to self */ + 0x010D, /* LATIN LETTER C HACEK */ + 0x010D, /* Monocases to self */ + 0x010F, /* LATIN LETTER D HACEK */ + 0x010F, /* Monocases to self */ + 0x0111, /* LATIN LETTER D BAR */ + 0x0111, /* Monocases to self */ + 0x0113, /* LATIN LETTER E MACRON */ + 0x0113, /* Monocases to self */ + 0x0115, /* LATIN LETTER E BREVE */ + 0x0115, /* Monocases to self */ + 0x0117, /* LATIN LETTER E DOT */ + 0x0117, /* Monocases to self */ + 0x0119, /* LATIN LETTER E OGONEK */ + 0x0119, /* Monocases to self */ + 0x011B, /* LATIN LETTER E HACEK */ + 0x011B, /* Monocases to self */ + 0x011D, /* LATIN LETTER G CIRCUMFLEX */ + 0x011D, /* Monocases to self */ + 0x011F, /* LATIN LETTER G BREVE */ + 0x011F, /* Monocases to self */ + 0x0121, /* LATIN LETTER G DOT */ + 0x0121, /* Monocases to self */ + 0x0123, /* LATIN LETTER G CEDILLA */ + 0x0123, /* Monocases to self */ + 0x0125, /* LATIN LETTER H CIRCUMFLEX */ + 0x0125, /* Monocases to self */ + 0x0127, /* LATIN LETTER H BAR */ + 0x0127, /* Monocases to self */ + 0x0129, /* LATIN LETTER I TILDE */ + 0x0129, /* Monocases to self */ + 0x012B, /* LATIN LETTER I MACRON */ + 0x012B, /* Monocases to self */ + 0x012D, /* LATIN LETTER I BREVE */ + 0x012D, /* Monocases to self */ + 0x012F, /* LATIN LETTER I OGONEK */ + 0x012F, /* Monocases to self */ + 0x0069, /* LATIN LETTER I DOT */ + 0x0131, /* Monocases to self */ + 0x0133, /* LATIN LETTER I J */ + 0x0133, /* Monocases to self */ + 0x0135, /* LATIN LETTER J CIRCUMFLEX */ + 0x0135, /* Monocases to self */ + 0x0137, /* LATIN LETTER K CEDILLA */ + 0x0137, /* Monocases to self */ + 0x0138, /* Monocases to self */ + 0x013A, /* LATIN LETTER L ACUTE */ + 0x013A, /* Monocases to self */ + 0x013C, /* LATIN LETTER L CEDILLA */ + 0x013C, /* Monocases to self */ + 0x013E, /* LATIN LETTER L HACEK */ + 0x013E, /* Monocases to self */ + 0x0140, /* LATIN LETTER L WITH MIDDLE DOT */ + 0x0140, /* Monocases to self */ + 0x0142, /* LATIN LETTER L SLASH */ + 0x0142, /* Monocases to self */ + 0x0144, /* LATIN LETTER N ACUTE */ + 0x0144, /* Monocases to self */ + 0x0146, /* LATIN LETTER N CEDILLA */ + 0x0146, /* Monocases to self */ + 0x0148, /* LATIN LETTER N HACEK */ + 0x0148, /* Monocases to self */ + 0x0149, /* Monocases to self */ + 0x014B, /* LATIN LETTER ENG */ + 0x014B, /* Monocases to self */ + 0x014D, /* LATIN LETTER O MACRON */ + 0x014D, /* Monocases to self */ + 0x014F, /* LATIN LETTER O BREVE */ + 0x014F, /* Monocases to self */ + 0x0151, /* LATIN LETTER O DOUBLE ACUTE */ + 0x0151, /* Monocases to self */ + 0x0153, /* LATIN LETTER O E */ + 0x0153, /* Monocases to self */ + 0x0155, /* LATIN LETTER R ACUTE */ + 0x0155, /* Monocases to self */ + 0x0157, /* LATIN LETTER R CEDILLA */ + 0x0157, /* Monocases to self */ + 0x0159, /* LATIN LETTER R HACEK */ + 0x0159, /* Monocases to self */ + 0x015B, /* LATIN LETTER S ACUTE */ + 0x015B, /* Monocases to self */ + 0x015D, /* LATIN LETTER S CIRCUMFLEX */ + 0x015D, /* Monocases to self */ + 0x015F, /* LATIN LETTER S CEDILLA */ + 0x015F, /* Monocases to self */ + 0x0161, /* LATIN LETTER S HACEK */ + 0x0161, /* Monocases to self */ + 0x0163, /* LATIN LETTER T CEDILLA */ + 0x0163, /* Monocases to self */ + 0x0165, /* LATIN LETTER T HACEK */ + 0x0165, /* Monocases to self */ + 0x0167, /* LATIN LETTER T BAR */ + 0x0167, /* Monocases to self */ + 0x0169, /* LATIN LETTER U TILDE */ + 0x0169, /* Monocases to self */ + 0x016B, /* LATIN LETTER U MACRON */ + 0x016B, /* Monocases to self */ + 0x016D, /* LATIN LETTER U BREVE */ + 0x016D, /* Monocases to self */ + 0x016F, /* LATIN LETTER U RING */ + 0x016F, /* Monocases to self */ + 0x0171, /* LATIN LETTER U DOUBLE ACUTE */ + 0x0171, /* Monocases to self */ + 0x0173, /* LATIN LETTER U OGONEK */ + 0x0173, /* Monocases to self */ + 0x0175, /* LATIN LETTER W CIRCUMFLEX */ + 0x0175, /* Monocases to self */ + 0x0177, /* LATIN LETTER Y CIRCUMFLEX */ + 0x0177, /* Monocases to self */ + 0x00FF, /* LATIN LETTER Y DIAERESIS */ + 0x017A, /* LATIN LETTER Z ACUTE */ + 0x017A, /* Monocases to self */ + 0x017C, /* LATIN LETTER Z DOT */ + 0x017C, /* Monocases to self */ + 0x017E, /* LATIN LETTER Z HACEK */ + 0x017E, /* Monocases to self */ + 0x017F, /* Monocases to self */ + 0x0180, /* Monocases to self */ + 0x0253, /* LATIN LETTER B HOOK */ + 0x0183, /* LATIN LETTER B TOPBAR */ + 0x0183, /* Monocases to self */ + 0x0185, /* LATIN LETTER TONE SIX */ + 0x0185, /* Monocases to self */ + 0x0254, /* LATIN LETTER OPEN O */ + 0x0188, /* LATIN LETTER C HOOK */ + 0x0188, /* Monocases to self */ + 0x0256, /* LATIN LETTER AFRICAN D */ + 0x0257, /* LATIN LETTER D HOOK */ + 0x018C, /* LATIN LETTER D TOPBAR */ + 0x018C, /* Monocases to self */ + 0x018D, /* Monocases to self */ + 0x01DD, /* LATIN LETTER TURNED E */ + 0x0259, /* LATIN LETTER SCHWA */ + 0x025B, /* LATIN LETTER EPSILON */ + 0x0192, /* LATIN LETTER F HOOK */ + 0x0192, /* Monocases to self */ + 0x0260, /* LATIN LETTER G HOOK */ + 0x0263, /* LATIN LETTER GAMMA */ + 0x0195, /* Monocases to self */ + 0x0269, /* LATIN LETTER IOTA */ + 0x0268, /* LATIN LETTER BARRED I */ + 0x0199, /* LATIN LETTER K HOOK */ + 0x0199, /* Monocases to self */ + 0x019A, /* Monocases to self */ + 0x019B, /* Monocases to self */ + 0x026F, /* LATIN LETTER TURNED M */ + 0x0272, /* LATIN LETTER N HOOK */ + 0x019E, /* Monocases to self */ + 0x0275, /* LATIN LETTER BARRED O */ + 0x01A1, /* LATIN LETTER O HORN */ + 0x01A1, /* Monocases to self */ + 0x01A3, /* LATIN LETTER O I */ + 0x01A3, /* Monocases to self */ + 0x01A5, /* LATIN LETTER P HOOK */ + 0x01A5, /* Monocases to self */ + 0x01A6, /* Monocases to self */ + 0x01A8, /* LATIN LETTER TONE TWO */ + 0x01A8, /* Monocases to self */ + 0x0283, /* LATIN LETTER ESH */ + 0x01AA, /* Monocases to self */ + 0x01AB, /* Monocases to self */ + 0x01AD, /* LATIN LETTER T HOOK */ + 0x01AD, /* Monocases to self */ + 0x0288, /* LATIN LETTER T RETROFLEX HOOK */ + 0x01B0, /* LATIN LETTER U HORN */ + 0x01B0, /* Monocases to self */ + 0x028A, /* LATIN LETTER UPSILON */ + 0x028B, /* LATIN LETTER SCRIPT V */ + 0x01B4, /* LATIN LETTER Y HOOK */ + 0x01B4, /* Monocases to self */ + 0x01B6, /* LATIN LETTER Z BAR */ + 0x01B6, /* Monocases to self */ + 0x0292, /* LATIN LETTER YOGH */ + 0x01B9, /* LATIN LETTER REVERSED YOGH */ + 0x01B9, /* Monocases to self */ + 0x01BA, /* Monocases to self */ + 0x01BB, /* Monocases to self */ + 0x01BD, /* LATIN LETTER TONE FIVE */ + 0x01BD, /* Monocases to self */ + 0x01BE, /* Monocases to self */ + 0x01BF, /* Monocases to self */ + 0x01C0, /* Monocases to self */ + 0x01C1, /* Monocases to self */ + 0x01C2, /* Monocases to self */ + 0x01C3, /* Monocases to self */ + 0x01C6, /* LATIN LETTER D Z HACEK */ + 0x01C6, /* LATIN LETTER CAPITAL D SMALL Z HACEK */ + 0x01C6, /* Monocases to self */ + 0x01C9, /* LATIN LETTER CAPITAL L CAPTIAL J */ + 0x01C9, /* LATIN LETTER CAPITAL L SMALL J */ + 0x01C9, /* Monocases to self */ + 0x01CC, /* LATIN LETTER CAPITAL N CAPITAL J */ + 0x01CC, /* LATIN LETTER CAPITAL N SMALL J */ + 0x01CC, /* Monocases to self */ + 0x01CE, /* LATIN LETTER A HACEK */ + 0x01CE, /* Monocases to self */ + 0x01D0, /* LATIN LETTER I HACEK */ + 0x01D0, /* Monocases to self */ + 0x01D2, /* LATIN LETTER O HACEK */ + 0x01D2, /* Monocases to self */ + 0x01D4, /* LATIN LETTER U HACEK */ + 0x01D4, /* Monocases to self */ + 0x01D6, /* LATIN LETTER U DIAERESIS MACRON */ + 0x01D6, /* Monocases to self */ + 0x01D8, /* LATIN LETTER U DIAERESIS ACUTE */ + 0x01D8, /* Monocases to self */ + 0x01DA, /* LATIN LETTER U DIAERESIS HACEK */ + 0x01DA, /* Monocases to self */ + 0x01DC, /* LATIN LETTER U DIAERESIS GRAVE */ + 0x01DC, /* Monocases to self */ + 0x01DD, /* Monocases to self */ + 0x01DF, /* LATIN LETTER A DIAERESIS MACRON */ + 0x01DF, /* Monocases to self */ + 0x01E1, /* LATIN LETTER A DOT MACRON */ + 0x01E1, /* Monocases to self */ + 0x01E3, /* LATIN LETTER A E MACRON */ + 0x01E3, /* Monocases to self */ + 0x01E5, /* LATIN LETTER G BAR */ + 0x01E5, /* Monocases to self */ + 0x01E7, /* LATIN LETTER G HACEK */ + 0x01E7, /* Monocases to self */ + 0x01E9, /* LATIN LETTER K HACEK */ + 0x01E9, /* Monocases to self */ + 0x01EB, /* LATIN LETTER O OGONEK */ + 0x01EB, /* Monocases to self */ + 0x01ED, /* LATIN LETTER O OGONEK MACRON */ + 0x01ED, /* Monocases to self */ + 0x01EF, /* LATIN LETTER YOGH HACEK */ + 0x01EF, /* Monocases to self */ + 0x01F0, /* Monocases to self */ + 0x01F1, /* Monocases to self */ + 0x01F2, /* Monocases to self */ + 0x01F3, /* Monocases to self */ + 0x01F4, /* Monocases to self */ + 0x01F5, /* Monocases to self */ + 0x01F6, /* Monocases to self */ + 0x01F7, /* Monocases to self */ + 0x01F8, /* Monocases to self */ + 0x01F9, /* Monocases to self */ + 0x01FA, /* Monocases to self */ + 0x01FB, /* Monocases to self */ + 0x01FC, /* Monocases to self */ + 0x01FD, /* Monocases to self */ + 0x01FE, /* Monocases to self */ + 0x01FF, /* Monocases to self */ + 0x0200, /* Monocases to self */ + 0x0201, /* Monocases to self */ + 0x0202, /* Monocases to self */ + 0x0203, /* Monocases to self */ + 0x0204, /* Monocases to self */ + 0x0205, /* Monocases to self */ + 0x0206, /* Monocases to self */ + 0x0207, /* Monocases to self */ + 0x0208, /* Monocases to self */ + 0x0209, /* Monocases to self */ + 0x020A, /* Monocases to self */ + 0x020B, /* Monocases to self */ + 0x020C, /* Monocases to self */ + 0x020D, /* Monocases to self */ + 0x020E, /* Monocases to self */ + 0x020F, /* Monocases to self */ + 0x0210, /* Monocases to self */ + 0x0211, /* Monocases to self */ + 0x0212, /* Monocases to self */ + 0x0213, /* Monocases to self */ + 0x0214, /* Monocases to self */ + 0x0215, /* Monocases to self */ + 0x0216, /* Monocases to self */ + 0x0217, /* Monocases to self */ + 0x0218, /* Monocases to self */ + 0x0219, /* Monocases to self */ + 0x021A, /* Monocases to self */ + 0x021B, /* Monocases to self */ + 0x021C, /* Monocases to self */ + 0x021D, /* Monocases to self */ + 0x021E, /* Monocases to self */ + 0x021F, /* Monocases to self */ + 0x0220, /* Monocases to self */ + 0x0221, /* Monocases to self */ + 0x0222, /* Monocases to self */ + 0x0223, /* Monocases to self */ + 0x0224, /* Monocases to self */ + 0x0225, /* Monocases to self */ + 0x0226, /* Monocases to self */ + 0x0227, /* Monocases to self */ + 0x0228, /* Monocases to self */ + 0x0229, /* Monocases to self */ + 0x022A, /* Monocases to self */ + 0x022B, /* Monocases to self */ + 0x022C, /* Monocases to self */ + 0x022D, /* Monocases to self */ + 0x022E, /* Monocases to self */ + 0x022F, /* Monocases to self */ + 0x0230, /* Monocases to self */ + 0x0231, /* Monocases to self */ + 0x0232, /* Monocases to self */ + 0x0233, /* Monocases to self */ + 0x0234, /* Monocases to self */ + 0x0235, /* Monocases to self */ + 0x0236, /* Monocases to self */ + 0x0237, /* Monocases to self */ + 0x0238, /* Monocases to self */ + 0x0239, /* Monocases to self */ + 0x023A, /* Monocases to self */ + 0x023B, /* Monocases to self */ + 0x023C, /* Monocases to self */ + 0x023D, /* Monocases to self */ + 0x023E, /* Monocases to self */ + 0x023F, /* Monocases to self */ + 0x0240, /* Monocases to self */ + 0x0241, /* Monocases to self */ + 0x0242, /* Monocases to self */ + 0x0243, /* Monocases to self */ + 0x0244, /* Monocases to self */ + 0x0245, /* Monocases to self */ + 0x0246, /* Monocases to self */ + 0x0247, /* Monocases to self */ + 0x0248, /* Monocases to self */ + 0x0249, /* Monocases to self */ + 0x024A, /* Monocases to self */ + 0x024B, /* Monocases to self */ + 0x024C, /* Monocases to self */ + 0x024D, /* Monocases to self */ + 0x024E, /* Monocases to self */ + 0x024F, /* Monocases to self */ + 0x0250, /* Monocases to self */ + 0x0251, /* Monocases to self */ + 0x0252, /* Monocases to self */ + 0x0253, /* Monocases to self */ + 0x0254, /* Monocases to self */ + 0x0255, /* Monocases to self */ + 0x0256, /* Monocases to self */ + 0x0257, /* Monocases to self */ + 0x0258, /* Monocases to self */ + 0x0259, /* Monocases to self */ + 0x025A, /* Monocases to self */ + 0x025B, /* Monocases to self */ + 0x025C, /* Monocases to self */ + 0x025D, /* Monocases to self */ + 0x025E, /* Monocases to self */ + 0x025F, /* Monocases to self */ + 0x0260, /* Monocases to self */ + 0x0261, /* Monocases to self */ + 0x0262, /* Monocases to self */ + 0x0263, /* Monocases to self */ + 0x0264, /* Monocases to self */ + 0x0265, /* Monocases to self */ + 0x0266, /* Monocases to self */ + 0x0267, /* Monocases to self */ + 0x0268, /* Monocases to self */ + 0x0269, /* Monocases to self */ + 0x026A, /* Monocases to self */ + 0x026B, /* Monocases to self */ + 0x026C, /* Monocases to self */ + 0x026D, /* Monocases to self */ + 0x026E, /* Monocases to self */ + 0x026F, /* Monocases to self */ + 0x0270, /* Monocases to self */ + 0x0271, /* Monocases to self */ + 0x0272, /* Monocases to self */ + 0x0273, /* Monocases to self */ + 0x0274, /* Monocases to self */ + 0x0275, /* Monocases to self */ + 0x0276, /* Monocases to self */ + 0x0277, /* Monocases to self */ + 0x0278, /* Monocases to self */ + 0x0279, /* Monocases to self */ + 0x027A, /* Monocases to self */ + 0x027B, /* Monocases to self */ + 0x027C, /* Monocases to self */ + 0x027D, /* Monocases to self */ + 0x027E, /* Monocases to self */ + 0x027F, /* Monocases to self */ + 0x0280, /* Monocases to self */ + 0x0281, /* Monocases to self */ + 0x0282, /* Monocases to self */ + 0x0283, /* Monocases to self */ + 0x0284, /* Monocases to self */ + 0x0285, /* Monocases to self */ + 0x0286, /* Monocases to self */ + 0x0287, /* Monocases to self */ + 0x0288, /* Monocases to self */ + 0x0289, /* Monocases to self */ + 0x028A, /* Monocases to self */ + 0x028B, /* Monocases to self */ + 0x028C, /* Monocases to self */ + 0x028D, /* Monocases to self */ + 0x028E, /* Monocases to self */ + 0x028F, /* Monocases to self */ + 0x0290, /* Monocases to self */ + 0x0291, /* Monocases to self */ + 0x0292, /* Monocases to self */ + 0x0293, /* Monocases to self */ + 0x0294, /* Monocases to self */ + 0x0295, /* Monocases to self */ + 0x0296, /* Monocases to self */ + 0x0297, /* Monocases to self */ + 0x0298, /* Monocases to self */ + 0x0299, /* Monocases to self */ + 0x029A, /* Monocases to self */ + 0x029B, /* Monocases to self */ + 0x029C, /* Monocases to self */ + 0x029D, /* Monocases to self */ + 0x029E, /* Monocases to self */ + 0x029F, /* Monocases to self */ + 0x02A0, /* Monocases to self */ + 0x02A1, /* Monocases to self */ + 0x02A2, /* Monocases to self */ + 0x02A3, /* Monocases to self */ + 0x02A4, /* Monocases to self */ + 0x02A5, /* Monocases to self */ + 0x02A6, /* Monocases to self */ + 0x02A7, /* Monocases to self */ + 0x02A8, /* Monocases to self */ + 0x02A9, /* Monocases to self */ + 0x02AA, /* Monocases to self */ + 0x02AB, /* Monocases to self */ + 0x02AC, /* Monocases to self */ + 0x02AD, /* Monocases to self */ + 0x02AE, /* Monocases to self */ + 0x02AF, /* Monocases to self */ + 0x02B0, /* Monocases to self */ + 0x02B1, /* Monocases to self */ + 0x02B2, /* Monocases to self */ + 0x02B3, /* Monocases to self */ + 0x02B4, /* Monocases to self */ + 0x02B5, /* Monocases to self */ + 0x02B6, /* Monocases to self */ + 0x02B7, /* Monocases to self */ + 0x02B8, /* Monocases to self */ + 0x02B9, /* Monocases to self */ + 0x02BA, /* Monocases to self */ + 0x02BB, /* Monocases to self */ + 0x02BC, /* Monocases to self */ + 0x02BD, /* Monocases to self */ + 0x02BE, /* Monocases to self */ + 0x02BF, /* Monocases to self */ + 0x02C0, /* Monocases to self */ + 0x02C1, /* Monocases to self */ + 0x02C2, /* Monocases to self */ + 0x02C3, /* Monocases to self */ + 0x02C4, /* Monocases to self */ + 0x02C5, /* Monocases to self */ + 0x02C6, /* Monocases to self */ + 0x02C7, /* Monocases to self */ + 0x02C8, /* Monocases to self */ + 0x02C9, /* Monocases to self */ + 0x02CA, /* Monocases to self */ + 0x02CB, /* Monocases to self */ + 0x02CC, /* Monocases to self */ + 0x02CD, /* Monocases to self */ + 0x02CE, /* Monocases to self */ + 0x02CF, /* Monocases to self */ + 0x02D0, /* Monocases to self */ + 0x02D1, /* Monocases to self */ + 0x02D2, /* Monocases to self */ + 0x02D3, /* Monocases to self */ + 0x02D4, /* Monocases to self */ + 0x02D5, /* Monocases to self */ + 0x02D6, /* Monocases to self */ + 0x02D7, /* Monocases to self */ + 0x02D8, /* Monocases to self */ + 0x02D9, /* Monocases to self */ + 0x02DA, /* Monocases to self */ + 0x02DB, /* Monocases to self */ + 0x02DC, /* Monocases to self */ + 0x02DD, /* Monocases to self */ + 0x02DE, /* Monocases to self */ + 0x02DF, /* Monocases to self */ + 0x02E0, /* Monocases to self */ + 0x02E1, /* Monocases to self */ + 0x02E2, /* Monocases to self */ + 0x02E3, /* Monocases to self */ + 0x02E4, /* Monocases to self */ + 0x02E5, /* Monocases to self */ + 0x02E6, /* Monocases to self */ + 0x02E7, /* Monocases to self */ + 0x02E8, /* Monocases to self */ + 0x02E9, /* Monocases to self */ + 0x02EA, /* Monocases to self */ + 0x02EB, /* Monocases to self */ + 0x02EC, /* Monocases to self */ + 0x02ED, /* Monocases to self */ + 0x02EE, /* Monocases to self */ + 0x02EF, /* Monocases to self */ + 0x02F0, /* Monocases to self */ + 0x02F1, /* Monocases to self */ + 0x02F2, /* Monocases to self */ + 0x02F3, /* Monocases to self */ + 0x02F4, /* Monocases to self */ + 0x02F5, /* Monocases to self */ + 0x02F6, /* Monocases to self */ + 0x02F7, /* Monocases to self */ + 0x02F8, /* Monocases to self */ + 0x02F9, /* Monocases to self */ + 0x02FA, /* Monocases to self */ + 0x02FB, /* Monocases to self */ + 0x02FC, /* Monocases to self */ + 0x02FD, /* Monocases to self */ + 0x02FE, /* Monocases to self */ + 0x02FF, /* Monocases to self */ + 0x0300, /* Monocases to self */ + 0x0301, /* Monocases to self */ + 0x0302, /* Monocases to self */ + 0x0303, /* Monocases to self */ + 0x0304, /* Monocases to self */ + 0x0305, /* Monocases to self */ + 0x0306, /* Monocases to self */ + 0x0307, /* Monocases to self */ + 0x0308, /* Monocases to self */ + 0x0309, /* Monocases to self */ + 0x030A, /* Monocases to self */ + 0x030B, /* Monocases to self */ + 0x030C, /* Monocases to self */ + 0x030D, /* Monocases to self */ + 0x030E, /* Monocases to self */ + 0x030F, /* Monocases to self */ + 0x0310, /* Monocases to self */ + 0x0311, /* Monocases to self */ + 0x0312, /* Monocases to self */ + 0x0313, /* Monocases to self */ + 0x0314, /* Monocases to self */ + 0x0315, /* Monocases to self */ + 0x0316, /* Monocases to self */ + 0x0317, /* Monocases to self */ + 0x0318, /* Monocases to self */ + 0x0319, /* Monocases to self */ + 0x031A, /* Monocases to self */ + 0x031B, /* Monocases to self */ + 0x031C, /* Monocases to self */ + 0x031D, /* Monocases to self */ + 0x031E, /* Monocases to self */ + 0x031F, /* Monocases to self */ + 0x0320, /* Monocases to self */ + 0x0321, /* Monocases to self */ + 0x0322, /* Monocases to self */ + 0x0323, /* Monocases to self */ + 0x0324, /* Monocases to self */ + 0x0325, /* Monocases to self */ + 0x0326, /* Monocases to self */ + 0x0327, /* Monocases to self */ + 0x0328, /* Monocases to self */ + 0x0329, /* Monocases to self */ + 0x032A, /* Monocases to self */ + 0x032B, /* Monocases to self */ + 0x032C, /* Monocases to self */ + 0x032D, /* Monocases to self */ + 0x032E, /* Monocases to self */ + 0x032F, /* Monocases to self */ + 0x0330, /* Monocases to self */ + 0x0331, /* Monocases to self */ + 0x0332, /* Monocases to self */ + 0x0333, /* Monocases to self */ + 0x0334, /* Monocases to self */ + 0x0335, /* Monocases to self */ + 0x0336, /* Monocases to self */ + 0x0337, /* Monocases to self */ + 0x0338, /* Monocases to self */ + 0x0339, /* Monocases to self */ + 0x033A, /* Monocases to self */ + 0x033B, /* Monocases to self */ + 0x033C, /* Monocases to self */ + 0x033D, /* Monocases to self */ + 0x033E, /* Monocases to self */ + 0x033F, /* Monocases to self */ + 0x0340, /* Monocases to self */ + 0x0341, /* Monocases to self */ + 0x0342, /* Monocases to self */ + 0x0343, /* Monocases to self */ + 0x0344, /* Monocases to self */ + 0x0345, /* Monocases to self */ + 0x0346, /* Monocases to self */ + 0x0347, /* Monocases to self */ + 0x0348, /* Monocases to self */ + 0x0349, /* Monocases to self */ + 0x034A, /* Monocases to self */ + 0x034B, /* Monocases to self */ + 0x034C, /* Monocases to self */ + 0x034D, /* Monocases to self */ + 0x034E, /* Monocases to self */ + 0x034F, /* Monocases to self */ + 0x0350, /* Monocases to self */ + 0x0351, /* Monocases to self */ + 0x0352, /* Monocases to self */ + 0x0353, /* Monocases to self */ + 0x0354, /* Monocases to self */ + 0x0355, /* Monocases to self */ + 0x0356, /* Monocases to self */ + 0x0357, /* Monocases to self */ + 0x0358, /* Monocases to self */ + 0x0359, /* Monocases to self */ + 0x035A, /* Monocases to self */ + 0x035B, /* Monocases to self */ + 0x035C, /* Monocases to self */ + 0x035D, /* Monocases to self */ + 0x035E, /* Monocases to self */ + 0x035F, /* Monocases to self */ + 0x0360, /* Monocases to self */ + 0x0361, /* Monocases to self */ + 0x0362, /* Monocases to self */ + 0x0363, /* Monocases to self */ + 0x0364, /* Monocases to self */ + 0x0365, /* Monocases to self */ + 0x0366, /* Monocases to self */ + 0x0367, /* Monocases to self */ + 0x0368, /* Monocases to self */ + 0x0369, /* Monocases to self */ + 0x036A, /* Monocases to self */ + 0x036B, /* Monocases to self */ + 0x036C, /* Monocases to self */ + 0x036D, /* Monocases to self */ + 0x036E, /* Monocases to self */ + 0x036F, /* Monocases to self */ + 0x0370, /* Monocases to self */ + 0x0371, /* Monocases to self */ + 0x0372, /* Monocases to self */ + 0x0373, /* Monocases to self */ + 0x0374, /* Monocases to self */ + 0x0375, /* Monocases to self */ + 0x0376, /* Monocases to self */ + 0x0377, /* Monocases to self */ + 0x0378, /* Monocases to self */ + 0x0379, /* Monocases to self */ + 0x037A, /* Monocases to self */ + 0x037B, /* Monocases to self */ + 0x037C, /* Monocases to self */ + 0x037D, /* Monocases to self */ + 0x037E, /* Monocases to self */ + 0x037F, /* Monocases to self */ + 0x0380, /* Monocases to self */ + 0x0381, /* Monocases to self */ + 0x0382, /* Monocases to self */ + 0x0383, /* Monocases to self */ + 0x0384, /* Monocases to self */ + 0x0385, /* Monocases to self */ + 0x03AC, /* GREEK LETTER ALPHA TONOS */ + 0x0387, /* Monocases to self */ + 0x03AD, /* GREEK LETTER EPSILON TONOS */ + 0x03AE, /* GREEK LETTER ETA TONOS */ + 0x03AF, /* GREEK LETTER IOTA TONOS */ + 0x038B, /* Monocases to self */ + 0x03CC, /* GREEK LETTER OMICRON TONOS */ + 0x038D, /* Monocases to self */ + 0x03CD, /* GREEK LETTER UPSILON TONOS */ + 0x03CE, /* GREEK LETTER OMEGA TONOS */ + 0x0390, /* Monocases to self */ + 0x03B1, /* GREEK LETTER ALPHA */ + 0x03B2, /* GREEK LETTER BETA */ + 0x03B3, /* GREEK LETTER GAMMA */ + 0x03B4, /* GREEK LETTER DELTA */ + 0x03B5, /* GREEK LETTER EPSILON */ + 0x03B6, /* GREEK LETTER ZETA */ + 0x03B7, /* GREEK LETTER ETA */ + 0x03B8, /* GREEK LETTER THETA */ + 0x03B9, /* GREEK LETTER IOTA */ + 0x03BA, /* GREEK LETTER KAPPA */ + 0x03BB, /* GREEK LETTER LAMBDA */ + 0x03BC, /* GREEK LETTER MU */ + 0x03BD, /* GREEK LETTER NU */ + 0x03BE, /* GREEK LETTER Xl */ + 0x03BF, /* GREEK LETTER OMICRON */ + 0x03C0, /* GREEK LETTER PI */ + 0x03C1, /* GREEK LETTER RHO */ + 0x03A2, /* Monocases to self */ + 0x03C3, /* GREEK LETTER SIGMA */ + 0x03C4, /* GREEK LETTER TAU */ + 0x03C5, /* GREEK LETTER UPSILON */ + 0x03C6, /* GREEK LETTER PHI */ + 0x03C7, /* GREEK LETTER CHI */ + 0x03C8, /* GREEK LETTER PSI */ + 0x03C9, /* GREEK LETTER OMEGA */ + 0x03CA, /* GREEK LETTER IOTA DIAERESIS */ + 0x03CB, /* GREEK LETTER UPSILON DIAERESIS */ + 0x03AC, /* Monocases to self */ + 0x03AD, /* Monocases to self */ + 0x03AE, /* Monocases to self */ + 0x03AF, /* Monocases to self */ + 0x03B0, /* Monocases to self */ + 0x03B1, /* Monocases to self */ + 0x03B2, /* Monocases to self */ + 0x03B3, /* Monocases to self */ + 0x03B4, /* Monocases to self */ + 0x03B5, /* Monocases to self */ + 0x03B6, /* Monocases to self */ + 0x03B7, /* Monocases to self */ + 0x03B8, /* Monocases to self */ + 0x03B9, /* Monocases to self */ + 0x03BA, /* Monocases to self */ + 0x03BB, /* Monocases to self */ + 0x03BC, /* Monocases to self */ + 0x03BD, /* Monocases to self */ + 0x03BE, /* Monocases to self */ + 0x03BF, /* Monocases to self */ + 0x03C0, /* Monocases to self */ + 0x03C1, /* Monocases to self */ + 0x03C2, /* Monocases to self */ + 0x03C3, /* Monocases to self */ + 0x03C4, /* Monocases to self */ + 0x03C5, /* Monocases to self */ + 0x03C6, /* Monocases to self */ + 0x03C7, /* Monocases to self */ + 0x03C8, /* Monocases to self */ + 0x03C9, /* Monocases to self */ + 0x03CA, /* Monocases to self */ + 0x03CB, /* Monocases to self */ + 0x03CC, /* Monocases to self */ + 0x03CD, /* Monocases to self */ + 0x03CE, /* Monocases to self */ + 0x03CF, /* Monocases to self */ + 0x03D0, /* Monocases to self */ + 0x03D1, /* Monocases to self */ + 0x03C5, /* GREEK LETTER UPSILON HOOK */ + 0x03CD, /* GREEK LETTER UPSILON HOOK TONOS */ + 0x03CB, /* GREEK LETTER UPSILON HOOK DIAERESIS */ + 0x03D5, /* Monocases to self */ + 0x03D6, /* Monocases to self */ + 0x03D7, /* Monocases to self */ + 0x03D8, /* Monocases to self */ + 0x03D9, /* Monocases to self */ + 0x03DB, /* GREEK LETTER STIGMA */ + 0x03DB, /* Monocases to self */ + 0x03DD, /* GREEK LETTER DIGAMMA */ + 0x03DD, /* Monocases to self */ + 0x03DF, /* GREEK LETTER KOPPA */ + 0x03DF, /* Monocases to self */ + 0x03E1, /* GREEK LETTER SAMPI */ + 0x03E1, /* Monocases to self */ + 0x03E3, /* GREEK LETTER SHEI */ + 0x03E3, /* Monocases to self */ + 0x03E5, /* GREEK LETTER FEI */ + 0x03E5, /* Monocases to self */ + 0x03E7, /* GREEK LETTER KHEI */ + 0x03E7, /* Monocases to self */ + 0x03E9, /* GREEK LETTER HORI */ + 0x03E9, /* Monocases to self */ + 0x03EB, /* GREEK LETTER GANGIA */ + 0x03EB, /* Monocases to self */ + 0x03ED, /* GREEK LETTER SHIMA */ + 0x03ED, /* Monocases to self */ + 0x03EF, /* GREEK LETTER DEI */ + 0x03EF, /* Monocases to self */ + 0x03F0, /* Monocases to self */ + 0x03F1, /* Monocases to self */ + 0x03F2, /* Monocases to self */ + 0x03F3, /* Monocases to self */ + 0x03F4, /* Monocases to self */ + 0x03F5, /* Monocases to self */ + 0x03F6, /* Monocases to self */ + 0x03F7, /* Monocases to self */ + 0x03F8, /* Monocases to self */ + 0x03F9, /* Monocases to self */ + 0x03FA, /* Monocases to self */ + 0x03FB, /* Monocases to self */ + 0x03FC, /* Monocases to self */ + 0x03FD, /* Monocases to self */ + 0x03FE, /* Monocases to self */ + 0x03FF, /* Monocases to self */ + 0x0400, /* Monocases to self */ + 0x0451, /* CYRILLIC LETTER IO */ + 0x0452, /* CYRILLIC LETTER DJE */ + 0x0453, /* CYRILLIC LETTER GJE */ + 0x0454, /* CYRILLIC LETTER E */ + 0x0455, /* CYRILLIC LETTER DZE */ + 0x0456, /* CYRILLIC LETTER I */ + 0x0457, /* CYRILLIC LETTER YI */ + 0x0458, /* CYRILLIC LETTER JE */ + 0x0459, /* CYRILLIC LETTER LJE */ + 0x045A, /* CYRILLIC LETTER NJE */ + 0x045B, /* CYRILLIC LETTER TSHE */ + 0x045C, /* CYRILLIC LETTER KJE */ + 0x040D, /* Monocases to self */ + 0x045E, /* CYRILLIC LETTER SHORT U */ + 0x045F, /* CYRILLIC LETTER DZHE */ + 0x0430, /* CYRILLIC LETTER A */ + 0x0431, /* CYRILLIC LETTER BE */ + 0x0432, /* CYRILLIC LETTER VE */ + 0x0433, /* CYRILLIC LETTER GE */ + 0x0434, /* CYRILLIC LETTER DE */ + 0x0435, /* CYRILLIC LETTER IE */ + 0x0436, /* CYRILLIC LETTER ZHE */ + 0x0437, /* CYRILLIC LETTER ZE */ + 0x0438, /* CYRILLIC LETTER II */ + 0x0439, /* CYRILLIC LETTER SHORT II */ + 0x043A, /* CYRILLIC LETTER KA */ + 0x043B, /* CYRILLIC LETTER EL */ + 0x043C, /* CYRILLIC LETTER EM */ + 0x043D, /* CYRILLIC LETTER EN */ + 0x043E, /* CYRILLIC LETTER O */ + 0x043F, /* CYRILLIC LETTER PE */ + 0x0440, /* CYRILLIC LETTER ER */ + 0x0441, /* CYRILLIC LETTER ES */ + 0x0442, /* CYRILLIC LETTER TE */ + 0x0443, /* CYRILLIC LETTER U */ + 0x0444, /* CYRILLIC LETTER EF */ + 0x0445, /* CYRILLIC LETTER KHA */ + 0x0446, /* CYRILLIC LETTER TSE */ + 0x0447, /* CYRILLIC LETTER CHE */ + 0x0448, /* CYRILLIC LETTER SHA */ + 0x0449, /* CYRILLIC LETTER SHCHA */ + 0x044A, /* CYRILLIC LETTER HARD SIGN */ + 0x044B, /* CYRILLIC LETTER YERI */ + 0x044C, /* CYRILLIC LETTER SOFT SIGN */ + 0x044D, /* CYRILLIC LETTER REVERSED E */ + 0x044E, /* CYRILLIC LETTER IU */ + 0x044F, /* CYRILLIC LETTER IA */ + 0x0430, /* Monocases to self */ + 0x0431, /* Monocases to self */ + 0x0432, /* Monocases to self */ + 0x0433, /* Monocases to self */ + 0x0434, /* Monocases to self */ + 0x0435, /* Monocases to self */ + 0x0436, /* Monocases to self */ + 0x0437, /* Monocases to self */ + 0x0438, /* Monocases to self */ + 0x0439, /* Monocases to self */ + 0x043A, /* Monocases to self */ + 0x043B, /* Monocases to self */ + 0x043C, /* Monocases to self */ + 0x043D, /* Monocases to self */ + 0x043E, /* Monocases to self */ + 0x043F, /* Monocases to self */ + 0x0440, /* Monocases to self */ + 0x0441, /* Monocases to self */ + 0x0442, /* Monocases to self */ + 0x0443, /* Monocases to self */ + 0x0444, /* Monocases to self */ + 0x0445, /* Monocases to self */ + 0x0446, /* Monocases to self */ + 0x0447, /* Monocases to self */ + 0x0448, /* Monocases to self */ + 0x0449, /* Monocases to self */ + 0x044A, /* Monocases to self */ + 0x044B, /* Monocases to self */ + 0x044C, /* Monocases to self */ + 0x044D, /* Monocases to self */ + 0x044E, /* Monocases to self */ + 0x044F, /* Monocases to self */ + 0x0450, /* Monocases to self */ + 0x0451, /* Monocases to self */ + 0x0452, /* Monocases to self */ + 0x0453, /* Monocases to self */ + 0x0454, /* Monocases to self */ + 0x0455, /* Monocases to self */ + 0x0456, /* Monocases to self */ + 0x0457, /* Monocases to self */ + 0x0458, /* Monocases to self */ + 0x0459, /* Monocases to self */ + 0x045A, /* Monocases to self */ + 0x045B, /* Monocases to self */ + 0x045C, /* Monocases to self */ + 0x045D, /* Monocases to self */ + 0x045E, /* Monocases to self */ + 0x045F, /* Monocases to self */ + 0x0461, /* CYRILLIC LETTER OMEGA */ + 0x0461, /* Monocases to self */ + 0x0463, /* CYRILLIC LETTER YAT */ + 0x0463, /* Monocases to self */ + 0x0465, /* CYRILLIC LETTER IOTIFIED E */ + 0x0465, /* Monocases to self */ + 0x0467, /* CYRILLIC LETTER LITTLE YUS */ + 0x0467, /* Monocases to self */ + 0x0469, /* CYRILLIC LETTER IOTIFIED LITTLE YUS */ + 0x0469, /* Monocases to self */ + 0x046B, /* CYRILLIC LETTER BIG YUS */ + 0x046B, /* Monocases to self */ + 0x046D, /* CYRILLIC LETTER IOTIFIED BIG YUS */ + 0x046D, /* Monocases to self */ + 0x046F, /* CYRILLIC LETTER KSI */ + 0x046F, /* Monocases to self */ + 0x0471, /* CYRILLIC LETTER PSI */ + 0x0471, /* Monocases to self */ + 0x0473, /* CYRILLIC LETTER FITA */ + 0x0473, /* Monocases to self */ + 0x0475, /* CYRILLIC LETTER IZHITSA */ + 0x0475, /* Monocases to self */ + 0x0477, /* CYRILLIC LETTER IZHITSA DOUBLE GRAVE */ + 0x0477, /* Monocases to self */ + 0x0479, /* CYRILLIC LETTER UK DIGRAPH */ + 0x0479, /* Monocases to self */ + 0x047B, /* CYRILLIC LETTER ROUND OMEGA */ + 0x047B, /* Monocases to self */ + 0x047D, /* CYRILLIC LETTER OMEGA TITLO */ + 0x047D, /* Monocases to self */ + 0x047F, /* CYRILLIC LETTER OT */ + 0x047F, /* Monocases to self */ + 0x0481, /* CYRILLIC LETTER KOPPA */ + 0x0481, /* Monocases to self */ + 0x0482, /* Monocases to self */ + 0x0483, /* Monocases to self */ + 0x0484, /* Monocases to self */ + 0x0485, /* Monocases to self */ + 0x0486, /* Monocases to self */ + 0x0487, /* Monocases to self */ + 0x0488, /* Monocases to self */ + 0x0489, /* Monocases to self */ + 0x048A, /* Monocases to self */ + 0x048B, /* Monocases to self */ + 0x048C, /* Monocases to self */ + 0x048D, /* Monocases to self */ + 0x048E, /* Monocases to self */ + 0x048F, /* Monocases to self */ + 0x0491, /* CYRILLIC LETTER GE WITH UPTURN */ + 0x0491, /* Monocases to self */ + 0x0493, /* CYRILLIC LETTER GE BAR */ + 0x0493, /* Monocases to self */ + 0x0495, /* CYRILLIC LETTER GE HOOK */ + 0x0495, /* Monocases to self */ + 0x0497, /* CYRILLIC LETTER ZHE WITH RIGHT DESCENDER */ + 0x0497, /* Monocases to self */ + 0x0499, /* CYRILLIC LETTER ZE CEDILLA */ + 0x0499, /* Monocases to self */ + 0x049B, /* CYRILLIC LETTER KA WITH RIGHT DESCENDER */ + 0x049B, /* Monocases to self */ + 0x049D, /* CYRILLIC LETTER KA VERTICAL BAR */ + 0x049D, /* Monocases to self */ + 0x049F, /* CYRILLIC LETTER KA BAR */ + 0x049F, /* Monocases to self */ + 0x04A1, /* CYRILLIC LETTER REVERSED GE KA */ + 0x04A1, /* Monocases to self */ + 0x04A3, /* CYRILLIC LETTER EN WITH RIGHT DESCENDER */ + 0x04A3, /* Monocases to self */ + 0x04A5, /* CYRILLIC LETTER EN GE */ + 0x04A5, /* Monocases to self */ + 0x04A7, /* CYRILLIC LETTER PE HOOK */ + 0x04A7, /* Monocases to self */ + 0x04A9, /* CYRILLIC LETTER O HOOK */ + 0x04A9, /* Monocases to self */ + 0x04AB, /* CYRILLIC LETTER ES CEDILLA */ + 0x04AB, /* Monocases to self */ + 0x04AD, /* CYRILLIC LETTER TE WITH RIGHT DESCENDER */ + 0x04AD, /* Monocases to self */ + 0x04AF, /* CYRILLIC LETTER STRAIGHT U */ + 0x04AF, /* Monocases to self */ + 0x04B1, /* CYRILLIC LETTER STRAIGHT U BAR */ + 0x04B1, /* Monocases to self */ + 0x04B3, /* CYRILLIC LETTER KHA WITH RIGHT DESCENDER */ + 0x04B3, /* Monocases to self */ + 0x04B5, /* CYRILLIC LETTER TE TSE */ + 0x04B5, /* Monocases to self */ + 0x04B7, /* CYRILLIC LETTER CHE WITH RIGHT DESCENDER */ + 0x04B7, /* Monocases to self */ + 0x04B9, /* CYRILLIC LETTER CHE VERTICAL BAR */ + 0x04B9, /* Monocases to self */ + 0x04BB, /* CYRILLIC LETTER H */ + 0x04BB, /* Monocases to self */ + 0x04BD, /* CYRILLIC LETTER IE HOOK */ + 0x04BD, /* Monocases to self */ + 0x04BF, /* CYRILLIC LETTER IE HOOK OGONEK */ + 0x04BF, /* Monocases to self */ + 0x04C0, /* Monocases to self */ + 0x04C2, /* CYRILLIC LETTER SHORT ZHE */ + 0x04C2, /* Monocases to self */ + 0x04C4, /* CYRILLIC LETTER KA HOOK */ + 0x04C4, /* Monocases to self */ + 0x04C6, /* CYRILLIC LETTER KA OGONEK */ + 0x04C6, /* Monocases to self */ + 0x04C8, /* CYRILLIC LETTER EN HOOK */ + 0x04C8, /* Monocases to self */ + 0x04CA, /* CYRILLIC LETTER KHA OGONEK */ + 0x04CA, /* Monocases to self */ + 0x04CC, /* CYRILLIC LETTER CHE WITH LEFT DESCENDER */ + 0x04CC, /* Monocases to self */ + 0x04CD, /* Monocases to self */ + 0x04CE, /* Monocases to self */ + 0x04CF, /* Monocases to self */ + 0x04D0, /* Monocases to self */ + 0x04D1, /* Monocases to self */ + 0x04D2, /* Monocases to self */ + 0x04D3, /* Monocases to self */ + 0x04D4, /* Monocases to self */ + 0x04D5, /* Monocases to self */ + 0x04D6, /* Monocases to self */ + 0x04D7, /* Monocases to self */ + 0x04D8, /* Monocases to self */ + 0x04D9, /* Monocases to self */ + 0x04DA, /* Monocases to self */ + 0x04DB, /* Monocases to self */ + 0x04DC, /* Monocases to self */ + 0x04DD, /* Monocases to self */ + 0x04DE, /* Monocases to self */ + 0x04DF, /* Monocases to self */ + 0x04E0, /* Monocases to self */ + 0x04E1, /* Monocases to self */ + 0x04E2, /* Monocases to self */ + 0x04E3, /* Monocases to self */ + 0x04E4, /* Monocases to self */ + 0x04E5, /* Monocases to self */ + 0x04E6, /* Monocases to self */ + 0x04E7, /* Monocases to self */ + 0x04E8, /* Monocases to self */ + 0x04E9, /* Monocases to self */ + 0x04EA, /* Monocases to self */ + 0x04EB, /* Monocases to self */ + 0x04EC, /* Monocases to self */ + 0x04ED, /* Monocases to self */ + 0x04EE, /* Monocases to self */ + 0x04EF, /* Monocases to self */ + 0x04F0, /* Monocases to self */ + 0x04F1, /* Monocases to self */ + 0x04F2, /* Monocases to self */ + 0x04F3, /* Monocases to self */ + 0x04F4, /* Monocases to self */ + 0x04F5, /* Monocases to self */ + 0x04F6, /* Monocases to self */ + 0x04F7, /* Monocases to self */ + 0x04F8, /* Monocases to self */ + 0x04F9, /* Monocases to self */ + 0x04FA, /* Monocases to self */ + 0x04FB, /* Monocases to self */ + 0x04FC, /* Monocases to self */ + 0x04FD, /* Monocases to self */ + 0x04FE, /* Monocases to self */ + 0x04FF, /* Monocases to self */ + 0x0500, /* Monocases to self */ + 0x0501, /* Monocases to self */ + 0x0502, /* Monocases to self */ + 0x0503, /* Monocases to self */ + 0x0504, /* Monocases to self */ + 0x0505, /* Monocases to self */ + 0x0506, /* Monocases to self */ + 0x0507, /* Monocases to self */ + 0x0508, /* Monocases to self */ + 0x0509, /* Monocases to self */ + 0x050A, /* Monocases to self */ + 0x050B, /* Monocases to self */ + 0x050C, /* Monocases to self */ + 0x050D, /* Monocases to self */ + 0x050E, /* Monocases to self */ + 0x050F, /* Monocases to self */ + 0x0510, /* Monocases to self */ + 0x0511, /* Monocases to self */ + 0x0512, /* Monocases to self */ + 0x0513, /* Monocases to self */ + 0x0514, /* Monocases to self */ + 0x0515, /* Monocases to self */ + 0x0516, /* Monocases to self */ + 0x0517, /* Monocases to self */ + 0x0518, /* Monocases to self */ + 0x0519, /* Monocases to self */ + 0x051A, /* Monocases to self */ + 0x051B, /* Monocases to self */ + 0x051C, /* Monocases to self */ + 0x051D, /* Monocases to self */ + 0x051E, /* Monocases to self */ + 0x051F, /* Monocases to self */ + 0x0520, /* Monocases to self */ + 0x0521, /* Monocases to self */ + 0x0522, /* Monocases to self */ + 0x0523, /* Monocases to self */ + 0x0524, /* Monocases to self */ + 0x0525, /* Monocases to self */ + 0x0526, /* Monocases to self */ + 0x0527, /* Monocases to self */ + 0x0528, /* Monocases to self */ + 0x0529, /* Monocases to self */ + 0x052A, /* Monocases to self */ + 0x052B, /* Monocases to self */ + 0x052C, /* Monocases to self */ + 0x052D, /* Monocases to self */ + 0x052E, /* Monocases to self */ + 0x052F, /* Monocases to self */ + 0x0530, /* Monocases to self */ + 0x0561, /* ARMENIAN LETTER AYB */ + 0x0562, /* ARMENIAN LETTER BEN */ + 0x0563, /* ARMENIAN LETTER GIM */ + 0x0564, /* ARMENIAN LETTER DA */ + 0x0565, /* ARMENIAN LETTER ECH */ + 0x0566, /* ARMENIAN LETTER ZA */ + 0x0567, /* ARMENIAN LETTER EH */ + 0x0568, /* ARMENIAN LETTER ET */ + 0x0569, /* ARMENIAN LETTER TO */ + 0x056A, /* ARMENIAN LETTER ZHE */ + 0x056B, /* ARMENIAN LETTER INI */ + 0x056C, /* ARMENIAN LETTER LIWN */ + 0x056D, /* ARMENIAN LETTER XEH */ + 0x056E, /* ARMENIAN LETTER CA */ + 0x056F, /* ARMENIAN LETTER KEN */ + 0x0570, /* ARMENIAN LETTER HO */ + 0x0571, /* ARMENIAN LETTER JA */ + 0x0572, /* ARMENIAN LETTER LAD */ + 0x0573, /* ARMENIAN LETTER CHEH */ + 0x0574, /* ARMENIAN LETTER MEN */ + 0x0575, /* ARMENIAN LETTER YI */ + 0x0576, /* ARMENIAN LETTER NOW */ + 0x0577, /* ARMENIAN LETTER SHA */ + 0x0578, /* ARMENIAN LETTER VO */ + 0x0579, /* ARMENIAN LETTER CHA */ + 0x057A, /* ARMENIAN LETTER PEH */ + 0x057B, /* ARMENIAN LETTER JHEH */ + 0x057C, /* ARMENIAN LETTER RA */ + 0x057D, /* ARMENIAN LETTER SEH */ + 0x057E, /* ARMENIAN LETTER VEW */ + 0x057F, /* ARMENIAN LETTER TIWN */ + 0x0580, /* ARMENIAN LETTER REH */ + 0x0581, /* ARMENIAN LETTER CO */ + 0x0582, /* ARMENIAN LETTER YIWN */ + 0x0583, /* ARMENIAN LETTER PIWR */ + 0x0584, /* ARMENIAN LETTER KEH */ + 0x0585, /* ARMENIAN LETTER OH */ + 0x0586, /* ARMENIAN LETTER FEH */ + 0x0557, /* Monocases to self */ + 0x0558, /* Monocases to self */ + 0x0559, /* Monocases to self */ + 0x055A, /* Monocases to self */ + 0x055B, /* Monocases to self */ + 0x055C, /* Monocases to self */ + 0x055D, /* Monocases to self */ + 0x055E, /* Monocases to self */ + 0x055F, /* Monocases to self */ + 0x0560, /* Monocases to self */ + 0x0561, /* Monocases to self */ + 0x0562, /* Monocases to self */ + 0x0563, /* Monocases to self */ + 0x0564, /* Monocases to self */ + 0x0565, /* Monocases to self */ + 0x0566, /* Monocases to self */ + 0x0567, /* Monocases to self */ + 0x0568, /* Monocases to self */ + 0x0569, /* Monocases to self */ + 0x056A, /* Monocases to self */ + 0x056B, /* Monocases to self */ + 0x056C, /* Monocases to self */ + 0x056D, /* Monocases to self */ + 0x056E, /* Monocases to self */ + 0x056F, /* Monocases to self */ + 0x0570, /* Monocases to self */ + 0x0571, /* Monocases to self */ + 0x0572, /* Monocases to self */ + 0x0573, /* Monocases to self */ + 0x0574, /* Monocases to self */ + 0x0575, /* Monocases to self */ + 0x0576, /* Monocases to self */ + 0x0577, /* Monocases to self */ + 0x0578, /* Monocases to self */ + 0x0579, /* Monocases to self */ + 0x057A, /* Monocases to self */ + 0x057B, /* Monocases to self */ + 0x057C, /* Monocases to self */ + 0x057D, /* Monocases to self */ + 0x057E, /* Monocases to self */ + 0x057F, /* Monocases to self */ + 0x0580, /* Monocases to self */ + 0x0581, /* Monocases to self */ + 0x0582, /* Monocases to self */ + 0x0583, /* Monocases to self */ + 0x0584, /* Monocases to self */ + 0x0585, /* Monocases to self */ + 0x0586, /* Monocases to self */ + 0x0587, /* Monocases to self */ + 0x0588, /* Monocases to self */ + 0x0589, /* Monocases to self */ + 0x058A, /* Monocases to self */ + 0x058B, /* Monocases to self */ + 0x058C, /* Monocases to self */ + 0x058D, /* Monocases to self */ + 0x058E, /* Monocases to self */ + 0x058F, /* Monocases to self */ + 0x0590, /* Monocases to self */ + 0x0591, /* Monocases to self */ + 0x0592, /* Monocases to self */ + 0x0593, /* Monocases to self */ + 0x0594, /* Monocases to self */ + 0x0595, /* Monocases to self */ + 0x0596, /* Monocases to self */ + 0x0597, /* Monocases to self */ + 0x0598, /* Monocases to self */ + 0x0599, /* Monocases to self */ + 0x059A, /* Monocases to self */ + 0x059B, /* Monocases to self */ + 0x059C, /* Monocases to self */ + 0x059D, /* Monocases to self */ + 0x059E, /* Monocases to self */ + 0x059F, /* Monocases to self */ + 0x05A0, /* Monocases to self */ + 0x05A1, /* Monocases to self */ + 0x05A2, /* Monocases to self */ + 0x05A3, /* Monocases to self */ + 0x05A4, /* Monocases to self */ + 0x05A5, /* Monocases to self */ + 0x05A6, /* Monocases to self */ + 0x05A7, /* Monocases to self */ + 0x05A8, /* Monocases to self */ + 0x05A9, /* Monocases to self */ + 0x05AA, /* Monocases to self */ + 0x05AB, /* Monocases to self */ + 0x05AC, /* Monocases to self */ + 0x05AD, /* Monocases to self */ + 0x05AE, /* Monocases to self */ + 0x05AF, /* Monocases to self */ + 0x05B0, /* Monocases to self */ + 0x05B1, /* Monocases to self */ + 0x05B2, /* Monocases to self */ + 0x05B3, /* Monocases to self */ + 0x05B4, /* Monocases to self */ + 0x05B5, /* Monocases to self */ + 0x05B6, /* Monocases to self */ + 0x05B7, /* Monocases to self */ + 0x05B8, /* Monocases to self */ + 0x05B9, /* Monocases to self */ + 0x05BA, /* Monocases to self */ + 0x05BB, /* Monocases to self */ + 0x05BC, /* Monocases to self */ + 0x05BD, /* Monocases to self */ + 0x05BE, /* Monocases to self */ + 0x05BF, /* Monocases to self */ + 0x05C0, /* Monocases to self */ + 0x05C1, /* Monocases to self */ + 0x05C2, /* Monocases to self */ + 0x05C3, /* Monocases to self */ + 0x05C4, /* Monocases to self */ + 0x05C5, /* Monocases to self */ + 0x05C6, /* Monocases to self */ + 0x05C7, /* Monocases to self */ + 0x05C8, /* Monocases to self */ + 0x05C9, /* Monocases to self */ + 0x05CA, /* Monocases to self */ + 0x05CB, /* Monocases to self */ + 0x05CC, /* Monocases to self */ + 0x05CD, /* Monocases to self */ + 0x05CE, /* Monocases to self */ + 0x05CF, /* Monocases to self */ + 0x05D0, /* Monocases to self */ + 0x05D1, /* Monocases to self */ + 0x05D2, /* Monocases to self */ + 0x05D3, /* Monocases to self */ + 0x05D4, /* Monocases to self */ + 0x05D5, /* Monocases to self */ + 0x05D6, /* Monocases to self */ + 0x05D7, /* Monocases to self */ + 0x05D8, /* Monocases to self */ + 0x05D9, /* Monocases to self */ + 0x05DA, /* Monocases to self */ + 0x05DB, /* Monocases to self */ + 0x05DC, /* Monocases to self */ + 0x05DD, /* Monocases to self */ + 0x05DE, /* Monocases to self */ + 0x05DF, /* Monocases to self */ + 0x05E0, /* Monocases to self */ + 0x05E1, /* Monocases to self */ + 0x05E2, /* Monocases to self */ + 0x05E3, /* Monocases to self */ + 0x05E4, /* Monocases to self */ + 0x05E5, /* Monocases to self */ + 0x05E6, /* Monocases to self */ + 0x05E7, /* Monocases to self */ + 0x05E8, /* Monocases to self */ + 0x05E9, /* Monocases to self */ + 0x05EA, /* Monocases to self */ + 0x05EB, /* Monocases to self */ + 0x05EC, /* Monocases to self */ + 0x05ED, /* Monocases to self */ + 0x05EE, /* Monocases to self */ + 0x05EF, /* Monocases to self */ + 0x05F0, /* Monocases to self */ + 0x05F1, /* Monocases to self */ + 0x05F2, /* Monocases to self */ + 0x05F3, /* Monocases to self */ + 0x05F4, /* Monocases to self */ + 0x05F5, /* Monocases to self */ + 0x05F6, /* Monocases to self */ + 0x05F7, /* Monocases to self */ + 0x05F8, /* Monocases to self */ + 0x05F9, /* Monocases to self */ + 0x05FA, /* Monocases to self */ + 0x05FB, /* Monocases to self */ + 0x05FC, /* Monocases to self */ + 0x05FD, /* Monocases to self */ + 0x05FE, /* Monocases to self */ + 0x05FF, /* Monocases to self */ + }; + + static const FLMUNICODE georgian[ 40] = + { + 0x10D0, /* GEORGIAN LETTER AN */ + 0x10D1, /* GEORGIAN LETTER BAN */ + 0x10D2, /* GEORGIAN LETTER GAN */ + 0x10D3, /* GEORGIAN LETTER DON */ + 0x10D4, /* GEORGIAN LETTER EN */ + 0x10D5, /* GEORGIAN LETTER VIN */ + 0x10D6, /* GEORGIAN LETTER ZEN */ + 0x10D7, /* GEORGIAN LETTER TAN */ + 0x10D8, /* GEORGIAN LETTER IN */ + 0x10D9, /* GEORGIAN LETTER KAN */ + 0x10DA, /* GEORGIAN LETTER LAS */ + 0x10DB, /* GEORGIAN LETTER MAN */ + 0x10DC, /* GEORGIAN LETTER NAR */ + 0x10DD, /* GEORGIAN LETTER ON */ + 0x10DE, /* GEORGIAN LETTER PAR */ + 0x10DF, /* GEORGIAN LETTER ZHAR */ + 0x10E0, /* GEORGIAN LETTER RAE */ + 0x10E1, /* GEORGIAN LETTER SAN */ + 0x10E2, /* GEORGIAN LETTER TAR */ + 0x10E3, /* GEORGIAN LETTER UN */ + 0x10E4, /* GEORGIAN LETTER PHAR */ + 0x10E5, /* GEORGIAN LETTER KHAR */ + 0x10E6, /* GEORGIAN LETTER GHAN */ + 0x10E7, /* GEORGIAN LETTER QAR */ + 0x10E8, /* GEORGIAN LETTER SHIN */ + 0x10E9, /* GEORGIAN LETTER CHIN */ + 0x10EA, /* GEORGIAN LETTER CAN */ + 0x10EB, /* GEORGIAN LETTER JIL */ + 0x10EC, /* GEORGIAN LETTER CIL */ + 0x10ED, /* GEORGIAN LETTER CHAR */ + 0x10EE, /* GEORGIAN LETTER XAN */ + 0x10EF, /* GEORGIAN LETTER JHAN */ + 0x10F0, /* GEORGIAN LETTER HAE */ + 0x10F1, /* GEORGIAN LETTER HE */ + 0x10F2, /* GEORGIAN LETTER HIE */ + 0x10F3, /* GEORGIAN LETTER WE */ + 0x10F4, /* GEORGIAN LETTER HAR */ + 0x10F5, /* GEORGIAN LETTER HOE */ + }; + + static const FLMUNICODE circledLatin[26] = + { + 0x24D0, /* CIRCLED LATIN LETTER A */ + 0x24D1, /* CIRCLED LATIN LETTER B */ + 0x24D2, /* CIRCLED LATIN LETTER C */ + 0x24D3, /* CIRCLED LATIN LETTER D */ + 0x24D4, /* CIRCLED LATIN LETTER E */ + 0x24D5, /* CIRCLED LATIN LETTER F */ + 0x24D6, /* CIRCLED LATIN LETTER G */ + 0x24D7, /* CIRCLED LATIN LETTER H */ + 0x24D8, /* CIRCLED LATIN LETTER I */ + 0x24D9, /* CIRCLED LATIN LETTER J */ + 0x24DA, /* CIRCLED LATIN LETTER K */ + 0x24DB, /* CIRCLED LATIN LETTER L */ + 0x24DC, /* CIRCLED LATIN LETTER M */ + 0x24DD, /* CIRCLED LATIN LETTER N */ + 0x24DE, /* CIRCLED LATIN LETTER O */ + 0x24DF, /* CIRCLED LATIN LETTER P */ + 0x24E0, /* CIRCLED LATIN LETTER Q */ + 0x24E1, /* CIRCLED LATIN LETTER R */ + 0x24E2, /* CIRCLED LATIN LETTER S */ + 0x24E3, /* CIRCLED LATIN LETTER T */ + 0x24E4, /* CIRCLED LATIN LETTER U */ + 0x24E5, /* CIRCLED LATIN LETTER V */ + 0x24E6, /* CIRCLED LATIN LETTER W */ + 0x24E7, /* CIRCLED LATIN LETTER X */ + 0x24E8, /* CIRCLED LATIN LETTER Y */ + 0x24E9, /* CIRCLED LATIN LETTER Z */ + }; + + static const FLMUNICODE compat[] = + { + 0x2025, + 0x2014, + 0x2013, + 0x005F, + 0x005F, + 0x0028, + 0x0029, + 0x007B, + 0x007D, + 0x3014, + 0x3015, + 0x3010, + 0x3011, + 0x300A, + 0x300B, + 0x3008, + 0x3009, + 0x300C, + 0x300D, + 0x300E, + 0x300F, + 0xFE45, + 0xFE46, + 0xFE47, + 0xFE48, + 0x203E, + 0x203E, + 0x203E, + 0x203E, + 0x005F, + 0x005F, + 0x005F, + 0x002C, + 0x3001, + 0x002E, + 0xFE53, + 0x003B, + 0x003A, + 0x003F, + 0x0021, + 0x2014, + 0x0028, + 0x0029, + 0x007B, + 0x007D, + 0x3014, + 0x3015, + 0x0023, + 0x0026, + 0x002A, + 0x002B, + 0x002D, + 0x003C, + 0x003E, + 0x003D, + 0xFE67, + 0x005C, + 0x0024, + 0x0025, + 0x0040, + 0xFE6C, + 0xFE6D, + 0xFE6E, + 0xFE6F, + 0x064B, + 0x064B, + 0x064C, + 0xFE73, + 0x064D, + 0xFE75, + 0x064E, + 0x064E, + 0x064F, + 0x064F, + 0x0650, + 0x0650, + 0x0651, + 0x0651, + 0x0652, + 0x0652, + 0x0621, + 0x0622, + 0x0622, + 0x0623, + 0x0623, + 0x0624, + 0x0624, + 0x0625, + 0x0625, + 0x0626, + 0x0626, + 0x0626, + 0x0626, + 0x0627, + 0x0627, + 0x0628, + 0x0628, + 0x0628, + 0x0628, + 0x0629, + 0x0629, + 0x062A, + 0x062A, + 0x062A, + 0x062A, + 0x062B, + 0x062B, + 0x062B, + 0x062B, + 0x062C, + 0x062C, + 0x062C, + 0x062C, + 0x062D, + 0x062D, + 0x062D, + 0x062D, + 0x062E, + 0x062E, + 0x062E, + 0x062E, + 0x062F, + 0x062F, + 0x0630, + 0x0630, + 0x0631, + 0x0631, + 0x0632, + 0x0632, + 0x0633, + 0x0633, + 0x0633, + 0x0633, + 0x0634, + 0x0634, + 0x0634, + 0x0634, + 0x0635, + 0x0635, + 0x0635, + 0x0635, + 0x0636, + 0x0636, + 0x0636, + 0x0636, + 0x0637, + 0x0637, + 0x0637, + 0x0637, + 0x0638, + 0x0638, + 0x0638, + 0x0638, + 0x0639, + 0x0639, + 0x0639, + 0x0639, + 0x063A, + 0x063A, + 0x063A, + 0x063A, + 0x0641, + 0x0641, + 0x0641, + 0x0641, + 0x0642, + 0x0642, + 0x0642, + 0x0642, + 0x0643, + 0x0643, + 0x0643, + 0x0643, + 0x0644, + 0x0644, + 0x0644, + 0x0644, + 0x0645, + 0x0645, + 0x0645, + 0x0645, + 0x0646, + 0x0646, + 0x0646, + 0x0646, + 0x0647, + 0x0647, + 0x0647, + 0x0647, + 0x0648, + 0x0648, + 0x0649, + 0x0649, + 0x064A, + 0x064A, + 0x064A, + 0x064A, + 0xFEF5, + 0xFEF6, + 0xFEF7, + 0xFEF8, + 0xFEF9, + 0xFEFA, + 0xFEFB, + 0xFEFC, + 0xFEFD, + 0xFEFE, + 0xFEFE, + 0xFE00, + 0x0021, + 0x0022, + 0x0023, + 0x0024, + 0x0025, + 0x0026, + 0x0027, + 0x0028, + 0x0029, + 0x002A, + 0x002B, + 0x002C, + 0x002D, + 0x002E, + 0x002F, + 0x0030, + 0x0031, + 0x0032, + 0x0033, + 0x0034, + 0x0035, + 0x0036, + 0x0037, + 0x0038, + 0x0039, + 0x003A, + 0x003B, + 0x003C, + 0x003D, + 0x003E, + 0x003F, + 0x0040, + 0x0061, + 0x0062, + 0x0063, + 0x0064, + 0x0065, + 0x0066, + 0x0067, + 0x0068, + 0x0069, + 0x006A, + 0x006B, + 0x006C, + 0x006D, + 0x006E, + 0x006F, + 0x0070, + 0x0071, + 0x0072, + 0x0073, + 0x0074, + 0x0075, + 0x0076, + 0x0077, + 0x0078, + 0x0079, + 0x007A, + 0x005B, + 0x005C, + 0x005D, + 0x005E, + 0x005F, + 0x0060, + 0x0061, + 0x0062, + 0x0063, + 0x0064, + 0x0065, + 0x0066, + 0x0067, + 0x0068, + 0x0069, + 0x006A, + 0x006B, + 0x006C, + 0x006D, + 0x006E, + 0x006F, + 0x0070, + 0x0071, + 0x0072, + 0x0073, + 0x0074, + 0x0075, + 0x0076, + 0x0077, + 0x0078, + 0x0079, + 0x007A, + 0x007B, + 0x007C, + 0x007D, + 0x007E, + 0xFF5F, + 0xFF60, + 0x3002, + 0x300C, + 0x300D, + 0x3001, + 0x30FB, + 0x30F2, + 0x30A1, + 0x30A3, + 0x30A5, + 0x30A7, + 0x30A9, + 0x30E3, + 0x30E5, + 0x30E7, + 0x30C3, + 0x30FC, + 0x30A2, + 0x30A4, + 0x30A6, + 0x30A8, + 0x30AA, + 0x30AB, + 0x30AD, + 0x30AF, + 0x30B1, + 0x30B3, + 0x30B5, + 0x30B7, + 0x30B9, + 0x30BB, + 0x30BD, + 0x30BF, + 0x30C1, + 0x30C4, + 0x30C6, + 0x30C8, + 0x30CA, + 0x30CB, + 0x30CC, + 0x30CD, + 0x30CE, + 0x30CF, + 0x30D2, + 0x30D5, + 0x30D8, + 0x30DB, + 0x30DE, + 0x30DF, + 0x30E0, + 0x30E1, + 0x30E2, + 0x30E4, + 0x30E6, + 0x30E8, + 0x30E9, + 0x30EA, + 0x30EB, + 0x30EC, + 0x30ED, + 0x30EF, + 0x30F3, + 0x309B, + 0x309C, + 0x3164, + 0x3131, + 0x3132, + 0x3133, + 0x3134, + 0x3135, + 0x3136, + 0x3137, + 0x3138, + 0x3139, + 0x313A, + 0x313B, + 0x313C, + 0x313D, + 0x313E, + 0x313F, + 0x3140, + 0x3141, + 0x3142, + 0x3143, + 0x3144, + 0x3145, + 0x3146, + 0x3147, + 0x3148, + 0x3149, + 0x314A, + 0x314B, + 0x314C, + 0x314D, + 0x314E, + 0xFFBF, + 0xFFC0, + 0xFFC1, + 0x314F, + 0x3150, + 0x3151, + 0x3152, + 0x3153, + 0x3154, + 0xFFC8, + 0xFFC9, + 0x3155, + 0x3156, + 0x3157, + 0x3158, + 0x3159, + 0x315A, + 0xFFD0, + 0xFFD1, + 0x315B, + 0x315C, + 0x315D, + 0x315E, + 0x315F, + 0x3160, + 0xFFD8, + 0xFFD9, + 0x3161, + 0x3162, + 0x3163, + 0xFFDD, + 0xFFDE, + 0xFFDF, + 0x00A2, + 0x00A3, + 0x00AC, + 0x00AF, + 0x00A6, + 0x00A5, + 0x20A9 + }; + + if( uChar < 0x600) + { + uChar = basicAlpha[ uChar]; + } + else if( uChar < 0x10A0) + { + ; + } + else if( uChar >= 0x10A0 && uChar <= 0x10C5) + { + uChar = georgian[ uChar - 0x10A0]; + } + else if( uChar >= 0x24B6 && uChar <= 0x24CF) + { + uChar = circledLatin[ uChar - 0x24B6]; + } + else if( uChar >= 0xFE30 && uChar <= 0xFFE6) + { + uChar = compat[ uChar - 0xFE30]; + } + + return( uChar); +} + +/**************************************************************************** +Desc: Compares two Unicode strings +****************************************************************************/ +FLMINT FLMAPI f_unicmp( + const FLMUNICODE * puzStr1, + const FLMUNICODE * puzStr2) +{ + while( *puzStr1 == *puzStr2 && *puzStr1) + { + puzStr1++; + puzStr2++; + } + + return( (FLMINT)*puzStr1 - (FLMINT)*puzStr2); +} + +/**************************************************************************** +Desc: Performs a case-insensitive comparision of two Unicode strings +****************************************************************************/ +FLMINT FLMAPI f_uniicmp( + const FLMUNICODE * puzStr1, + const FLMUNICODE * puzStr2) +{ + while( f_unitolower( *puzStr1) == f_unitolower( *puzStr2) && *puzStr1) + { + puzStr1++; + puzStr2++; + } + + return( (FLMINT)f_unitolower( *puzStr1) - (FLMINT)f_unitolower( *puzStr2)); +} + +/**************************************************************************** +Desc: Compares two strings, one Unicode and one native +****************************************************************************/ +FLMINT FLMAPI f_uninativecmp( + const FLMUNICODE * puzStr1, + const char * pszStr2) +{ + while( *puzStr1 == ((FLMUNICODE)f_toascii( *pszStr2)) && *puzStr1) + { + puzStr1++; + pszStr2++; + } + + return( (FLMINT)*puzStr1 - (FLMINT)*pszStr2); +} + +/**************************************************************************** +Desc: Compares two strings, one Unicode and one native +****************************************************************************/ +FLMINT FLMAPI f_uninativencmp( + const FLMUNICODE * puzStr1, + const char * pszStr2, + FLMUINT uiCount) +{ + if( !uiCount) + { + return( 0); + } + + while( uiCount && + *puzStr1 == ((FLMUNICODE)f_toascii( *pszStr2)) && *puzStr1) + { + puzStr1++; + pszStr2++; + uiCount--; + } + + return( uiCount ? ((FLMINT)*puzStr1 - (FLMINT)*pszStr2) : 0); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL FLMAPI f_uniIsUpper( + FLMUNICODE uzChar) +{ + FLMBOOL bRV; + + if (uzChar & 0x1) + { + bRV = (UnicodeProperties[uzChar / 2] & UNICODE_UPPERCASE_MASK) + ? TRUE + : FALSE; + } + else + { + bRV = ((UnicodeProperties[ uzChar / 2] >> 4) & UNICODE_UPPERCASE_MASK) + ? TRUE + : FALSE; + } + + return( bRV); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL FLMAPI f_uniIsLower( + FLMUNICODE uzChar) +{ + FLMBOOL bRV; + + if (uzChar & 0x1) + { + bRV = (UnicodeProperties[ uzChar / 2] & UNICODE_LOWERCASE_MASK) + ? TRUE + : FALSE; + } + else + { + bRV = ((UnicodeProperties[ uzChar / 2] >> 4) & UNICODE_LOWERCASE_MASK) + ? TRUE + : FALSE; + } + + return( bRV); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL FLMAPI f_uniIsAlpha( + FLMUNICODE uzChar) +{ + FLMBOOL bRV; + + if (uzChar & 0x1) + { + bRV = (UnicodeProperties[ uzChar / 2] & UNICODE_ALPHABETIC_MASK) + ? TRUE + : FALSE; + } + else + { + bRV = ((UnicodeProperties[ uzChar / 2] >> 4) & UNICODE_ALPHABETIC_MASK) + ? TRUE + : FALSE; + } + + return( bRV); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL FLMAPI f_uniIsDecimalDigit( + FLMUNICODE uzChar) +{ + FLMBOOL bRV; + + if (uzChar & 0x1) + { + bRV = (UnicodeProperties[ uzChar / 2] & UNICODE_DECIMAL_DIGIT_MASK) + ? TRUE + : FALSE; + } + else + { + bRV = ((UnicodeProperties[ uzChar / 2] >> 4) & UNICODE_DECIMAL_DIGIT_MASK) + ? TRUE + : FALSE; + } + + return( bRV); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FLMUNICODE FLMAPI f_uniToLower( + FLMUNICODE uzChar) +{ + return( f_unitolower( uzChar)); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE FLMAPI f_nextUCS2Char( + const FLMBYTE ** ppszUTF8, + const FLMBYTE * pszEndOfUTF8String, + FLMUNICODE * puzChar) +{ + return( f_getCharFromUTF8Buf( ppszUTF8, pszEndOfUTF8String, puzChar)); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE FLMAPI f_numUCS2Chars( + const FLMBYTE * pszUTF8, + FLMUINT * puiNumChars) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiTemp = 0; + FLMUNICODE uzChar; + + for (;;) + { + if( RC_BAD( rc = f_getCharFromUTF8Buf( &pszUTF8, NULL, &uzChar))) + { + goto Exit; + } + + if (!uzChar) + { + break; + } + + uiTemp++; + } + +Exit: + + *puiNumChars = uiTemp; + return( rc); +} + +/**************************************************************************** +Desc: Reads the next character from the storage buffer +****************************************************************************/ +RCODE FLMAPI f_getCharFromUTF8Buf( + const FLMBYTE ** ppucBuf, + const FLMBYTE * pucEnd, + FLMUNICODE * puChar) +{ + const FLMBYTE * pucBuf = *ppucBuf; + FLMUINT uiMaxLen = pucEnd ? (FLMUINT)(pucEnd - *ppucBuf) : 3; + + if( !uiMaxLen) + { + *puChar = 0; + return( NE_FLM_OK); + } + + if( pucBuf[ 0] <= 0x7F) + { + if( (*puChar = (FLMUNICODE)pucBuf[ 0]) != 0) + { + (*ppucBuf)++; + } + return( NE_FLM_OK); + } + + if( uiMaxLen < 2 || (pucBuf[ 1] >> 6) != 0x02) + { + return( RC_SET( NE_FLM_BAD_UTF8)); + } + + if( (pucBuf[ 0] >> 5) == 0x06) + { + *puChar = + (FLMUNICODE)(((FLMUNICODE)( pucBuf[ 0] - 0xC0) << 6) + + (FLMUNICODE)(pucBuf[ 1] - 0x80)); + (*ppucBuf) += 2; + return( NE_FLM_OK); + } + + if( uiMaxLen < 3 || + (pucBuf[ 0] >> 4) != 0x0E || + (pucBuf[ 2] >> 6) != 0x02) + { + return( RC_SET( NE_FLM_BAD_UTF8)); + } + + *puChar = + (FLMUNICODE)(((FLMUNICODE)(pucBuf[ 0] - 0xE0) << 12) + + ((FLMUNICODE)(pucBuf[ 1] - 0x80) << 6) + + (FLMUNICODE)(pucBuf[ 2] - 0x80)); + (*ppucBuf) += 3; + + return( NE_FLM_OK); +} + +/**************************************************************************** +Desc: Convert a Unicode character to UTF-8 +*****************************************************************************/ +RCODE FLMAPI f_uni2UTF8( + FLMUNICODE uChar, + FLMBYTE * pucBuf, + FLMUINT * puiBufSize) +{ + if( uChar <= 0x007F) + { + if( pucBuf) + { + if( *puiBufSize < 1) + { + return( RC_SET( NE_FLM_CONV_DEST_OVERFLOW)); + } + + *pucBuf = (FLMBYTE)uChar; + } + *puiBufSize = 1; + } + else if( uChar <= 0x07FF) + { + if( pucBuf) + { + if( *puiBufSize < 2) + { + return( RC_SET( NE_FLM_CONV_DEST_OVERFLOW)); + } + + *pucBuf++ = (FLMBYTE)(0xC0 | (FLMBYTE)(uChar >> 6)); + *pucBuf = (FLMBYTE)(0x80 | (FLMBYTE)(uChar & 0x003F)); + } + *puiBufSize = 2; + } + else + { + if( pucBuf) + { + if( *puiBufSize < 3) + { + return( RC_SET( NE_FLM_CONV_DEST_OVERFLOW)); + } + + *pucBuf++ = (FLMBYTE)(0xE0 | (FLMBYTE)(uChar >> 12)); + *pucBuf++ = (FLMBYTE)(0x80 | (FLMBYTE)((uChar & 0x0FC0) >> 6)); + *pucBuf = (FLMBYTE)(0x80 | (FLMBYTE)(uChar & 0x003F)); + } + *puiBufSize = 3; + } + + return( NE_FLM_OK); +} + +/**************************************************************************** +Desc: Reads the next UTF-8 character from a UTF-8 buffer +Notes: This routine assumes that the destination buffer can hold at least + three bytes +****************************************************************************/ +RCODE FLMAPI f_getUTF8CharFromUTF8Buf( + FLMBYTE ** ppucBuf, + FLMBYTE * pucEnd, + FLMBYTE * pucDestBuf, + FLMUINT * puiLen) +{ + FLMBYTE * pucBuf = *ppucBuf; + FLMUINT uiMaxLen = pucEnd ? (FLMUINT)(pucEnd - *ppucBuf) : 3; + + if( !uiMaxLen || !pucBuf[ 0]) + { + *puiLen = 0; + return( NE_FLM_OK); + } + + if( pucBuf[ 0] <= 0x7F) + { + *pucDestBuf = pucBuf[ 0]; + (*ppucBuf)++; + *puiLen = 1; + return( NE_FLM_OK); + } + + if( uiMaxLen < 2 || (pucBuf[ 1] >> 6) != 0x02) + { + return( RC_SET( NE_FLM_BAD_UTF8)); + } + + if( (pucBuf[ 0] >> 5) == 0x06) + { + pucDestBuf[ 0] = pucBuf[ 0]; + pucDestBuf[ 1] = pucBuf[ 1]; + (*ppucBuf) += 2; + *puiLen = 2; + return( NE_FLM_OK); + } + + if( uiMaxLen < 3 || + (pucBuf[ 0] >> 4) != 0x0E || + (pucBuf[ 2] >> 6) != 0x02) + { + return( RC_SET( NE_FLM_BAD_UTF8)); + } + + pucDestBuf[ 0] = pucBuf[ 0]; + pucDestBuf[ 1] = pucBuf[ 1]; + pucDestBuf[ 2] = pucBuf[ 2]; + (*ppucBuf) += 3; + *puiLen = 3; + + return( NE_FLM_OK); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FLMAPI f_getUTF8Length( + const FLMBYTE * pucBuf, + FLMUINT uiBufLen, + FLMUINT * puiBytes, + FLMUINT * puiChars) +{ + const FLMBYTE * pucStart = pucBuf; + const FLMBYTE * pucEnd = uiBufLen ? (pucStart + uiBufLen) : NULL; + FLMUINT uiChars = 0; + + if (!pucBuf) + { + goto Exit; + } + + while( (!pucEnd || pucBuf < pucEnd) && *pucBuf) + { + if( *pucBuf <= 0x7F) + { + pucBuf++; + uiChars++; + continue; + } + + if( (pucEnd && pucBuf + 1 >= pucEnd) || + (pucBuf[ 1] >> 6) != 0x02) + { + return( RC_SET( NE_FLM_BAD_UTF8)); + } + + if( ((*pucBuf) >> 5) == 0x06) + { + pucBuf += 2; + uiChars++; + continue; + } + + if( (pucEnd && pucBuf + 2 >= pucEnd) || + (pucBuf[ 0] >> 4) != 0x0E || + (pucBuf[ 2] >> 6) != 0x02) + { + return( RC_SET( NE_FLM_BAD_UTF8)); + } + + pucBuf += 3; + uiChars++; + } + +Exit: + + *puiChars = uiChars; + if (pucEnd && pucBuf == pucEnd) + { + *puiBytes = (FLMUINT)(pucBuf - pucStart); + } + else + { + // Hit a null byte + *puiBytes = (FLMUINT)(pucBuf - pucStart) + 1; + } + + return( NE_FLM_OK); +} + +/**************************************************************************** +Desc: Reads the next UTF-8 character from the stream +****************************************************************************/ +RCODE FLMAPI f_readUTF8CharAsUnicode( + IF_IStream * pIStream, + FLMUNICODE * puChar) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE ucBuf[ 3]; + FLMUINT uiLen; + + uiLen = 1; + if( RC_BAD( rc = pIStream->read( &ucBuf[ 0], uiLen, &uiLen))) + { + goto Exit; + } + + if( ucBuf[ 0] <= 0x7F) + { + if( !ucBuf [0]) + { + rc = RC_SET( NE_FLM_EOF_HIT); + goto Exit; + } + *puChar = (FLMUNICODE)ucBuf[ 0]; + goto Exit; + } + + uiLen = 1; + if( RC_BAD( rc = pIStream->read( &ucBuf[ 1], uiLen, &uiLen))) + { + goto Exit; + } + + if( (ucBuf[ 1] >> 6) != 0x02) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BAD_UTF8); + goto Exit; + } + + if( (ucBuf[ 0] >> 5) == 0x06) + { + *puChar = ((FLMUNICODE)( ucBuf[ 0] - 0xC0) << 6) + + (FLMUNICODE)(ucBuf[ 1] - 0x80); + goto Exit; + } + + uiLen = 1; + if( RC_BAD( rc = pIStream->read( &ucBuf[ 2], uiLen, &uiLen))) + { + goto Exit; + } + + if( (ucBuf[ 0] >> 4) != 0x0E || (ucBuf[ 2] >> 6) != 0x02) + { + rc = RC_SET_AND_ASSERT( NE_FLM_BAD_UTF8); + goto Exit; + } + + *puChar = ((FLMUNICODE)(ucBuf[ 0] - 0xE0) << 12) + + ((FLMUNICODE)(ucBuf[ 1] - 0x80) << 6) + + (FLMUNICODE)(ucBuf[ 2] - 0x80); + +Exit: + + if( RC_BAD( rc)) + { + *puChar = 0; + } + + return( rc); +} + +/**************************************************************************** +Desc: Format text removing leading and trailing spaces. Treat + underscores as spaces. As options, remove all spaces and dashes. +****************************************************************************/ +RCODE FLMAPI f_formatUTF8Text( + IF_PosIStream * pIStream, + FLMBOOL bAllowEscapes, + FLMUINT uiCompareRules, + IF_DynaBuf * pDynaBuf) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiFirstSpaceCharPos = FLM_MAX_UINT; + FLMUNICODE uChar; + FLMUINT uiSize; + FLMUINT uiStrSize = 0; + FLMBYTE * pucTmp; + + if( !pIStream->remainingSize()) + { + pDynaBuf->truncateData( 0); + goto Exit; + } + + for (;;) + { + if (RC_BAD( rc = f_readUTF8CharAsUnicode( pIStream, &uChar))) + { + if (rc == NE_FLM_EOF_HIT) + { + rc = NE_FLM_OK; + break; + } + goto Exit; + } + + if ((uChar = f_convertChar( uChar, uiCompareRules)) == 0) + { + continue; + } + + if (uChar == ASCII_SPACE) + { + if (uiCompareRules & + (FLM_COMP_COMPRESS_WHITESPACE | + FLM_COMP_IGNORE_TRAILING_SPACE)) + { + + // Remember the position of the first space. + // When we come to the end of the spaces, we may reset + // the size to compress out spaces if necessary. Or, + // we may opt to get rid of all of them. + + if (uiFirstSpaceCharPos == FLM_MAX_UINT) + { + uiFirstSpaceCharPos = uiStrSize; + } + } + } + else + { + + // Once we hit a non-space character, we can turn off the + // ignore leading spaces flag. + + uiCompareRules &= (~(FLM_COMP_IGNORE_LEADING_SPACE)); + + // See if we need to compress spaces. + + if (uiFirstSpaceCharPos != FLM_MAX_UINT) + { + + // Output exactly one ASCII_SPACE character if we are compressing + // spaces. If we are not compressing spaces, then the only other + // way uiFirstSpaceCharPos would have been set is if we were + // ignoring trailing spaces. In that case, since the spaces + // were not trailing spaces, we need to leave them as is. + + if (uiCompareRules & FLM_COMP_COMPRESS_WHITESPACE) + { + + // A space will already have been encoded into the string. + // Since we know a space takes exactly one byte in the UTF8 + // space, we can simply set our pointer one byte past where + // the last non-space character was found. + + uiStrSize = uiFirstSpaceCharPos + 1; + pDynaBuf->truncateData( uiStrSize); + } + uiFirstSpaceCharPos = FLM_MAX_UINT; + } + + // If we are allowing escaped characters, backslash is treated + // always as an escape character. Whatever follows the + // backslash is the character we need to process. + + if (uChar == ASCII_BACKSLASH && bAllowEscapes) + { + if (RC_BAD( rc = f_readUTF8CharAsUnicode( pIStream, &uChar))) + { + if (rc == NE_FLM_EOF_HIT) + { + rc = NE_FLM_OK; + } + else + { + goto Exit; + } + } + } + } + + // Output the character - need at most three bytes + + if (RC_BAD( rc = pDynaBuf->allocSpace( 3, (void **)&pucTmp))) + { + goto Exit; + } + uiSize = 3; + if (RC_BAD( rc = f_uni2UTF8( uChar, pucTmp, &uiSize))) + { + goto Exit; + } + uiStrSize += uiSize; + pDynaBuf->truncateData( uiStrSize); + } + + // If uiFirstSpaceCharPos != FLM_MAX_UINT, it means that all of the + // characters at the end of the string were spaces. If we + // are ignoring trailing spaces, we need to truncate the string so + // they will be ignored. Otherwise, we need to compress them into + // a single space. + + if (uiFirstSpaceCharPos != FLM_MAX_UINT) + { + if (uiCompareRules & FLM_COMP_IGNORE_TRAILING_SPACE) + { + uiStrSize = uiFirstSpaceCharPos; + } + else + { + flmAssert( uiCompareRules & FLM_COMP_COMPRESS_WHITESPACE); + + // A space will already have been encoded into the string. + // Since we know a space takes exactly one byte in the UTF8 + // space, we can simply set our pointer one byte past where + // the last non-space character was found. + + uiStrSize = uiFirstSpaceCharPos + 1; + } + pDynaBuf->truncateData( uiStrSize); + } + + // Terminate the UTF-8 string + + if (RC_BAD( rc = pDynaBuf->appendByte( 0))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FSTATIC FLMBOOL f_addMetaphone( + const char * pszStr, + const char * pszAltStr, + FLMBYTE * pszMeta, + FLMUINT * puiMetaOffset, + FLMBYTE * pszAltMeta, + FLMUINT * puiAltMetaOffset) +{ + FLMBOOL bDone = FALSE; + + if( pszStr) + { + while( *pszStr) + { + if( *puiMetaOffset < 4) + { + pszMeta[ (*puiMetaOffset)++] = *pszStr; + } + + if( !pszAltStr && pszAltMeta && *puiAltMetaOffset < 4) + { + pszAltMeta[ (*puiAltMetaOffset)++] = *pszStr; + } + + if( *puiMetaOffset == 4 && *puiAltMetaOffset == 4) + { + bDone = TRUE; + break; + } + + pszStr++; + } + } + + if( pszAltStr) + { + while( *pszAltStr) + { + if( *puiAltMetaOffset < 4) + { + pszAltMeta[ (*puiAltMetaOffset)++] = *pszAltStr; + } + + if( *puiMetaOffset == 4 && *puiAltMetaOffset == 4) + { + bDone = TRUE; + break; + } + + pszAltStr++; + } + } + + return( bDone); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FSTATIC void f_metaStrToNum( + FLMBYTE * pszMeta, + FLMUINT * puiMeta) +{ + FLMUINT uiMeta = 0; + FLMUINT uiOffset = 0; + + for( ;;) + { + if( *pszMeta) + { + switch( *pszMeta) + { + case '0': + break; + case 'A': + uiMeta += 1; + break; + case 'F': + uiMeta += 2; + break; + case 'H': + uiMeta += 3; + break; + case 'J': + uiMeta += 4; + break; + case 'K': + uiMeta += 5; + break; + case 'L': + uiMeta += 6; + break; + case 'M': + uiMeta += 7; + break; + case 'N': + uiMeta += 8; + break; + case 'P': + uiMeta += 9; + break; + case 'R': + uiMeta += 10; + break; + case 'S': + uiMeta += 11; + break; + case 'T': + uiMeta += 12; + break; + case 'X': + uiMeta += 13; + break; + default: + flmAssert( 0); + } + + pszMeta++; + } + + if( ++uiOffset == 4) + { + flmAssert( *pszMeta == 0); + break; + } + uiMeta <<= 4; + } + + *puiMeta = uiMeta; +} + +/**************************************************************************** +Desc: Generate the metaphone and alternate metaphone keys for a given + input string +Notes: Lawrence Philips' Metaphone Algorithm is an algorithm which returns + the rough approximation of how an English word sounds. Rather + than returning the character representation of the encoded word, + this routine returns a 16-bit numeric representation. +****************************************************************************/ +RCODE FLMAPI f_getNextMetaphone( + IF_IStream * pIStream, + FLMUINT * puiMetaphone, + FLMUINT * puiAltMetaphone) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiInputOffset = 0; + FLMUINT uiInputLen = 0; + FLMUINT uiLast; + FLMUINT uiLoop; + FLMUINT uiMetaOffset = 0; + FLMUINT uiAltMetaOffset = 0; + FLMBOOL bSlavoGermanic = FALSE; + FLMBOOL bHavePrefix = FALSE; +#define MAX_METAPHONE_INPUT_CHARS 32 + FLMUNICODE uzRealInputBuffer[ MAX_METAPHONE_INPUT_CHARS + 6]; + FLMUNICODE * uzInput = &uzRealInputBuffer [5]; + FLMUNICODE uChar; + FLMBYTE ucMeta[ 5]; + FLMBYTE ucAltMeta[ 5]; + + // Tack on five extra spaces at the beginning of the real buffer, so that we + // can safely access characters before the beginning of the string: + // i.e., the uzInput [uiInputLen - n] comparisons. NOTE: n never + // gets to be more than 5. + + for( uiLoop = 0; uiLoop < 5; uiLoop++) + { + uzRealInputBuffer [uiLoop] = FLM_UNICODE_SPACE; + } + + *puiMetaphone = 0; + + if( puiAltMetaphone) + { + *puiAltMetaphone = 0; + } + + // Get the first word from the stream + + for( ;;) + { + if( RC_BAD( rc = f_readUTF8CharAsUnicode( + pIStream, &uChar))) + { + if (rc == NE_FLM_EOF_HIT) + { + if( uiInputLen) + { + rc = NE_FLM_OK; + break; + } + } + + goto Exit; + } + + if( f_isWhitespace( uChar)) + { + if( !uiInputLen) + { + continue; + } + else + { + // Handle the special cases of "san ", "van ", "von ", + // and "mac ". Since these are common name prefixes + // handled by the metaphone algorithm, we want to continue + // getting the rest of the name. + + if( !bHavePrefix && uiInputLen == 3 && + (f_uninativencmp( uzInput, "san", 3) == 0 || + f_uninativencmp( uzInput, "van", 3) == 0 || + f_uninativencmp( uzInput, "von", 3) == 0 || + f_uninativencmp( uzInput, "mac", 3) == 0)) + { + uzInput[ uiInputLen++] = FLM_UNICODE_SPACE; + bHavePrefix = TRUE; + continue; + } + else + { + if( bHavePrefix && uiInputLen == 4) + { + // Since there wasn't anything following the "prefix", + // the trailing space needs to be removed + + uiInputLen--; + } + break; + } + } + } + + if( uiInputLen < (MAX_METAPHONE_INPUT_CHARS - 5)) + { + uzInput[ uiInputLen++] = f_unitolower( uChar); + + if( !bSlavoGermanic && + (uChar == FLM_UNICODE_w || + uChar == FLM_UNICODE_k || + (uiInputLen > 1 && uChar == FLM_UNICODE_z && + uzInput[ uiInputLen - 2] == FLM_UNICODE_c) || + (uiInputLen >= 4 && uChar == FLM_UNICODE_z && + uzInput[ uiInputLen - 2] == FLM_UNICODE_t && + uzInput[ uiInputLen - 3] == FLM_UNICODE_i && + uzInput[ uiInputLen - 4] == FLM_UNICODE_w))) + { + bSlavoGermanic = TRUE; + } + } + } + + // Tack on five extra spaces to the end of the string so that + // the algorithm below can access characters beyond the end safely. + + for( uiLoop = 0; uiLoop < 5; uiLoop++) + { + uzInput[ uiInputLen + uiLoop] = FLM_UNICODE_SPACE; + } + + uzInput[ uiInputLen + 5] = 0; + uiLast = uiInputLen - 1; + + // Skip the first letter of the following sequences when + // they are found at the beginning of the word + + if( f_uninativencmp( &uzInput[ uiInputOffset], "gn", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset], "kn", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset], "pn", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset], "wr", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset], "ps", 2) == 0) + { + uiInputOffset++; + } + else if( uzInput[ uiInputOffset] == FLM_UNICODE_x) + { + // An initial 'X' is pronounced as a 'Z' which maps to 'S' + + if( f_addMetaphone( "S", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset++; + } + + while( uiMetaOffset < 4 || uiAltMetaOffset < 4) + { + if( uiInputOffset >= uiInputLen) + { + break; + } + + switch( uzInput[ uiInputOffset]) + { + case FLM_UNICODE_a: + case FLM_UNICODE_e: + case FLM_UNICODE_i: + case FLM_UNICODE_o: + case FLM_UNICODE_u: + case FLM_UNICODE_y: + { + if( !uiInputOffset) + { + // All initial vowels map to 'A' + + if( f_addMetaphone( "A", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + + uiInputOffset++; + break; + } + + case FLM_UNICODE_b: + { + //"-mb", e.g", "dumb", already skipped over... + + if( f_addMetaphone( "P", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_b) + { + uiInputOffset += 2; + } + else + { + uiInputOffset++; + } + break; + } + + case FLM_UNICODE_c_CEDILLA: + { + if( f_addMetaphone( "S", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset++; + break; + } + + case FLM_UNICODE_c: + { + // Various Germanic + + if( uiInputOffset && !f_isvowel( uzInput[ uiInputOffset - 2]) && + f_uninativencmp( &uzInput[ uiInputOffset], "ach", 3) == 0 && + ((f_uninativencmp( &uzInput[ uiInputOffset + 2], "i", 1) != 0) && + ((f_uninativencmp( &uzInput[ uiInputOffset + 2], "e", 1) != 0) || + f_uninativencmp( &uzInput[ uiInputOffset - 2], "bacher", 6) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset - 2], "macher", 6) == 0))) + { + if( f_addMetaphone( "K", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 2; + break; + } + + // Special case of "caesar" + + if( !uiInputOffset && + f_uninativencmp( &uzInput[ uiInputOffset], "caesar", 6) == 0) + { + if( f_addMetaphone( "S", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset +=2; + break; + } + + // Italian "chianti" + + if( f_uninativencmp( &uzInput[ uiInputOffset], "chia", 4) == 0) + { + if( f_addMetaphone( "K", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 2; + break; + } + + if( f_uninativencmp( &uzInput[ uiInputOffset], "ch", 2) == 0) + { + // Handle case of "Michael" + + if( uiInputOffset && + f_uninativencmp( &uzInput[ uiInputOffset], "chae", 4) == 0) + { + if( f_addMetaphone( "K", "X", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset +=2; + break; + } + + // Greek roots such as "chemistry" and "chorus" + + if( !uiInputOffset && + (f_uninativencmp( &uzInput[ uiInputOffset + 1], "harac", 5) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "haris", 5) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "hor", 3) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "hym", 3) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "hia", 3) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "hem", 3) == 0) && + f_uninativencmp( &uzInput[ uiInputOffset + 1], "chore", 5) != 0) + { + if( f_addMetaphone( "K", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 2; + break; + } + + // Germanic, Greek 'CH' -> 'KH' + + if( f_uninativencmp( &uzInput[ 0], "van ", 4) == 0 || + f_uninativencmp( &uzInput[ 0], "von ", 4) == 0 || + f_uninativencmp( &uzInput[ 0], "sch", 3) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset - 2], "orches", 6) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset - 2], "archit", 6) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset - 2], "orchid", 6) == 0 || + uzInput[ uiInputOffset + 2] == FLM_UNICODE_t || + uzInput[ uiInputOffset + 2] == FLM_UNICODE_s || + ((uzInput[ uiInputOffset - 1] == FLM_UNICODE_a || + uzInput[ uiInputOffset - 1] == FLM_UNICODE_o || + uzInput[ uiInputOffset - 1] == FLM_UNICODE_u || + uzInput[ uiInputOffset - 1] == FLM_UNICODE_e || + !uiInputOffset) && + (uzInput[ uiInputOffset + 2] == FLM_UNICODE_l || + uzInput[ uiInputOffset + 2] == FLM_UNICODE_r || + uzInput[ uiInputOffset + 2] == FLM_UNICODE_n || + uzInput[ uiInputOffset + 2] == FLM_UNICODE_m || + uzInput[ uiInputOffset + 2] == FLM_UNICODE_b || + uzInput[ uiInputOffset + 2] == FLM_UNICODE_h || + uzInput[ uiInputOffset + 2] == FLM_UNICODE_f || + uzInput[ uiInputOffset + 2] == FLM_UNICODE_v || + uzInput[ uiInputOffset + 2] == FLM_UNICODE_w || + uzInput[ uiInputOffset + 2] == FLM_UNICODE_SPACE))) + { + if( f_addMetaphone( "K", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( uiInputOffset) + { + if( f_uninativencmp( &uzInput[ 0], "mc", 2) == 0) + { + // Names such as "McHugh" + + if( f_addMetaphone( "K", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( f_addMetaphone( "X", "K", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + } + else + { + if( f_addMetaphone( "X", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + } + + uiInputOffset += 2; + break; + } + + // "czerny" + + if( f_uninativencmp( &uzInput[ uiInputOffset], "cz", 2) == 0 && + f_uninativencmp( &uzInput[ uiInputOffset - 2], "wicz", 4) != 0) + { + if( f_addMetaphone( "S", "X", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 2; + break; + } + + if( f_uninativencmp( &uzInput[ uiInputOffset + 1], "cia", 3) == 0) + { + // Words such as "focaccia" + + if( f_addMetaphone( "X", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 3; + break; + } + + // Double 'C', but not if in a name such as "McClellan" + + if( f_uninativencmp( &uzInput[ uiInputOffset], "cc", 2) == 0 && + !(uiInputOffset == 1 && uzInput[ 0] == FLM_UNICODE_m)) + { + // "bellocchio" but not "bacchus" + + if( (uzInput[ uiInputOffset + 2] == FLM_UNICODE_i || + uzInput[ uiInputOffset + 2] == FLM_UNICODE_e || + uzInput[ uiInputOffset + 2] == FLM_UNICODE_h) && + f_uninativencmp( &uzInput[ uiInputOffset + 2], "hu", 2) != 0) + { + // "accident", "accede", "succeed" + + if( (uiInputOffset == 1 && + uzInput[ uiInputOffset - 1] == FLM_UNICODE_a) || + f_uninativencmp( &uzInput[ uiInputOffset - 1], "uccee", 5) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset - 1], "ucces", 5) == 0) + { + if( f_addMetaphone( "KS", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + // "bacci", "bertucci", and other Italian words + + if( f_addMetaphone( "X", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + uiInputOffset += 3; + break; + } + else + { + // Pierce's rule + + if( f_addMetaphone( "K", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 2; + break; + } + } + + if( f_uninativencmp( &uzInput[ uiInputOffset], "ck", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset], "cg", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset], "cq", 2) == 0) + { + if( f_addMetaphone( "K", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 2; + break; + } + + if( f_uninativencmp( &uzInput[ uiInputOffset], "ci", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset], "ce", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset], "cy", 2) == 0) + { + // Italian vs. English + + if( f_uninativencmp( &uzInput[ uiInputOffset], "cio", 3) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset], "cie", 3) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset], "cia", 3) == 0) + { + if( f_addMetaphone( "S", "X", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( f_addMetaphone( "S", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + + uiInputOffset += 2; + break; + } + + // else + + if( f_addMetaphone( "K", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + // Name such as "Mac Caffrey", "Mac Gregor" + + if( f_uninativencmp( &uzInput[ uiInputOffset + 1], " c", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], " q", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], " g", 2) == 0) + { + uiInputOffset += 3; + } + else + { + if( (uzInput[ uiInputOffset + 1] == FLM_UNICODE_c || + uzInput[ uiInputOffset + 1] == FLM_UNICODE_k || + uzInput[ uiInputOffset + 1] == FLM_UNICODE_q) && + !(f_uninativencmp( &uzInput[ uiInputOffset + 1], "ce", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "ci", 2) == 0)) + { + uiInputOffset += 2; + } + else + { + uiInputOffset += 1; + } + } + + break; + } + + case FLM_UNICODE_d: + { + if( f_uninativencmp( &uzInput[ uiInputOffset], "dg", 2) == 0) + { + if( uzInput[ uiInputOffset + 2] == FLM_UNICODE_i || + uzInput[ uiInputOffset + 2] == FLM_UNICODE_e || + uzInput[ uiInputOffset + 2] == FLM_UNICODE_y) + { + // "edge" + + if( f_addMetaphone( "J", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 3; + break; + } + else + { + // "edgar" + + if( f_addMetaphone( "TK", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 2; + break; + } + } + + if( f_uninativencmp( &uzInput[ uiInputOffset], "dt", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset], "dd", 2) == 0) + { + if( f_addMetaphone( "T", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 2; + break; + } + + // else + + if( f_addMetaphone( "T", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset++; + break; + } + + case FLM_UNICODE_f: + { + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_f) + { + uiInputOffset += 2; + } + else + { + uiInputOffset += 1; + } + + if( f_addMetaphone( "F", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + break; + } + + case FLM_UNICODE_g: + { + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_h) + { + if( uiInputOffset > 0 && + !f_isvowel( uzInput[ uiInputOffset - 1])) + { + if( f_addMetaphone( "K", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 2; + break; + } + + if( uiInputOffset < 3) + { + // "ghislane", "ghiradelli" + + if( !uiInputOffset) + { + if( uzInput[ uiInputOffset + 2] == FLM_UNICODE_i) + { + if( f_addMetaphone( "J", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( f_addMetaphone( "K", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + uiInputOffset += 2; + break; + } + } + + // Parker's rule (with some further refinements) - "hugh" + + if( (uiInputOffset && + (uzInput[ uiInputOffset - 2] == FLM_UNICODE_b || + uzInput[ uiInputOffset - 2] == FLM_UNICODE_h || + uzInput[ uiInputOffset - 2] == FLM_UNICODE_d)) || + (uiInputOffset > 2 && // "bough" + (uzInput[ uiInputOffset - 3] == FLM_UNICODE_b || + uzInput[ uiInputOffset - 3] == FLM_UNICODE_h || + uzInput[ uiInputOffset - 3] == FLM_UNICODE_d)) || + (uiInputOffset > 3 && // "broughton" + (uzInput[ uiInputOffset - 4] == FLM_UNICODE_b || + uzInput[ uiInputOffset - 4] == FLM_UNICODE_h))) + { + uiInputOffset += 2; + break; + } + else + { + // "laugh", "McLaughlin", "cough", "gough", "rough", "tough" + + if( uiInputOffset > 2 && + uzInput[ uiInputOffset - 1] == FLM_UNICODE_u && + (uzInput[ uiInputOffset - 3] == FLM_UNICODE_c || + uzInput[ uiInputOffset - 3] == FLM_UNICODE_g || + uzInput[ uiInputOffset - 3] == FLM_UNICODE_l || + uzInput[ uiInputOffset - 3] == FLM_UNICODE_r || + uzInput[ uiInputOffset - 3] == FLM_UNICODE_t)) + { + if( f_addMetaphone( "F", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else if( uiInputOffset && uzInput[ uiInputOffset - 1] != FLM_UNICODE_i) + { + if( f_addMetaphone( "K", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + + uiInputOffset += 2; + break; + } + } + + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_n) + { + if( uiInputOffset == 1 && f_isvowel( uzInput[ 0]) && + !bSlavoGermanic) + { + if( f_addMetaphone( "KN", "N", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + // Not "cagney", etc. + + if( f_uninativencmp( &uzInput[ uiInputOffset + 2], "ey", 2) != 0 && + uzInput[ uiInputOffset + 1] != FLM_UNICODE_y && + !bSlavoGermanic) + { + if( f_addMetaphone( "N", "KN", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( f_addMetaphone( "KN", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + } + + uiInputOffset += 2; + break; + } + + // "tagliaro" + + if( f_uninativencmp( &uzInput[ uiInputOffset + 1], "li", 2) == 0 && + !bSlavoGermanic) + { + if( f_addMetaphone( "KL", "L", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 2; + break; + } + + // Words starting with "ges", "gep", "gel", "gie", etc. + + if( !uiInputOffset && + (uzInput[ uiInputOffset + 1] == FLM_UNICODE_y || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "es", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "ep", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "eb", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "el", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "ey", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "ib", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "il", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "in", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "ie", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "ei", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "er", 2) == 0)) + { + if( f_addMetaphone( "K", "J", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 2; + break; + } + + // -ger-, -gy- + + if( (f_uninativencmp( &uzInput[ uiInputOffset + 1], "er", 2) == 0 || + uzInput[ uiInputOffset + 1] == FLM_UNICODE_y) && + !(f_uninativencmp( &uzInput[ 0], "danger", 6) == 0 || + f_uninativencmp( &uzInput[ 0], "ranger", 6) == 0 || + f_uninativencmp( &uzInput[ 0], "manger", 6) == 0) && + !(uzInput[ uiInputOffset - 1] == FLM_UNICODE_e || + uzInput[ uiInputOffset - 1] == FLM_UNICODE_i) && + !(f_uninativencmp( &uzInput[ uiInputOffset - 1], "rgy", 3) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset - 1], "ogy", 3) == 0)) + { + if( f_addMetaphone( "K", "J", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 2; + break; + } + + // Italian words such as "biaggi" + + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_e || + uzInput[ uiInputOffset + 1] == FLM_UNICODE_i || + uzInput[ uiInputOffset + 1] == FLM_UNICODE_y || + f_uninativencmp( &uzInput[ uiInputOffset - 1], "aggi", 4) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset - 1], "oggi", 4) == 0) + { + // Obvious Germanic + + if( f_uninativencmp( &uzInput[ 0], "van ", 4) == 0 || + f_uninativencmp( &uzInput[ 0], "von ", 4) == 0 || + f_uninativencmp( &uzInput[ 0], "sch", 3) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "et", 2) == 0) + { + if( f_addMetaphone( "K", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + // Always soft if French ending + + if( f_uninativencmp( &uzInput[ uiInputOffset + 1], "ier ", 4) == 0) + { + if( f_addMetaphone( "J", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( f_addMetaphone( "J", "K", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + uiInputOffset += 2; + break; + } + } + + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_g) + { + uiInputOffset += 2; + } + else + { + uiInputOffset += 1; + } + + if( f_addMetaphone( "K", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + break; + } + + case FLM_UNICODE_h: + { + // Only keep if first and if before a vowel or between two vowels + + if( (!uiInputOffset || f_isvowel( uzInput[ uiInputOffset - 1])) && + f_isvowel( uzInput[ uiInputOffset + 1])) + { + if( f_addMetaphone( "H", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 2; + } + else + { + // Take care of "HH" + + uiInputOffset++; + } + break; + } + + case FLM_UNICODE_j: + { + // Obvious Spanish such as "Jose" and "San Jacinto" + + if( f_uninativencmp( &uzInput[ uiInputOffset], "jose", 4) == 0 || + f_uninativencmp( &uzInput[ 0], "san ", 4) == 0) + { + if( (!uiInputOffset && uzInput[ uiInputOffset + 4] == FLM_UNICODE_SPACE) || + f_uninativencmp( &uzInput[ 0], "san ", 4) == 0) + { + if( f_addMetaphone( "H", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( f_addMetaphone( "J", "H", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + uiInputOffset++; + break; + } + + if( !uiInputOffset && + f_uninativencmp( &uzInput[ uiInputOffset], "jose", 4) != 0) + { + // Yankelovich / Jankelowicz + + if( f_addMetaphone( "J", "A", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + // Spanish pronunciation of words such as "bajador" + + if( f_isvowel( uzInput[ uiInputOffset - 1]) && + !bSlavoGermanic && + (uzInput[ uiInputOffset + 1] == FLM_UNICODE_a || + uzInput[ uiInputOffset + 1] == FLM_UNICODE_o)) + { + if( f_addMetaphone( "J", "H", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( uiInputOffset == uiLast) + { + if( f_addMetaphone( "J", NULL, ucMeta, &uiMetaOffset, + NULL, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( uzInput[ uiInputOffset + 1] != FLM_UNICODE_l && + uzInput[ uiInputOffset + 1] != FLM_UNICODE_t && + uzInput[ uiInputOffset + 1] != FLM_UNICODE_k && + uzInput[ uiInputOffset + 1] != FLM_UNICODE_s && + uzInput[ uiInputOffset + 1] != FLM_UNICODE_n && + uzInput[ uiInputOffset + 1] != FLM_UNICODE_m && + uzInput[ uiInputOffset + 1] != FLM_UNICODE_b && + uzInput[ uiInputOffset + 1] != FLM_UNICODE_z && + uzInput[ uiInputOffset - 1] != FLM_UNICODE_s && + uzInput[ uiInputOffset - 1] != FLM_UNICODE_k && + uzInput[ uiInputOffset - 1] != FLM_UNICODE_l) + { + if( f_addMetaphone( "J", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + } + } + } + + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_j) + { + uiInputOffset += 2; + } + else + { + uiInputOffset++; + } + break; + } + + case FLM_UNICODE_k: + { + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_k) + { + uiInputOffset += 2; + } + else + { + uiInputOffset++; + } + + if( f_addMetaphone( "K", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + break; + } + + case FLM_UNICODE_l: + { + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_l) + { + // Spanish words such as "cabrillo" and "gallegos" + + if( (uiInputOffset == (uiInputLen - 3) && + (f_uninativencmp( &uzInput[ uiInputOffset - 1], "illo", 4) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset - 1], "illa", 4) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset - 1], "alle", 4) == 0)) || + ((f_uninativencmp( &uzInput[ uiLast - 1], "as", 2) == 0 || + f_uninativencmp( &uzInput[ uiLast - 1], "os", 2) == 0 || + uzInput[ uiLast] == FLM_UNICODE_a || + uzInput[ uiLast] == FLM_UNICODE_o) && + f_uninativencmp( &uzInput[ uiInputOffset - 1], "alle", 4) == 0)) + { + if( f_addMetaphone( "L", NULL, ucMeta, &uiMetaOffset, + NULL, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 2; + break; + } + uiInputOffset += 2; + } + else + { + uiInputOffset++; + } + + if( f_addMetaphone( "L", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + break; + } + + case FLM_UNICODE_m: + { + if( (f_uninativencmp( &uzInput[ uiInputOffset - 1], "umb", 3) == 0 && + ((uiInputOffset + 1) == uiLast || + f_uninativencmp( &uzInput[ uiInputOffset + 2], "er", 2) == 0)) || + uzInput[ uiInputOffset + 1] == FLM_UNICODE_m) // "dumb", "thumb", etc. + { + uiInputOffset += 2; + } + else + { + uiInputOffset++; + } + + if( f_addMetaphone( "M", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + break; + } + + case FLM_UNICODE_n: + { + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_n) + { + uiInputOffset += 2; + } + else + { + uiInputOffset++; + } + + if( f_addMetaphone( "N", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + break; + } + + case FLM_UNICODE_n_TILDE: + { + if( f_addMetaphone( "N", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset++; + break; + } + + case FLM_UNICODE_p: + { + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_h) + { + if( f_addMetaphone( "F", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 2; + break; + } + + // Account for "Campbell", "raspberry", etc. + + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_p || + uzInput[ uiInputOffset + 1] == FLM_UNICODE_b) + { + uiInputOffset += 2; + } + else + { + uiInputOffset++; + } + + if( f_addMetaphone( "P", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + break; + } + + case FLM_UNICODE_q: + { + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_q) + { + uiInputOffset += 2; + } + else + { + uiInputOffset += 1; + } + + if( f_addMetaphone( "K", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + break; + } + + case FLM_UNICODE_r: + { + // French words such as "rogier". Excludes "Hochmeier" + + if( uiInputOffset == uiLast && + !bSlavoGermanic && + f_uninativencmp( &uzInput[ uiInputOffset - 2], "ie", 2) == 0 && + f_uninativencmp( &uzInput[ uiInputOffset - 4], "me", 2) != 0 && + f_uninativencmp( &uzInput[ uiInputOffset - 4], "ma", 2) != 0) + { + if( f_addMetaphone( NULL, "R", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( f_addMetaphone( "R", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_r) + { + uiInputOffset += 2; + } + else + { + uiInputOffset++; + } + break; + } + + case FLM_UNICODE_s: + { + // Special cases of "island", "isle", "carlisle", "carlysle" + + if( f_uninativencmp( &uzInput[ uiInputOffset - 1], "isl", 3) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset - 1], "ysl", 3) == 0) + { + uiInputOffset++; + break; + } + + // Special case of 'sugar-' + + if( !uiInputOffset && + f_uninativencmp( &uzInput[ uiInputOffset], "sugar", 5) == 0) + { + if( f_addMetaphone( "X", "S", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset++; + break; + } + + if( f_uninativencmp( &uzInput[ uiInputOffset], "sh", 2) == 0) + { + // Germanic + + if( f_uninativencmp( &uzInput[ uiInputOffset + 1], "heim", 4) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "hoek", 4) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "holm", 4) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "holz", 4) == 0) + { + if( f_addMetaphone( "S", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( f_addMetaphone( "X", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + + uiInputOffset += 2; + break; + } + + // Italian and Armenian + + if( f_uninativencmp( &uzInput[ uiInputOffset], "sio", 3) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset], "sia", 3) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset], "sian", 4) == 0) + { + if( !bSlavoGermanic) + { + if( f_addMetaphone( "S", "X", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( f_addMetaphone( "S", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + + uiInputOffset += 3; + break; + } + + // German & Anglicisations such as "Smith" matching "Schmidt" and + // "Snider" matching "Schneider" + + if( (!uiInputOffset && + (uzInput[ uiInputOffset + 1] == FLM_UNICODE_m || + uzInput[ uiInputOffset + 1] == FLM_UNICODE_n || + uzInput[ uiInputOffset + 1] == FLM_UNICODE_l || + uzInput[ uiInputOffset + 1] == FLM_UNICODE_w)) || + uzInput[ uiInputOffset + 1] == FLM_UNICODE_z) + { + if( f_addMetaphone( "S", "X", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_z) + { + uiInputOffset += 2; + } + else + { + uiInputOffset++; + } + break; + } + + if( f_uninativencmp( &uzInput[ uiInputOffset], "sc", 2) == 0) + { + // Schlesinger's rule + + if( uzInput[ uiInputOffset + 2] == FLM_UNICODE_h) + { + // Words of Dutch origin such as "school" and "schooner" + + if( f_uninativencmp( &uzInput[ uiInputOffset + 3], "oo", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 3], "er", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 3], "en", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 3], "uy", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 3], "ed", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 3], "em", 2) == 0) + { + // "Schermerhorn", "Schenker" + + if( f_uninativencmp( &uzInput[ uiInputOffset + 3], "er", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 3], "en", 2) == 0) + { + if( f_addMetaphone( "X", "SK", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( f_addMetaphone( "SK", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + + uiInputOffset += 3; + break; + } + else + { + if( !uiInputOffset && !f_isvowel( uzInput[ 3]) && + uzInput[ 3] != FLM_UNICODE_w) + { + if( f_addMetaphone( "X", "S", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( f_addMetaphone( "X", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + + uiInputOffset += 3; + break; + } + } + + if( uzInput[ uiInputOffset + 2] == FLM_UNICODE_i || + uzInput[ uiInputOffset + 2] == FLM_UNICODE_e || + uzInput[ uiInputOffset + 2] == FLM_UNICODE_y) + { + if( f_addMetaphone( "S", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 3; + break; + } + + if( f_addMetaphone( "SK", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 3; + break; + } + + // French words such as "resnais" and "artois" + + if( uiInputOffset == uiLast && + (f_uninativencmp( &uzInput[ uiInputOffset - 2], "ai", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset - 2], "oi", 2) == 0)) + { + if( f_addMetaphone( NULL, "S", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( f_addMetaphone( "S", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_s || + uzInput[ uiInputOffset + 1] == FLM_UNICODE_z) + { + uiInputOffset += 2; + } + else + { + uiInputOffset++; + } + break; + } + + case FLM_UNICODE_t: + { + if( f_uninativencmp( &uzInput[ uiInputOffset], "tion", 4) == 0) + { + if( f_addMetaphone( "X", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 3; + break; + } + + if( f_uninativencmp( &uzInput[ uiInputOffset], "tia", 3) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset], "tch", 3) == 0) + { + if( f_addMetaphone( "X", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 3; + break; + } + + if( f_uninativencmp( &uzInput[ uiInputOffset], "th", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset], "tth", 3) == 0) + { + // Special cases of "Thomas", "Thames", or Germanic + + if( f_uninativencmp( &uzInput[ uiInputOffset + 2], "om", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 2], "am", 2) == 0 || + f_uninativencmp( &uzInput[ 0], "van ", 4) == 0 || + f_uninativencmp( &uzInput[ 0], "von ", 4) == 0 || + f_uninativencmp( &uzInput[ 0], "sch", 3) == 0) + { + if( f_addMetaphone( "T", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( f_addMetaphone( "0", "T", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + + uiInputOffset += 2; + break; + } + + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_t || + uzInput[ uiInputOffset] == FLM_UNICODE_d) + { + uiInputOffset += 2; + } + else + { + uiInputOffset++; + } + + if( f_addMetaphone( "T", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + break; + } + + case FLM_UNICODE_v: + { + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_v) + { + uiInputOffset += 2; + } + else + { + uiInputOffset++; + } + + if( f_addMetaphone( "F", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + break; + } + + case FLM_UNICODE_w: + { + if( f_uninativencmp( &uzInput[ uiInputOffset], "wr", 2) == 0) + { + if( f_addMetaphone( "R", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 2; + break; + } + + if( !uiInputOffset && + (f_isvowel( uzInput[ uiInputOffset + 1]) || + f_uninativencmp( &uzInput[ uiInputOffset], "wh", 2) == 0)) + { + // "Wasserman" should match "Vasserman" + + if( f_isvowel( uzInput[ uiInputOffset + 1])) + { + if( f_addMetaphone( "A", "F", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + // "Uomo" should match "Womo" + + if( f_addMetaphone( "A", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + } + + // "Arnow" should match "Arnoff" + + if( (uiInputOffset == uiLast && + f_isvowel( uzInput[ uiInputOffset - 1])) || + f_uninativencmp( &uzInput[ uiInputOffset - 1], "ewski", 5) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset - 1], "ewsky", 5) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset - 1], "owski", 5) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset - 1], "owsky", 5) == 0 || + f_uninativencmp( &uzInput[ 0], "sch", 3) == 0) + { + if( f_addMetaphone( NULL, "F", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset++; + break; + } + + // Polish names and words such as "Filipowicz" + + if( f_uninativencmp( &uzInput[ uiInputOffset], "wicz", 4) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset], "witz", 4) == 0) + { + if( f_addMetaphone( "TS", "FX", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset +=4; + break; + } + + // otherwise, skip the current character + + uiInputOffset++; + break; + } + + case FLM_UNICODE_x: + { + // French words such as "breaux" + + if( !(uiInputOffset == uiLast && + (f_uninativencmp( &uzInput[ uiInputOffset - 3], "iau", 3) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset - 3], "eau", 3) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset - 2], "au", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset - 2], "ou", 2) == 0))) + { + if( f_addMetaphone( "KS", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_c || + uzInput[ uiInputOffset + 1] == FLM_UNICODE_x) + { + uiInputOffset += 2; + } + else + { + uiInputOffset++; + } + break; + } + + case FLM_UNICODE_z: + { + // Chinese pinyin such as "Zhao" + + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_h) + { + if( f_addMetaphone( "J", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 2; + break; + } + else + { + if( f_uninativencmp( &uzInput[ uiInputOffset + 1], "zo", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "zi", 2) == 0 || + f_uninativencmp( &uzInput[ uiInputOffset + 1], "za", 2) == 0 || + (bSlavoGermanic && uiInputOffset && + uzInput[ uiInputOffset - 1] != FLM_UNICODE_t)) + { + if( f_addMetaphone( "S", "TS", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( f_addMetaphone( "S", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_z) + { + uiInputOffset += 2; + } + else + { + uiInputOffset++; + } + } + + break; + } + + default: + { + uiInputOffset++; + break; + } + } + } + +Done: + + ucMeta[ uiMetaOffset] = 0; + f_metaStrToNum( ucMeta, puiMetaphone); + + if( puiAltMetaphone) + { + ucAltMeta[ uiAltMetaOffset] = 0; + f_metaStrToNum( ucAltMeta, puiAltMetaphone); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Verifies that the metaphone routines are generating the correct + codes for a hard-coded set of words. +****************************************************************************/ +#ifdef FLM_DEBUG +RCODE f_verifyMetaphoneRoutines( void) +{ + RCODE rc = NE_FLM_OK; + METAPHONE_MAPPING * pMetaMap = gv_MetaTestTable; + F_BufferIStream bufferStream; + FLMUINT uiMeta; + FLMUINT uiAltMeta; + + for( ;;) + { + if( !pMetaMap->pszWord) + { + break; + } + + if( RC_BAD( rc = bufferStream.open( + (FLMBYTE *)pMetaMap->pszWord, f_strlen( pMetaMap->pszWord)))) + { + goto Exit; + } + + if( RC_BAD( rc = f_getNextMetaphone( &bufferStream, + &uiMeta, &uiAltMeta))) + { + goto Exit; + } + + if( uiMeta != pMetaMap->uiMeta || + uiAltMeta != pMetaMap->uiAltMeta) + { + rc = RC_SET_AND_ASSERT( NE_FLM_FAILURE); + goto Exit; + } + + bufferStream.close(); + pMetaMap++; + } + +Exit: + + flmAssert( RC_OK( rc)); + return( rc); +} +#endif diff --git a/ftk/src/ftkthrd.cpp b/ftk/src/ftkthrd.cpp new file mode 100644 index 0000000..cf80d54 --- /dev/null +++ b/ftk/src/ftkthrd.cpp @@ -0,0 +1,1016 @@ +//------------------------------------------------------------------------------ +// Desc: Functions for creating, starting, stopping, controlling threads. +// +// Tabs: 3 +// +// Copyright (c) 2000-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: ftkthrd.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +#ifdef FLM_NLM + void * threadStub( + void * pvThread); +#elif defined( FLM_WIN) + unsigned __stdcall threadStub( + void * pvThread); +#elif defined( FLM_UNIX) + extern "C" void * threadStub( + void * pvThread); +#endif + +/**************************************************************************** +Desc: Add a Reference to this object. +****************************************************************************/ +FLMINT FLMAPI F_Thread::AddRef( void) +{ + return( f_atomicInc( &m_refCnt)); +} + +/**************************************************************************** +Desc: Removes a reference to this object. +****************************************************************************/ +FLMINT FLMAPI F_Thread::Release( void) +{ + FLMINT iRefCnt = f_atomicDec( &m_refCnt); + + if( !iRefCnt) + { + delete this; + } + + return( iRefCnt); +} + +/**************************************************************************** +Desc: Performs various setup work and starts a new thread +****************************************************************************/ +RCODE FLMAPI F_Thread::startThread( + F_THREAD_FUNC fnThread, + const char * pszThreadName, + FLMUINT uiThreadGroup, + FLMUINT uiAppId, + void * pvParm1, + void * pvParm2, + FLMUINT uiStackSize) +{ + RCODE rc = NE_FLM_OK; + FLMBOOL bManagerMutexLocked = FALSE; +#ifdef FLM_NLM + pthread_attr_t thread_attr; + pthread_t uiThreadId; +#endif +#ifdef FLM_WIN + unsigned uiThreadId; +#endif +#if defined( FLM_UNIX) + #if defined( _POSIX_THREADS) + pthread_attr_t thread_attr; + pthread_t uiThreadId; + #else + threadid_p uiThreadId; + #endif +#endif + + flmAssert( fnThread != NULL && m_fnThread == NULL); + + m_fnThread = fnThread; + m_pvParm1 = pvParm1; + m_pvParm2 = pvParm2; + + // Initialize the thread's mutex + + if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) + { + goto Exit; + } + + // Set the stack size + + m_uiStackSize = (uiStackSize < F_THREAD_MIN_STACK_SIZE) + ? F_THREAD_MIN_STACK_SIZE + : uiStackSize; + + // Set the thread name + + if( pszThreadName && *pszThreadName) + { + FLMUINT uiNameLen = f_strlen( pszThreadName) + 1; + + if( RC_BAD( rc = f_alloc( uiNameLen, &m_pszThreadName))) + { + goto Exit; + } + + f_memcpy( (void *)m_pszThreadName, pszThreadName, uiNameLen); + } + + // Set the thread group ID and the application-specified thread ID + + m_uiThreadGroup = uiThreadGroup; + m_uiAppId = uiAppId; + + // Set the thread's state to "running" -- if we fail to + // start the thread, this will be set back to false when + // the cleanupThread() method is called below. We set this + // to TRUE here so that the stopThread() method won't get + // stuck in an infinite loop if the thread was never started. + + m_bRunning = TRUE; + + // Lock the thread manager's mutex. + + f_mutexLock( gv_pThreadMgr->m_hMutex); + bManagerMutexLocked = TRUE; + + // Increment the active thread count + + gv_pThreadMgr->m_uiNumThreads++; + + // Link the thread into the manager's list. We can't link threads in order + // by thread ID at this point, because we don't know what the new thread's + // ID will be. + + if( gv_pThreadMgr->m_pThreadList) + { + gv_pThreadMgr->m_pThreadList->m_pPrev = this; + } + + m_pNext = gv_pThreadMgr->m_pThreadList; + gv_pThreadMgr->m_pThreadList = this; + + // Increment the reference count of the thread object now + // that it is linked into the thread manager's list. + + m_refCnt++; + + // Start the thread + +#ifdef FLM_WIN + if( _beginthreadex( + NULL, (unsigned int)m_uiStackSize, threadStub, + (void *)this, 0, &uiThreadId) == 0) + { + rc = RC_SET( NE_FLM_COULD_NOT_START_THREAD); + goto Exit; + } + m_uiThreadId = (FLMUINT)uiThreadId; +#elif defined( FLM_NLM) + pthread_attr_init( &thread_attr); + pthread_attr_setdetachstate( &thread_attr, PTHREAD_CREATE_DETACHED); + + if (pthread_create( &uiThreadId, &thread_attr, + threadStub, this) != 0) + { + rc = RC_SET( NE_FLM_COULD_NOT_START_THREAD); + goto Exit; + } + + m_uiThreadId = (FLMUINT)uiThreadId; + pthread_attr_destroy( &thread_attr); +#elif defined( FLM_UNIX) + #ifdef _POSIX_THREADS + pthread_attr_init( &thread_attr); + pthread_attr_setdetachstate( &thread_attr, PTHREAD_CREATE_DETACHED); + + if (pthread_create( &uiThreadId, &thread_attr, + threadStub, this) != 0) + { + rc = RC_SET( NE_FLM_COULD_NOT_START_THREAD); + goto Exit; + } + #else + m_uiStackSize = f_max( m_uiStackSize, thr_minstack()); + m_uiStackSize = f_max( m_uiStackSize, thr_min_stack()); + + if( thr_create( (void*)NULL, (size_t)uiStackSize, + threadStub, this, (long)0, &uiThreadId) != 0) + { + rc = RC_SET( NE_FLM_COULD_NOT_START_THREAD); + goto Exit; + } + #endif + + m_uiThreadId = (FLMUINT)uiThreadId; + + #ifdef _POSIX_THREADS + pthread_attr_destroy( &thread_attr); + #endif +#endif + + // Code is not designed to handle a thread ID of 0 + + flmAssert( m_uiThreadId != 0); + + // Unlock the thread manager's mutex. + + f_mutexUnlock( gv_pThreadMgr->m_hMutex); + bManagerMutexLocked = FALSE; + +Exit: + + if( RC_BAD( rc)) + { + // Unlink the thread from the manager's list. This call + // won't do anything if the thread was not linked above. + + gv_pThreadMgr->unlinkThread( this, bManagerMutexLocked); + + // Reset the thread object back to its initial state + + cleanupThread(); + } + + if( bManagerMutexLocked) + { + f_mutexUnlock( gv_pThreadMgr->m_hMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: Stop a running thread +****************************************************************************/ +void FLMAPI F_Thread::stopThread( void) +{ + // Set the shutdown flag and wait for the thread's + // status to be something other than "running" + + m_bShutdown = TRUE; + while( m_bRunning) + { + f_sleep( 10); + } + + // Reset the shutdown flag in case this object is re-used. + + m_bShutdown = FALSE; +} + +/**************************************************************************** +Desc: Begins a new thread of execution and calls the passed function. + Performs generic thread init and cleanup functions. +****************************************************************************/ +#ifdef FLM_NLM +void * threadStub( + void * pvThread) +#elif defined( FLM_WIN) +unsigned __stdcall threadStub( + void * pvThread) +#elif defined( FLM_UNIX) +void * threadStub( + void * pvThread) +#endif +{ + F_Thread * pThread = (F_Thread *)pvThread; + +#if defined( FLM_UNIX) || defined( FLM_NLM) + // Block all signals (main thread will handle all signals) + + sigset_t mask; + sigfillset(&mask); + pthread_sigmask(SIG_SETMASK, &mask, 0); +#endif + + // Lock the manager's mutex + + gv_pThreadMgr->lockMutex(); + + // At this point, the thread ID must match. + + flmAssert( pThread->m_uiThreadId == f_threadId()); + + // Set the start time + + f_timeGetSeconds( &pThread->m_uiStartTime); + + // Unlock the manager's mutex + + gv_pThreadMgr->unlockMutex(); + + // Call the thread's function + + pThread->m_exitRc = pThread->m_fnThread( pThread); + + // Add a temporary reference to the thread object so + // it doesn't go away when we unlink it from the + // manager + + pThread->AddRef(); + + // Unlink the thread from the thread manager. + + gv_pThreadMgr->unlinkThread( pThread, FALSE); + + // Set the running flag to FALSE + + pThread->m_bRunning = FALSE; + + // Release the temporary reference to the thread. Once the + // reference is release, pThread must not be accessed because + // the object may have gone away. + + pThread->Release(); + pThread = NULL; + + // Terminate the thread + +#if defined( FLM_WIN) + _endthreadex( 0); + return( 0); +#endif + +#if defined( FLM_NLM) || defined( FLM_UNIX) + return( NULL); +#endif +} + +/**************************************************************************** +Desc: Frees any resources allocated to the thread and resets member + variables to their initial state +****************************************************************************/ +void FLMAPI F_Thread::cleanupThread( void) +{ + flmAssert( !m_pPrev && !m_pNext); + + if( m_hMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hMutex); + } + + if( m_pszThreadName) + { + f_free( &m_pszThreadName); + } + + if( m_pszThreadStatus) + { + f_free( &m_pszThreadStatus); + } + + m_uiStatusBufLen = 0; + m_bShutdown = FALSE; + m_fnThread = NULL; + m_bRunning = FALSE; + m_uiStackSize = 0; + m_pvParm1 = NULL; + m_pvParm2 = NULL; + m_uiThreadId = 0; + m_uiThreadGroup = 0; + m_uiAppId = 0; + m_uiStartTime = 0; + m_exitRc = NE_FLM_OK; +} + +/**************************************************************************** +Desc: Set the thread's status +****************************************************************************/ +void FLMAPI F_Thread::setThreadStatusStr( + const char * pszStatus) +{ + FLMUINT uiStatusLen = f_strlen( pszStatus) + 1; + + if( m_uiStatusBufLen < uiStatusLen) + { + FLMUINT uiAllocSize = uiStatusLen < 128 ? 128 : uiStatusLen; + + if( m_pszThreadStatus != NULL) + { + f_free( &m_pszThreadStatus); + } + m_uiStatusBufLen = 0; + + if( RC_BAD( f_alloc( uiAllocSize, &m_pszThreadStatus))) + { + goto Exit; + } + m_uiStatusBufLen = uiAllocSize; + } + + f_mutexLock( m_hMutex); + f_memcpy( m_pszThreadStatus, pszStatus, uiStatusLen); + f_mutexUnlock( m_hMutex); + +Exit: + + return; +} + +/**************************************************************************** +Desc: Set the thread's status +****************************************************************************/ +void FLMAPI F_Thread::setThreadStatus( + const char * pszFormat, ...) +{ + char pucBuffer[ 128]; + f_va_list args; + + f_va_start( args, pszFormat); + f_vsprintf( pucBuffer, pszFormat, &args); + f_va_end( args); + + setThreadStatusStr( pucBuffer); +} + +/**************************************************************************** +Desc: Set the thread's status to a generic string +****************************************************************************/ +void FLMAPI F_Thread::setThreadStatus( + eThreadStatus genericStatus) +{ + const char * pszStatus = NULL; + + switch( genericStatus) + { + case FLM_THREAD_STATUS_INITIALIZING: + pszStatus = "Initializing"; + break; + + case FLM_THREAD_STATUS_RUNNING: + pszStatus = "Running"; + break; + + case FLM_THREAD_STATUS_SLEEPING: + pszStatus = "Sleeping"; + break; + + case FLM_THREAD_STATUS_TERMINATING: + pszStatus = "Terminating"; + break; + + case FLM_THREAD_STATUS_STARTING_TRANS: + pszStatus = "Starting transaction"; + break; + + case FLM_THREAD_STATUS_COMMITTING_TRANS: + pszStatus = "Committing transaction"; + break; + + case FLM_THREAD_STATUS_ABORTING_TRANS: + pszStatus = "Aborting transaction"; + break; + + case FLM_THREAD_STATUS_UNKNOWN: + default: + pszStatus = "Unknown"; + break; + } + + if( pszStatus) + { + setThreadStatusStr( pszStatus); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_ThreadMgr::~F_ThreadMgr() +{ + F_Thread * pTmpThread; + + if( m_hMutex != F_MUTEX_NULL) + { + f_mutexLock( m_hMutex); + pTmpThread = m_pThreadList; + while( pTmpThread) + { + pTmpThread->setShutdownFlag(); + pTmpThread = pTmpThread->m_pNext; + } + + while( m_pThreadList) + { + f_mutexUnlock( m_hMutex); + f_sleep( 50); + f_mutexLock( m_hMutex); + } + + f_mutexUnlock( m_hMutex); + f_mutexDestroy( &m_hMutex); + } +} +/**************************************************************************** +Desc: Allocates resources needed by the thread manager +****************************************************************************/ +RCODE FLMAPI F_ThreadMgr::setupThreadMgr( void) +{ + RCODE rc = NE_FLM_OK; + + flmAssert( m_hMutex == F_MUTEX_NULL); + + if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Removes a thread from the thread manager's list +Notes: This routine assumes that the manager's mutex is already locked. +****************************************************************************/ +void F_ThreadMgr::unlinkThread( + IF_Thread * ifpThread, + FLMBOOL bMutexIsLocked) +{ + F_Thread * pThread = (F_Thread *)ifpThread; + + // Lock the thread manager's mutex + + if( !bMutexIsLocked) + { + f_mutexLock( m_hMutex); + } + + // If the thread isn't linked into the list, + // don't do anything + + if( !pThread->m_pPrev && !pThread->m_pNext && + m_pThreadList != pThread) + { + goto Exit; + } + + // Decrement the active thread count + + flmAssert( m_uiNumThreads); + m_uiNumThreads--; + + if( pThread->m_pPrev) + { + pThread->m_pPrev->m_pNext = pThread->m_pNext; + } + else + { + m_pThreadList = pThread->m_pNext; + } + + if( pThread->m_pNext) + { + pThread->m_pNext->m_pPrev = pThread->m_pPrev; + } + + pThread->m_pNext = NULL; + pThread->m_pPrev = NULL; + + // Release the thread object + + pThread->Release(); + +Exit: + + if( !bMutexIsLocked) + { + f_mutexUnlock( m_hMutex); + } +} + +/**************************************************************************** +Desc: Signals all threads in a thread group to shut down and waits + for them to terminate. +****************************************************************************/ +void FLMAPI F_ThreadMgr::shutdownThreadGroup( + FLMUINT uiThreadGroup) +{ + F_Thread * pThread; + FLMUINT uiCount; + + for( ;;) + { + f_mutexLock( m_hMutex); + + uiCount = 0; + pThread = m_pThreadList; + while( pThread) + { + if( pThread->m_uiThreadGroup == uiThreadGroup) + { + pThread->setShutdownFlag(); + uiCount++; + } + pThread = pThread->m_pNext; + } + + f_mutexUnlock( m_hMutex); + + if( !uiCount) + { + break; + } + + // The threads will automatically unlink themselves from + // the manager before they terminate. Just sleep for + // a few milliseconds and look through the list again to + // verify that there are no more threads in the group. + + f_sleep( 200); + } +} + +/**************************************************************************** +Desc: Signals a thread to shut down. +****************************************************************************/ +void FLMAPI F_ThreadMgr::setThreadShutdownFlag( + FLMUINT uiThreadId) +{ + F_Thread * pThread; + + flmAssert( uiThreadId != 0); + + f_mutexLock( m_hMutex); + pThread = m_pThreadList; + while( pThread) + { + if( pThread->m_uiThreadId == uiThreadId) + { + pThread->setShutdownFlag(); + break; + } + pThread = pThread->m_pNext; + } + + f_mutexUnlock( m_hMutex); +} + +/**************************************************************************** +Desc: Allocates an array of F_THREAD_INFO structures and populates them + with information about the threads being managed by this object. +****************************************************************************/ +RCODE FLMAPI F_ThreadMgr::getThreadInfo( + IF_Pool * pPool, + F_THREAD_INFO ** ppThreadInfo, + FLMUINT * puiNumThreads) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiOffset; + FLMUINT uiLoop; + FLMUINT uiSubLoop; + FLMUINT uiLen; + FLMBOOL bMutexLocked = FALSE; + F_THREAD_INFO * pThreadInfo = NULL; + F_THREAD_INFO tmpThreadInfo; + F_Thread * pCurThread; + void * pvMark = pPool->poolMark(); + + *ppThreadInfo = NULL; + *puiNumThreads = 0; + + f_mutexLock( m_hMutex); + bMutexLocked = TRUE; + + if( m_uiNumThreads == 0) + { + goto Exit; + } + + if (RC_BAD( rc = pPool->poolCalloc( sizeof( F_THREAD_INFO) * m_uiNumThreads, + (void **)&pThreadInfo))) + { + goto Exit; + } + + uiOffset = 0; + pCurThread = m_pThreadList; + while( pCurThread) + { + flmAssert( uiOffset < m_uiNumThreads); + f_mutexLock( pCurThread->m_hMutex); + + pThreadInfo[ uiOffset].uiThreadId = pCurThread->m_uiThreadId; + pThreadInfo[ uiOffset].uiThreadGroup = pCurThread->m_uiThreadGroup; + pThreadInfo[ uiOffset].uiAppId = pCurThread->m_uiAppId; + pThreadInfo[ uiOffset].uiStartTime = pCurThread->m_uiStartTime; + + if( pCurThread->m_pszThreadName) + { + uiLen = f_strlen( pCurThread->m_pszThreadName) + 1; + + if (RC_OK( pPool->poolCalloc( uiLen, + (void **)&pThreadInfo[ uiOffset].pszThreadName))) + { + f_memcpy( (void *)pThreadInfo[ uiOffset].pszThreadName, + pCurThread->m_pszThreadName, uiLen); + } + } + + if( pCurThread->m_pszThreadStatus) + { + uiLen = f_strlen( pCurThread->m_pszThreadStatus) + 1; + + if (RC_OK( pPool->poolCalloc( uiLen, + (void **)&pThreadInfo[ uiOffset].pszThreadStatus))) + { + f_memcpy( (void *)(pThreadInfo[ uiOffset].pszThreadStatus), + pCurThread->m_pszThreadStatus, uiLen); + } + } + + f_mutexUnlock( pCurThread->m_hMutex); + uiOffset++; + pCurThread = pCurThread->m_pNext; + } + + flmAssert( uiOffset == m_uiNumThreads); + *puiNumThreads = m_uiNumThreads; + + f_mutexUnlock( m_hMutex); + bMutexLocked = FALSE; + + // Sort the list by thread ID + + for( uiLoop = 0; uiLoop < *puiNumThreads; uiLoop++) + { + for( uiSubLoop = uiLoop + 1; uiSubLoop < *puiNumThreads; uiSubLoop++) + { + if( pThreadInfo[ uiLoop].uiThreadId > + pThreadInfo[ uiSubLoop].uiThreadId) + { + f_memcpy( &tmpThreadInfo, + &pThreadInfo[ uiLoop], sizeof( F_THREAD_INFO)); + f_memcpy( &pThreadInfo[ uiLoop], + &pThreadInfo[ uiSubLoop], sizeof( F_THREAD_INFO)); + f_memcpy( &pThreadInfo[ uiSubLoop], + &tmpThreadInfo, sizeof( F_THREAD_INFO)); + } + } + } + + *ppThreadInfo = pThreadInfo; + +Exit: + + if( RC_BAD( rc)) + { + pPool->poolReset( pvMark); + } + + if( bMutexLocked) + { + f_mutexUnlock( m_hMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: Finds a thread based on user-specified identifiers +****************************************************************************/ +RCODE FLMAPI F_ThreadMgr::findThread( + IF_Thread ** ppThread, + FLMUINT uiThreadGroup, + FLMUINT uiAppId, + FLMBOOL bOkToFindMe) +{ + RCODE rc = NE_FLM_OK; + FLMBOOL bMutexLocked = FALSE; + F_Thread * pCurThread; + + *ppThread = NULL; + + f_mutexLock( m_hMutex); + bMutexLocked = TRUE; + + if( m_uiNumThreads == 0) + { + rc = RC_SET( NE_FLM_NOT_FOUND); + goto Exit; + } + + pCurThread = m_pThreadList; + while( pCurThread) + { + f_mutexLock( pCurThread->m_hMutex); + + if( pCurThread->m_uiThreadGroup == uiThreadGroup && + pCurThread->m_uiAppId == uiAppId) + { + if( bOkToFindMe || + (!bOkToFindMe && pCurThread->m_uiThreadId != f_threadId())) + { + // Found a match. + + pCurThread->AddRef(); + *ppThread = pCurThread; + f_mutexUnlock( pCurThread->m_hMutex); + goto Exit; + } + } + + f_mutexUnlock( pCurThread->m_hMutex); + pCurThread = pCurThread->m_pNext; + } + + rc = RC_SET( NE_FLM_NOT_FOUND); + +Exit: + + if( bMutexLocked) + { + f_mutexUnlock( m_hMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: Finds a thread based on user-specified identifiers +****************************************************************************/ +RCODE FLMAPI F_ThreadMgr::getNextGroupThread( + IF_Thread ** ppThread, + FLMUINT uiThreadGroup, + FLMUINT * puiThreadId) +{ + RCODE rc = NE_FLM_OK; + FLMBOOL bMutexLocked = FALSE; + F_Thread * pCurThread; + F_Thread * pFoundThread = NULL; + + f_mutexLock( m_hMutex); + bMutexLocked = TRUE; + + if( m_uiNumThreads == 0) + { + rc = RC_SET( NE_FLM_NOT_FOUND); + goto Exit; + } + + pCurThread = m_pThreadList; + while( pCurThread) + { + if( pCurThread->m_uiThreadGroup == uiThreadGroup && + pCurThread->m_uiThreadId > *puiThreadId) + { + // The threads are not kept in order by thread ID in the + // manager's list. So, we need to make sure we get the + // thread with the next ID beyond the ID passed into the + // routine. + + if( !pFoundThread || + pCurThread->m_uiThreadId < pFoundThread->m_uiThreadId) + { + pFoundThread = pCurThread; + } + } + + pCurThread = pCurThread->m_pNext; + } + + if( !pFoundThread) + { + rc = RC_SET( NE_FLM_NOT_FOUND); + goto Exit; + } + + pFoundThread->AddRef(); + *ppThread = pFoundThread; + *puiThreadId = pFoundThread->m_uiThreadId; + +Exit: + + if( RC_BAD( rc)) + { + *ppThread = NULL; + *puiThreadId = 0xFFFFFFFF; + } + + if( bMutexLocked) + { + f_mutexUnlock( m_hMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: Returns a count of the number of threads in a specified group +****************************************************************************/ +FLMUINT FLMAPI F_ThreadMgr::getThreadGroupCount( + FLMUINT uiThreadGroup) +{ + F_Thread * pThread; + FLMUINT uiCount; + + f_mutexLock( m_hMutex); + + uiCount = 0; + pThread = m_pThreadList; + while( pThread) + { + if( pThread->m_uiThreadGroup == uiThreadGroup) + { + uiCount++; + } + pThread = pThread->m_pNext; + } + + f_mutexUnlock( m_hMutex); + return( uiCount); +} + +/**************************************************************************** +Desc: Allocate a thread object and start the thread +****************************************************************************/ +RCODE FLMAPI f_threadCreate( + IF_Thread ** ppThread, + F_THREAD_FUNC fnThread, + const char * pszThreadName, + FLMUINT uiThreadGroup, + FLMUINT uiAppId, + void * pvParm1, + void * pvParm2, + FLMUINT uiStackSize) +{ + RCODE rc = NE_FLM_OK; + F_Thread * pThread = NULL; + + if( ppThread) + { + *ppThread = NULL; + } + + if( (pThread = f_new F_Thread) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pThread->startThread( + fnThread, pszThreadName, uiThreadGroup, uiAppId, + pvParm1, pvParm2, uiStackSize))) + { + goto Exit; + } + + if( ppThread) + { + *ppThread = pThread; + + // Set pThread to NULL so that the object won't be released + // below. The application has indicated (by passing in a + // non-NULL ppThread) that it wants to keep a reference to + // the thread. + + pThread = NULL; + } + +Exit: + + if( pThread) + { + pThread->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Deletes a thread object and sets the passed-in pointer to NULL +Notes: Should not be used on threads that were started with the + auto-destroy flag set to TRUE +****************************************************************************/ +void FLMAPI f_threadDestroy( + IF_Thread ** ppThread) +{ + if( *ppThread != NULL) + { + (*ppThread)->stopThread(); + (*ppThread)->Release(); + *ppThread = NULL; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT FLMAPI f_threadId( void) +{ +#ifdef FLM_WIN + return( (FLMUINT)_threadid); +#elif defined( FLM_NLM) || defined( FLM_UNIX) + return( (FLMUINT)pthread_self()); +#else + #error Platform not supprted +#endif +} diff --git a/ftk/src/ftktime.cpp b/ftk/src/ftktime.cpp new file mode 100644 index 0000000..69c11f2 --- /dev/null +++ b/ftk/src/ftktime.cpp @@ -0,0 +1,337 @@ +//------------------------------------------------------------------------------ +// Desc: Date and time functions +// +// Tabs: 3 +// +// Copyright (c) 1991-2000, 2002-2003,2005-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: ftktime.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +#define BASEYR 1970 // all gmt calcs done since 1970 +#define SECONDSPERDAY 86400l // 24 hours * 60 minutes * 60 seconds +#define SECONDSPERHOUR 3600 // 60 minutes * 60 seconds +#define DDAYSPERYEAR 365 // 365 days/year + +static FLMUINT8 ui8NumDaysPerMonth[2][12] = +{ + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } +}; + +static FLMUINT16 ui16NumDaysFromJan1st[2][12] = +{ + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }, + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 } +}; + +static F_TMSTAMP maxdate = +{ + 2106, 1, 6, 11, 0, 0, 0 +}; + +static FLMUINT f_timeLeapYearsSince1970( + FLMUINT16 year); + +/**************************************************************************** +Desc: Gets the number of seconds since 1980 or 1970. +****************************************************************************/ +void f_timeGetSeconds( + FLMUINT * puiSeconds) +{ +#if defined( FLM_WIN) + *puiSeconds = (FLMUINT) time( NULL); + +#elif defined( FLM_UNIX) || defined( FLM_NLM) + *puiSeconds = (FLMUINT) time( 0); + +#else + #error Platform not supported +#endif +} + +/**************************************************************************** +Desc: Gets the time stamp from the system clock. +Notes: +****************************************************************************/ +void f_timeGetTimeStamp( + F_TMSTAMP * pTimeStamp) +{ +#if defined( FLM_WIN) + SYSTEMTIME rightnow; + + GetLocalTime( &rightnow ); + + pTimeStamp->year = rightnow.wYear; + pTimeStamp->month = (FLMUINT8)(rightnow.wMonth - 1); + pTimeStamp->day = (FLMUINT8)rightnow.wDay; + + pTimeStamp->hour = (FLMUINT8)rightnow.wHour; + pTimeStamp->minute = (FLMUINT8)rightnow.wMinute; + pTimeStamp->second = (FLMUINT8)rightnow.wSecond; + pTimeStamp->hundredth = rightnow.wMilliseconds / 10; + +#elif defined( FLM_UNIX) || defined( FLM_NLM) + time_t now; + struct tm rightnow; + + now = time( (time_t *) 0 ); + (void)localtime_r( &now, &rightnow ); + + pTimeStamp->year = rightnow.tm_year + 1900; + pTimeStamp->month = rightnow.tm_mon; + pTimeStamp->day = rightnow.tm_mday; + pTimeStamp->hour = rightnow.tm_hour; + pTimeStamp->minute = rightnow.tm_min; + pTimeStamp->second = rightnow.tm_sec; + pTimeStamp->hundredth = 0; +#else + #error Platform not supported +#endif +} + +/**************************************************************************** +Desc: Returns the local time bias in seconds +****************************************************************************/ +FLMINT f_timeGetLocalOffset( void) +{ + FLMINT iOffset = 0; + +#if defined( FLM_WIN) + TIME_ZONE_INFORMATION tzInfo; + DWORD retVal; + + retVal = GetTimeZoneInformation( &tzInfo); + + if( retVal != TIME_ZONE_ID_UNKNOWN) + { + iOffset = + (retVal == TIME_ZONE_ID_DAYLIGHT && tzInfo.DaylightDate.wMonth + ? tzInfo.Bias + tzInfo.DaylightBias + : tzInfo.Bias) * 60; + } + +#elif defined( FLM_UNIX) || defined( FLM_NLM) + time_t gmtTime; + time_t localTime; + struct tm gmtTm; + + gmtTime = time( (time_t *)0); + gmtime_r( &gmtTime, &gmtTm); + localTime = mktime( &gmtTm); + iOffset = (FLMINT)((FLMINT64)localTime - (FLMINT64)gmtTime); + +#else + #error Platform not supported +#endif + + return( iOffset); +} + +/**************************************************************************** +Desc: Count the number of leap years from 1970 to given year. + +In: year - FLMUINT16 value containing the year +Out: (None) +Ret: Number of leap years since 1970 +Notes: According to the Gregorian calendar (which we currently use), the + year is a leap year if it is divisible by 4, unless it is a century + year, then it must be divisible by 400. +****************************************************************************/ +static FLMUINT f_timeLeapYearsSince1970( + FLMUINT16 ui16Year) +{ + FLMUINT uiTemp; + + /* first calculate # of leap years since 1600 */ + + ui16Year -= 1601; /* ui16Year = number of years since 1600*/ + uiTemp = ( /* Count leap years */ + (ui16Year / 4) - /* Count potential leap years */ + (ui16Year / 100) + /* Subtract out century years */ + (ui16Year / 400) + /* Add back in quadricentenial years*/ + 1 /* And don't forget to count 1600 */ + ); + + /* now subtract # of leap years between 1600 and 1970 */ + /* (the following becomes a constant at compile time) */ + + uiTemp -= ((BASEYR-1600) / 4) - ((BASEYR-1600) / 100) + 1; + return(uiTemp); +} + +/**************************************************************************** +Desc: Convert from seconds to the F_TMSTAMP structure. +Notes: +****************************************************************************/ +void f_timeSecondsToDate( + FLMUINT uiSeconds, + F_TMSTAMP * date) +{ + FLMUINT uiLeapYear; + FLMUINT uiMonth; + FLMUINT uiDaysInMonth; + FLMUINT uiDay; + + uiDay = uiSeconds / SECONDSPERDAY; // # of days since 1970 + date->year = (FLMUINT16)((uiDay / DDAYSPERYEAR) + BASEYR); + uiDay = uiDay % DDAYSPERYEAR; // # of days into year + + // Check to see that the value for the current day is greater than the + // number of leap years since 1970. This is because we will be + // subtracting the leap days from the current day and we don't want + // the value for the day to go negative. + + while( uiDay < f_timeLeapYearsSince1970(date->year)) // if day < # of leap years + { + date->year--; // decrement the year + uiDay += DDAYSPERYEAR; // adjust day by days/year + } + + uiDay -= f_timeLeapYearsSince1970( date->year); // subtract leap days + uiLeapYear = f_timeIsLeapYear( date->year ); // set leap year flag + + // Find what our offset into the current month is. + // To do this, we subtract out the number of days for each month, until + // the number of days left does not span the end of the current month + + for( uiMonth = 0; + uiMonth < 12 && + (uiDay >= (uiDaysInMonth = ui8NumDaysPerMonth[uiLeapYear][uiMonth])); + uiMonth++) + { + uiDay -= uiDaysInMonth; // subtract days in month + } + date->month = (FLMUINT8) uiMonth; // set month, day + date->day = (FLMUINT8) (++uiDay); + + uiDay = uiSeconds % SECONDSPERDAY; // mod by seconds/day + date->hour = (FLMUINT8)(uiDay / SECONDSPERHOUR);// get # of hours + uiDay = uiDay % SECONDSPERHOUR; + date->minute = (FLMUINT8)(uiDay / 60); // get # of minutes + date->second = (FLMUINT8)(uiDay % 60); + date->hundredth = 0; // no fractional seconds +} + +/**************************************************************************** +Desc: Convert a time stamp to the number of seconds. +****************************************************************************/ +void f_timeDateToSeconds( + F_TMSTAMP * pTimeStamp, + FLMUINT * puiSeconds) +{ + FLMUINT uiDays = 0; + + // is date past max? + + if( f_timeCompareTimeStamps( pTimeStamp, &maxdate, 0) > 0) + { + *pTimeStamp = maxdate; + } + + // Do date portion of calculation - result is days since 1/1/1970. + + if( pTimeStamp->year) + { + uiDays = + (pTimeStamp->year - BASEYR) * 365 + // years since BASE * days + f_timeLeapYearsSince1970( pTimeStamp->year) +// leap years since BASE + ui16NumDaysFromJan1st[ f_timeIsLeapYear(pTimeStamp->year)][pTimeStamp->month] + + pTimeStamp->day - 1; // days since 1st of month + } + + // Do time part of calculation - secs since 1/1/1970 12:00am. + + *puiSeconds = (((uiDays * 24) + // convert days to hours + pTimeStamp->hour ) * 60 + // convert hours to min + pTimeStamp->minute) * 60 + // convert min to sec + pTimeStamp->second; // give secs granularity + +} + +/**************************************************************************** +Desc: Compare two time stamps +In: date1, date2 - pointers to two DATIM structures + flag - flag to indicate the type of comparison + 0 - compare date and time + 1 - compare date only + 2 - compare time only +Out: +Ret: -1 if date1 is less than date2 + 0 if date1 is equal to date2 + 1 if date1 is greater than date2 +Notes: +****************************************************************************/ +FLMINT f_timeCompareTimeStamps( + F_TMSTAMP * pTimeStamp1, + F_TMSTAMP * pTimeStamp2, + FLMUINT flag) +{ + if( flag != 2) /* not comparing times only */ + { + if( pTimeStamp1->year != pTimeStamp2->year) + { + return((pTimeStamp1->year < pTimeStamp2->year) ? -1 : 1); + } + if( pTimeStamp1->month != pTimeStamp2->month) + { + return((pTimeStamp1->month < pTimeStamp2->month) ? -1 : 1); + } + if( pTimeStamp1->day != pTimeStamp2->day) + { + return((pTimeStamp1->day < pTimeStamp2->day) ? -1 : 1); + } + } + if( flag != 1) + { + if( pTimeStamp1->hour != pTimeStamp2->hour) + { + return((pTimeStamp1->hour < pTimeStamp2->hour) ? -1 : 1); + } + if( pTimeStamp1->minute != pTimeStamp2->minute) + { + return((pTimeStamp1->minute < pTimeStamp2->minute) ? -1 : 1); + } + if( pTimeStamp1->second != pTimeStamp2->second) + { + return((pTimeStamp1->second < pTimeStamp2->second) ? -1 : 1); + } + } + return( 0); +} + +/**************************************************************************** +Desc: Get the current time in milliseconds. +****************************************************************************/ +#if defined( FLM_UNIX) +unsigned f_timeGetMilliTime() +{ +#if defined( FLM_SOLARIS) + return( (unsigned)((FLMUINT64)gethrtime() / (FLMUINT64)1000000)); +#else + struct timeval tv; + + gettimeofday(&tv, 0); + + return( (((FLMUINT64)tv.tv_sec * (FLMUINT64)1000000) + + (FLMUINT64)tv.tv_usec) / 1000); +#endif +} +#endif diff --git a/ftk/src/ftkunix.cpp b/ftk/src/ftkunix.cpp new file mode 100644 index 0000000..b7f890e --- /dev/null +++ b/ftk/src/ftkunix.cpp @@ -0,0 +1,1618 @@ +//------------------------------------------------------------------------------ +// Desc: Contains the methods for the F_FileHdl class for UNIX. +// +// Tabs: 3 +// +// Copyright (c) 1999-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: fposix.cpp 3123 2006-01-24 17:19:50 -0700 (Tue, 24 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +#if defined( FLM_UNIX) + +#ifdef FLM_AIX + #ifndef _LARGE_FILES + #define _LARGE_FILES + #endif + #include +#endif + +#include +#ifndef FLM_OSX + #include +#endif + +#include + +#if defined( FLM_SOLARIS) + #include +#elif defined( FLM_LINUX) + #include +#elif defined( FLM_OSF) + + // Tru64 4.0 does not have this declaration. Tru64 5.0 renames statfs + // in vague ways, so we put these declarations before including + // + + // DSS NOTE: statfs declaration below conflicts with one found in + // sys/mount.h header file, so I commented it out. This was when I + // compiled using the GNU compiler. + + struct statfs; + #include +#endif + +/****************************************************************************** +Desc: +*******************************************************************************/ +F_FileHdl::F_FileHdl() +{ + m_pNext = NULL; + m_pPrev = NULL; + m_bInList = FALSE; + m_uiAvailTime = 0; + m_bFileOpened = FALSE; + m_bDeleteOnRelease = FALSE; + m_bOpenedReadOnly = FALSE; + m_pszFileName = NULL; + + m_fd = -1; + m_bDoDirectIO = FALSE; + m_uiExtendSize = 0; + m_uiMaxAutoExtendSize = gv_XFlmSysData.uiMaxFileSize; + m_uiBytesPerSector = 0; + m_ui64NotOnSectorBoundMask = 0; + m_ui64GetSectorBoundMask = 0; + m_uiExtendSize = 0; + m_ui64CurrentPos = 0; + m_bDoDirectIO = FALSE; + m_bCanDoAsync = FALSE; + m_pucAlignedBuff = NULL; + m_uiAlignedBuffSize = 0; +} + +/****************************************************************************** +Desc: +******************************************************************************/ +F_FileHdl::~F_FileHdl() +{ + if( m_bFileOpened) + { + (void)Close(); + } + + if( m_pucAlignedBuff) + { + free( m_pucAlignedBuff); + } + + if (m_pszFileName) + { + f_free( &m_pszFileName); + } +} + +/*************************************************************************** +Desc: Open or create a file. +***************************************************************************/ +RCODE F_FileHdl::OpenOrCreate( + const char * pszFileName, + FLMUINT uiAccess, + FLMBOOL bCreateFlag) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bDoDirectIO = FALSE; + char szSaveFileName[ F_PATH_MAX_SIZE]; + int openFlags = O_RDONLY; + +#if !defined( FLM_UNIX) || defined( FLM_LINUX) || defined( FLM_SOLARIS) + bDoDirectIO = (uiAccess & XFLM_IO_DIRECT) ? TRUE : FALSE; +#endif + +// HPUX needs this defined to access files larger than 2 GB. The Linux +// man pages *say* it's needed although as of Suse 9.1 it actually +// isn't. Including this flag on Linux anyway just it case... +#if defined( FLM_HPUX) || defined( FLM_LINUX) + openFlags |= O_LARGEFILE; +#endif + + // Save the file name in case we have to create the directory + + if( bCreateFlag && (uiAccess & XFLM_IO_CREATE_DIR)) + { + f_strcpy( &szSaveFileName, pszFileName); + } + + if( bCreateFlag) + { + openFlags |= O_CREAT; + if( uiAccess & XFLM_IO_EXCL) + { + openFlags |= O_EXCL; + } + else + { + openFlags |= O_TRUNC; + } + } + + if( !(uiAccess & XFLM_IO_RDONLY)) + { + openFlags |= O_RDWR; + } + + if( !(uiAccess & XFLM_IO_RDONLY)) + { + openFlags |= O_RDWR; + } + + // If doing direct IO, need to get the sector size. + + if( bDoDirectIO) + { + if( !m_uiBlockSize) + { + bDoDirectIO = FALSE; + } + else + { + if( RC_BAD( rc = gv_pFileSystem->GetSectorSize( + pszFileName, &m_uiBytesPerSector))) + { + goto Exit; + } + + m_ui64NotOnSectorBoundMask = m_uiBytesPerSector - 1; + m_ui64GetSectorBoundMask = ~m_ui64NotOnSectorBoundMask; + + // Can't do direct IO if the block size isn't a multiple of + // the sector size. + + if( m_uiBlockSize < m_uiBytesPerSector || + m_uiBlockSize % m_uiBytesPerSector != 0) + { + bDoDirectIO = FALSE; + } + else + { +#if defined( FLM_LINUX) + FLMUINT uiMajor = gv_XFlmSysData.uiLinuxMajorVer; + FLMUINT uiMinor = gv_XFlmSysData.uiLinuxMinorVer; + FLMUINT uiRevision = gv_XFlmSysData.uiLinuxRevision; + + if( uiMajor > 2 || (uiMajor == 2 && uiMinor > 6) || + (uiMajor == 2 && uiMinor == 6 && uiRevision >= 5)) + { + openFlags |= O_DIRECT; + + if( gv_XFlmSysData.bOkToDoAsyncWrites) + { + m_bCanDoAsync = TRUE; + } + } + else + { + bDoDirectIO = FALSE; + } +#elif defined( FLM_SOLARIS) + if( gv_XFlmSysData.bOkToDoAsyncWrites) + { + m_bCanDoAsync = TRUE; + } +#endif + } + } + } + +Retry_Create: + + // Try to create or open the file + + if ((m_fd = open( pszFileName, openFlags, 0600)) == -1) + { + if ((errno == ENOENT) && (uiAccess & XFLM_IO_CREATE_DIR)) + { + char szTemp[ F_PATH_MAX_SIZE]; + char szIoDirPath[ F_PATH_MAX_SIZE]; + + uiAccess &= ~XFLM_IO_CREATE_DIR; + + // Remove the file name for which we are creating the directory + + if( RC_OK( gv_pFileSystem->pathReduce( szSaveFileName, + szIoDirPath, szTemp))) + { + if( RC_OK( rc = gv_pFileSystem->CreateDir( szIoDirPath))) + { + goto Retry_Create; + } + else + { + goto Exit; + } + } + } +#ifdef FLM_LINUX + else if( errno == EINVAL && bDoDirectIO) + { + openFlags &= ~O_DIRECT; + bDoDirectIO = FALSE; + m_bCanDoAsync = FALSE; + goto Retry_Create; + } +#endif + + rc = MapErrnoToFlaimErr( errno, NE_XFLM_OPENING_FILE); + goto Exit; + } + +#if defined( FLM_SOLARIS) + if( bDoDirectIO) + { + directio( m_fd, DIRECTIO_ON); + } +#endif + + m_bDoDirectIO = bDoDirectIO; + +Exit: + + if( RC_BAD( rc)) + { + m_fd = -1; + m_bDoDirectIO = FALSE; + m_bCanDoAsync = FALSE; + } + + return( rc); +} + +/****************************************************************************** +Desc: Create a file +******************************************************************************/ +RCODE XFLMAPI F_FileHdl::Create( + const char * pszFileName, + FLMUINT uiIoFlags) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bFileOpened == FALSE); + + if( m_bDeleteOnRelease) + { + // This file handle had better not been used for another file + // before. Otherwise, we will get a memory leak. + + flmAssert( m_pszFileName == NULL); + + // Note: 'OpenOrCreate' will set m_pszFileName + + if( RC_BAD( rc = f_alloc( F_PATH_MAX_SIZE, &m_pszFileName))) + { + goto Exit; + } + + f_strcpy( m_pszFileName, pszFileName); + } + + if( RC_BAD( rc = OpenOrCreate( pszFileName, uiIoFlags, TRUE))) + { + goto Exit; + } + + m_bFileOpened = TRUE; + m_ui64CurrentPos = 0; + m_bOpenedExclusive = (uiIoFlags & XFLM_IO_SH_DENYRW) ? TRUE : FALSE; + +Exit: + + if( RC_BAD( rc) && m_bDeleteOnRelease && m_pszFileName) + { + f_free( &m_pszFileName); + } + + return( rc); +} + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_FileHdl::CreateUnique( + const char * pszDirName, + const char * pszFileExtension, + FLMUINT uiIoFlags) +{ + RCODE rc = NE_XFLM_OK; + char * pszTmp; + FLMBOOL bModext = TRUE; + FLMUINT uiBaseTime = 0; + FLMBYTE ucHighByte = 0; + char szFileName[ F_FILENAME_SIZE]; + char * pszDirPath; + char szDirPath[ F_PATH_MAX_SIZE]; + char szTmpPath[ F_PATH_MAX_SIZE]; + FLMUINT uiCount; + + flmAssert( !m_bFileOpened); + f_memset( szFileName, 0, sizeof( szFileName)); + + if( m_bDeleteOnRelease) + { + // This file handle had better not been used for another file + // before. Otherwise, we will get a memory leak. + + flmAssert( !m_pszFileName); + } + + if( !pszDirName || pszDirName[ 0] == '\0') + { + f_strcpy( szDirPath, "./"); + } + else + { + f_strcpy( szDirPath, pszDirName); + } + pszDirPath = &szDirPath [0]; + + // Search backwards replacing trailing spaces with NULLs. + + pszTmp = pszDirPath; + pszTmp += (f_strlen( pszTmp) - 1); + while (*pszTmp == ' ' && pszTmp >= pszDirPath) + { + *pszTmp = 0; + pszTmp--; + } + + // Append a slash if one isn't already there + + if (pszTmp >= pszDirPath && *pszTmp != '/') + { + pszTmp++; + *pszTmp++ = '/'; + } + else + { + pszTmp++; + } + *pszTmp = 0; + + if( pszFileExtension && f_strlen( pszFileExtension) >= 3) + { + bModext = FALSE; + } + + uiCount = 0; + do + { + gv_pFileSystem->pathCreateUniqueName( &uiBaseTime, szFileName, + pszFileExtension, + &ucHighByte, bModext); + + f_strcpy( szTmpPath, pszDirPath); + gv_pFileSystem->pathAppend( szTmpPath, szFileName); + if( m_pszFileName) + { + f_free( &m_pszFileName); + } + + rc = Create( szTmpPath, uiIoFlags | XFLM_IO_EXCL); + + if (rc == NE_XFLM_IO_DISK_FULL) + { + gv_pFileSystem->Delete( pszDirPath); + goto Exit; + } + + if( rc == NE_XFLM_IO_PATH_NOT_FOUND || rc == NE_XFLM_IO_INVALID_PASSWORD) + { + goto Exit; + } + } while ((rc != NE_XFLM_OK) && (uiCount++ < 10)); + + // Check if the path was created + + if( uiCount >= 10 && rc != NE_XFLM_OK) + { + rc = RC_SET( NE_XFLM_IO_PATH_CREATE_FAILURE); + goto Exit; + } + + m_bFileOpened = TRUE; + m_bOpenedExclusive = (uiIoFlags & XFLM_IO_SH_DENYRW) ? TRUE : FALSE; + + // Created file name needs to be returned. + + f_strcpy( pszDirName, szTmpPath); + +Exit: + + if( RC_BAD( rc) && m_pszFileName) + { + f_free( &m_pszFileName); + m_pszFileName = NULL; + } + + return( rc); +} + +/****************************************************************************** +Desc: Open a file +******************************************************************************/ +RCODE XFLMAPI F_FileHdl::Open( + const char * pszFileName, + FLMUINT uiIoFlags) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( !m_bFileOpened); + + if( m_bDeleteOnRelease) + { + // This file handle had better not been used for another file + // before. Otherwise, we will get a memory leak. + + flmAssert( !m_pszFileName); + + if( RC_BAD( rc = f_alloc( F_PATH_MAX_SIZE, &m_pszFileName))) + { + goto Exit; + } + } + + // Loop on error open conditions. + + for( ;;) + { + if( RC_OK( rc = OpenOrCreate( pszFileName, uiIoFlags, FALSE))) + { + break; + } + + if( rc != NE_XFLM_IO_TOO_MANY_OPEN_FILES) + { + goto Exit; + } + + // If for some reason we cannot open the file, then + // try to close some other file handle in the list. + + gv_XFlmSysData.pFileHdlMgr->releaseOneAvail( FALSE); + } + + m_bFileOpened = TRUE; + m_ui64CurrentPos = 0; + m_bOpenedReadOnly = (uiIoFlags & XFLM_IO_RDONLY) ? TRUE : FALSE; + m_bOpenedExclusive = (uiIoFlags & XFLM_IO_SH_DENYRW) ? TRUE : FALSE; + +Exit: + + if( RC_BAD( rc) && m_bDeleteOnRelease && m_pszFileName) + { + f_free( &m_pszFileName); + m_pszFileName = NULL; + } + + return( rc); +} + +/****************************************************************************** +Desc: Close a file +******************************************************************************/ +RCODE XFLMAPI F_FileHdl::Close( void) +{ + FLMBOOL bDeleteAllowed = TRUE; + RCODE rc = NE_XFLM_OK; + + if( !m_bFileOpened) + { + goto Exit; + } + + close( m_fd); + + m_fd = -1; + m_bFileOpened = FALSE; + m_bOpenedReadOnly = FALSE; + m_bOpenedExclusive = FALSE; + + if( m_bDeleteOnRelease) + { + flmAssert( m_pszFileName); + + if( bDeleteAllowed) + { + gv_pFileSystem->Delete( m_pszFileName); + } + m_bDeleteOnRelease = FALSE; + + f_free( &m_pszFileName); + m_pszFileName = NULL; + } + +Exit: + + return( rc); +} + +/****************************************************************************** +Desc: Make sure all file data is safely on disk +******************************************************************************/ +RCODE XFLMAPI F_FileHdl::Flush() +{ +#ifdef FLM_SOLARIS + // Direct I/O on Solaris is ADVISORY, meaning that the + // operating system may or may not actually honor the + // option for some or all operations on a given file. + // Thus, the only way to guarantee that writes are on disk + // is to call fdatasync. + // + // If a process is killed (with SIGKILL or SIGTERM), the + // dirty cache buffers associated with open files will be discarded unless + // the process intercepts the signal and properly closes the files. + // + // NOTES FROM THE UNIX MAN PAGES ON SIGNALS + // + // When killing a process or series of processes, it is common sense + // to start trying with the least dangerous signal, SIGTERM. That way, + // programs that care about an orderly shutdown get the chance to follow + // the procedures that they have been designed to execute when getting + // the SIGTERM signal, such as cleaning up and closing open files. If you + // send a SIGKILL to a process, you remove any chance for the process + // to do a tidy cleanup and shutdown, which might have unfortunate + // consequences. + + if( fdatasync( m_fd) != 0) + { + return( MapErrnoToFlaimErr( errno, NE_XFLM_FLUSHING_FILE)); + } +#else + if( !m_bDoDirectIO) + { + #ifdef FLM_OSX + if( fsync( m_fd) != 0) + #else + if( fdatasync( m_fd) != 0) + #endif + { + return( MapErrnoToFlaimErr( errno, NE_XFLM_FLUSHING_FILE)); + } + } +#endif + + return( NE_XFLM_OK); +} + +/****************************************************************************** +Desc: Read from a file +******************************************************************************/ +RCODE F_FileHdl::DirectRead( + FLMUINT64 ui64ReadOffset, + FLMUINT uiBytesToRead, + void * pvBuffer, + FLMBOOL bBuffHasFullSectors, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBytesRead; + FLMBYTE * pucReadBuffer; + FLMBYTE * pucDestBuffer; + FLMUINT uiMaxBytesToRead; + FLMINT iTmp; + FLMBOOL bHitEOF; + + flmAssert( m_bFileOpened); + flmAssert( m_bDoDirectIO); + + if( puiBytesRead) + { + *puiBytesRead = 0; + } + + if( ui64ReadOffset == XFLM_IO_CURRENT_POS) + { + ui64ReadOffset = m_ui64CurrentPos; + } + + // This loop does multiple reads (if necessary) to get all of the + // data. It uses aligned buffers and reads at sector offsets. + + pucDestBuffer = (FLMBYTE *)pvBuffer; + for (;;) + { + // See if we are using an aligned buffer. If not, allocate + // one (if not already allocated), and use it. + + if( (ui64ReadOffset & m_ui64NotOnSectorBoundMask) || + (((FLMUINT64)(FLMUINT)pucDestBuffer) & m_ui64NotOnSectorBoundMask) || + ((uiBytesToRead & m_ui64NotOnSectorBoundMask) && + !bBuffHasFullSectors)) + { + if( !m_pucAlignedBuff) + { + if( RC_BAD( rc = AllocAlignBuffer())) + { + goto Exit; + } + } + pucReadBuffer = m_pucAlignedBuff; + + // Must read enough bytes to cover all of the sectors that + // contain the data we are trying to read. The value of + // (ui64ReadOffset & m_ui64NotOnSectorBoundMask) will give us the + // number of additional bytes that are in the sector prior to + // the read offset. We then round that up to the next sector + // to get the total number of bytes we are going to read. + + uiMaxBytesToRead = (FLMUINT)RoundUpToSectorMultiple( + uiBytesToRead + (ui64ReadOffset & m_ui64NotOnSectorBoundMask)); + + // Can't read more than the aligned buffer will hold. + + if( uiMaxBytesToRead > m_uiAlignedBuffSize) + { + uiMaxBytesToRead = m_uiAlignedBuffSize; + } + } + else + { + uiMaxBytesToRead = (FLMUINT)RoundUpToSectorMultiple( uiBytesToRead); + flmAssert( uiMaxBytesToRead >= uiBytesToRead); + pucReadBuffer = pucDestBuffer; + } + + bHitEOF = FALSE; + + if( (iTmp = pread( m_fd, pucReadBuffer, + uiMaxBytesToRead, GetSectorStartOffset( ui64ReadOffset))) == -1) + { + rc = MapErrnoToFlaimErr( errno, NE_XFLM_READING_FILE); + goto Exit; + } + uiBytesRead = (FLMUINT)iTmp; + + if( uiBytesRead < uiMaxBytesToRead) + { + bHitEOF = TRUE; + } + + // If the offset we want to read from is not on a sector + // boundary, increment the read buffer pointer to the + // offset where the data we need starts and decrement the + // bytes read by the difference between the start of the + // sector and the actual read offset. + + if( ui64ReadOffset & m_ui64NotOnSectorBoundMask) + { + pucReadBuffer += (ui64ReadOffset & m_ui64NotOnSectorBoundMask); + flmAssert( uiBytesRead >= m_uiBytesPerSector); + uiBytesRead -= (FLMUINT)(ui64ReadOffset & m_ui64NotOnSectorBoundMask); + } + + // If bytes read is more than we actually need, truncate it back + // so that we only copy what we actually need. + + if( uiBytesRead > uiBytesToRead) + { + uiBytesRead = uiBytesToRead; + } + + uiBytesToRead -= uiBytesRead; + + if( puiBytesRead) + { + (*puiBytesRead) += uiBytesRead; + } + + m_ui64CurrentPos = ui64ReadOffset + uiBytesRead; + + // If using a different buffer for reading, copy the + // data read into the destination buffer. + + if( pucDestBuffer != pucReadBuffer) + { + f_memcpy( pucDestBuffer, pucReadBuffer, uiBytesRead); + } + + if( !uiBytesToRead) + { + break; + } + + // Still more to read - did we hit EOF above? + + if( bHitEOF) + { + rc = RC_SET( NE_XFLM_IO_END_OF_FILE); + break; + } + + pucDestBuffer += uiBytesRead; + ui64ReadOffset += uiBytesRead; + } + +Exit: + + return( rc); +} + +/****************************************************************************** +Desc: Read from a file +******************************************************************************/ +RCODE XFLMAPI F_FileHdl::Read( + FLMUINT64 ui64ReadOffset, + FLMUINT uiBytesToRead, + void * pvBuffer, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_XFLM_OK; + FLMINT iBytesRead; + + flmAssert( m_bFileOpened); + + if( m_bDoDirectIO) + { + rc = DirectRead( ui64ReadOffset, uiBytesToRead, + pvBuffer, FALSE, puiBytesRead); + goto Exit; + } + + if( ui64ReadOffset == XFLM_IO_CURRENT_POS) + { + ui64ReadOffset = m_ui64CurrentPos; + } + + if( (iBytesRead = pread( m_fd, pvBuffer, + uiBytesToRead, ui64ReadOffset)) == -1) + { + rc = MapErrnoToFlaimErr(errno, NE_XFLM_READING_FILE); + goto Exit; + } + + if( puiBytesRead) + { + *puiBytesRead = (FLMUINT)iBytesRead; + } + + m_ui64CurrentPos = ui64ReadOffset + (FLMUINT)iBytesRead; + + if( (FLMUINT)iBytesRead < uiBytesToRead) + { + rc = RC_SET( NE_XFLM_IO_END_OF_FILE); + goto Exit; + } + +Exit: + + return( rc); +} + +/****************************************************************************** +Note: This function assumes that the pvBuffer that is passed in is + a multiple of a the sector size. +******************************************************************************/ +RCODE XFLMAPI F_FileHdl::SectorRead( + FLMUINT64 ui64ReadOffset, + FLMUINT uiBytesToRead, + void * pvBuffer, + FLMUINT * puiBytesRead) +{ + if( m_bDoDirectIO) + { + return( DirectRead( ui64ReadOffset, uiBytesToRead, + pvBuffer, TRUE, puiBytesRead)); + } + else + { + return( Read( ui64ReadOffset, uiBytesToRead, pvBuffer, puiBytesRead)); + } +} + +/****************************************************************************** +Desc: Sets current position of file. +******************************************************************************/ +RCODE XFLMAPI F_FileHdl::Seek( + FLMUINT64 ui64Offset, + FLMINT iWhence, + FLMUINT64 * pui64NewOffset) +{ + RCODE rc = NE_XFLM_OK; + + switch( iWhence) + { + case XFLM_IO_SEEK_CUR: + { + m_ui64CurrentPos += ui64Offset; + break; + } + + case XFLM_IO_SEEK_SET: + { + m_ui64CurrentPos = ui64Offset; + break; + } + + case XFLM_IO_SEEK_END: + { + if( RC_BAD( rc = Size( &m_ui64CurrentPos))) + { + goto Exit; + } + + break; + } + + default: + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + } + + if( pui64NewOffset) + { + *pui64NewOffset = m_ui64CurrentPos; + } + +Exit: + + return( rc); +} + +/****************************************************************************** +Desc: Return the size of the file +******************************************************************************/ +RCODE XFLMAPI F_FileHdl::Size( + FLMUINT64 * pui64Size) +{ + RCODE rc = NE_XFLM_OK; + struct stat statBuf; + + if( fstat( m_fd, &statBuf) == -1) + { + rc = MapErrnoToFlaimErr( errno, NE_XFLM_GETTING_FILE_SIZE); + goto Exit; + } + + *pui64Size = statBuf.st_size; + +Exit: + + return( rc); +} + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_FileHdl::Tell( + FLMUINT64 * pui64Offset) +{ + *pui64Offset = m_ui64CurrentPos; + return( NE_XFLM_OK); +} + +/****************************************************************************** +Desc: Truncate the file to the indicated size +******************************************************************************/ +RCODE XFLMAPI F_FileHdl::Truncate( + FLMUINT64 ui64Size) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bFileOpened); + + if( ftruncate( m_fd, ui64Size) == -1) + { + rc = MapErrnoToFlaimErr( errno, NE_XFLM_TRUNCATING_FILE); + goto Exit; + } + +Exit: + + return( rc); +} + +/****************************************************************************** +Desc: Write to a file +******************************************************************************/ +RCODE XFLMAPI F_FileHdl::Write( + FLMUINT64 ui64WriteOffset, + FLMUINT uiBytesToWrite, + const void * pvBuffer, + FLMUINT * puiBytesWrittenRV) +{ + RCODE rc = NE_XFLM_OK; + FLMINT iBytesWritten = 0; + + flmAssert( m_bFileOpened); + + if( m_bDoDirectIO) + { + rc = DirectWrite( ui64WriteOffset, uiBytesToWrite, pvBuffer, + uiBytesToWrite, NULL, puiBytesWrittenRV, FALSE, TRUE); + goto Exit; + } + + if( ui64WriteOffset == XFLM_IO_CURRENT_POS) + { + ui64WriteOffset = m_ui64CurrentPos; + } + + if( (iBytesWritten = pwrite( m_fd, pvBuffer, + uiBytesToWrite, ui64WriteOffset)) == -1) + { + rc = MapErrnoToFlaimErr(errno, NE_XFLM_WRITING_FILE); + goto Exit; + } + + if( puiBytesWrittenRV) + { + *puiBytesWrittenRV = (FLMUINT)iBytesWritten; + } + + m_ui64CurrentPos = ui64WriteOffset + (FLMUINT)iBytesWritten; + + if( (FLMUINT)iBytesWritten < uiBytesToWrite) + { + rc = RC_SET( NE_XFLM_IO_DISK_FULL); + goto Exit; + } + +Exit: + + return( rc); +} + +/****************************************************************************** +Desc: Allocate an aligned buffer. +******************************************************************************/ +RCODE F_FileHdl::AllocAlignBuffer( void) +{ + RCODE rc = NE_XFLM_OK; + + if( m_pucAlignedBuff) + { + goto Exit; + } + + // Allocate at least 64K - this will handle most read and write + // operations and will also be a multiple of the sector size most of + // the time. The calculation below rounds it up to the next sector + // boundary if it is not already on one. + + m_uiAlignedBuffSize = (FLMUINT)RoundUpToSectorMultiple( 64 * 1024); + +#if defined( FLM_SOLARIS) + if( (m_pucAlignedBuff = (FLMBYTE *)memalign( + sysconf( _SC_PAGESIZE), m_uiAlignedBuffSize)) == NULL) +#elif defined( FLM_OSX) + if( (m_pucAlignedBuff = (FLMBYTE *)malloc( m_uiAlignedBuffSize)) == NULL) +#else + if( posix_memalign( (void **)&m_pucAlignedBuff, + sysconf( _SC_PAGESIZE), m_uiAlignedBuffSize) == -1) +#endif + { + m_uiAlignedBuffSize = 0; + rc = MapErrnoToFlaimErr( errno, NE_XFLM_MEM); + goto Exit; + } + +Exit: + + return( rc); +} + +/****************************************************************************** +Note: This routine assumes that the size of pvBuffer is a multiple of + sector size and can be used to write out full sectors. Even if + uiBytesToWrite does not account for full sectors, data from the + buffer will still be written out - a partial sector on disk will + not be preserved. +******************************************************************************/ +RCODE F_FileHdl::DirectWrite( + FLMUINT64 ui64WriteOffset, + FLMUINT uiBytesToWrite, + const void * pvBuffer, + FLMUINT, + F_IOBuffer * pBufferObj, + FLMUINT * puiBytesWrittenRV, + FLMBOOL bBuffHasFullSectors, + FLMBOOL bZeroFill) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBytesRead; + FLMUINT uiMaxBytesToWrite; + FLMUINT uiBytesBeingOutput; + FLMBYTE * pucWriteBuffer; + FLMBYTE * pucSrcBuffer; +#ifdef FLM_DEBUG + FLMBOOL bDoAsync = (pBufferObj != NULL) + ? TRUE + : FALSE; +#endif + FLMBOOL bDidAsync = FALSE; + FLMUINT uiLastWriteOffset; + FLMUINT uiLastWriteSize; + + flmAssert( m_bFileOpened); + +#ifdef FLM_DEBUG + if( bDoAsync) + { + flmAssert( m_bCanDoAsync); + } +#endif + + if( puiBytesWrittenRV) + { + *puiBytesWrittenRV = 0; + } + + if( ui64WriteOffset == XFLM_IO_CURRENT_POS) + { + ui64WriteOffset = m_ui64CurrentPos; + } + + // This loop is for direct IO - must make sure we use + // aligned buffers. + + pucSrcBuffer = (FLMBYTE *)pvBuffer; + for (;;) + { + // See if we are using an aligned buffer. If not, allocate + // one (if not already allocated), and use it. + + if( (ui64WriteOffset & m_ui64NotOnSectorBoundMask) || + (((FLMUINT64)(FLMUINT)pucSrcBuffer) & m_ui64NotOnSectorBoundMask) || + ((uiBytesToWrite & m_ui64NotOnSectorBoundMask) && !bBuffHasFullSectors)) + { + // Cannot be using a temporary write buffer if we are doing + // asynchronous writes! + + flmAssert( !bDoAsync || !m_bCanDoAsync); + + if( !m_pucAlignedBuff) + { + if( RC_BAD( rc = AllocAlignBuffer())) + { + goto Exit; + } + } + pucWriteBuffer = m_pucAlignedBuff; + + // Must write enough bytes to cover all of the sectors that + // contain the data we are trying to write out. The value of + // (ui64WriteOffset & m_ui64NotOnSectorBoundMask) will give us the + // number of additional bytes that are in the sector prior to + // the read offset. We then round to the next sector to get the + // total number of bytes we are going to write. + + uiMaxBytesToWrite = (FLMUINT)RoundUpToSectorMultiple( + uiBytesToWrite + (ui64WriteOffset & m_ui64NotOnSectorBoundMask)); + + // Can't write more than the aligned buffer will hold. + + if( uiMaxBytesToWrite > m_uiAlignedBuffSize) + { + uiMaxBytesToWrite = m_uiAlignedBuffSize; + uiBytesBeingOutput = uiMaxBytesToWrite - + (FLMUINT)(ui64WriteOffset & m_ui64NotOnSectorBoundMask); + } + else + { + uiBytesBeingOutput = uiBytesToWrite; + } + + // If the write offset is not on a sector boundary, or if + // we are writing a partial sector, we must read the + // sector into the buffer. + + if( (ui64WriteOffset & m_ui64NotOnSectorBoundMask) || + (uiBytesBeingOutput < m_uiBytesPerSector && !bBuffHasFullSectors)) + { + // Read the first sector that is to be written out. + // Read one sector's worth of data - so that we will + // preserve what is already in the sector before + // writing it back out again. + + if( RC_BAD( rc = Read( GetSectorStartOffset( ui64WriteOffset), + m_uiBytesPerSector, pucWriteBuffer, &uiBytesRead))) + { + if( rc != NE_XFLM_IO_END_OF_FILE) + { + goto Exit; + } + + rc = NE_XFLM_OK; + f_memset( &pucWriteBuffer[ uiBytesRead], 0, + m_uiBytesPerSector - uiBytesRead); + } + } + + // Finally, copy the data from the source buffer into the + // write buffer. + + f_memcpy( &pucWriteBuffer[ ui64WriteOffset & m_ui64NotOnSectorBoundMask], + pucSrcBuffer, uiBytesBeingOutput); + } + else + { + uiMaxBytesToWrite = (FLMUINT)RoundUpToSectorMultiple( uiBytesToWrite); + uiBytesBeingOutput = uiBytesToWrite; + pucWriteBuffer = pucSrcBuffer; + + if( bZeroFill && uiMaxBytesToWrite > uiBytesToWrite) + { + f_memset( &pucWriteBuffer[ uiBytesToWrite], 0, + uiMaxBytesToWrite - uiBytesToWrite); + } + } + + // Position the file to the nearest sector below the write offset. + + uiLastWriteOffset = (FLMUINT)GetSectorStartOffset( ui64WriteOffset); + uiLastWriteSize = uiMaxBytesToWrite; + + if( !m_bCanDoAsync || !pBufferObj) + { + FLMINT iBytesWritten; + + if( (iBytesWritten = pwrite( m_fd, + pucWriteBuffer, uiMaxBytesToWrite, uiLastWriteOffset)) == -1) + { + rc = MapErrnoToFlaimErr( errno, NE_XFLM_WRITING_FILE); + goto Exit; + } + + if( (FLMUINT)iBytesWritten < uiMaxBytesToWrite) + { + rc = RC_SET( NE_XFLM_IO_DISK_FULL); + goto Exit; + } + } + else + { + struct aiocb * pAio = pBufferObj->getAIOStruct(); + + f_memset( pAio, 0, sizeof( struct aiocb)); + pAio->aio_lio_opcode = LIO_WRITE; + pAio->aio_sigevent.sigev_notify = SIGEV_NONE; + pAio->aio_fildes = m_fd; + pAio->aio_offset = uiLastWriteOffset; + pAio->aio_nbytes = uiMaxBytesToWrite; + pAio->aio_buf = pucWriteBuffer; + + if( aio_write( pAio) == -1) + { + rc = MapErrnoToFlaimErr( errno, NE_XFLM_WRITING_FILE); + goto Exit; + } + + pBufferObj->makePending(); + bDidAsync = TRUE; + } + + uiBytesToWrite -= uiBytesBeingOutput; + if( puiBytesWrittenRV) + { + (*puiBytesWrittenRV) += uiBytesBeingOutput; + } + + m_ui64CurrentPos = ui64WriteOffset + uiBytesBeingOutput; + + if( !uiBytesToWrite) + { + break; + } + + flmAssert( !pBufferObj); + + pucSrcBuffer += uiBytesBeingOutput; + ui64WriteOffset += uiBytesBeingOutput; + } + +Exit: + + if( !bDidAsync && pBufferObj) + { + pBufferObj->notifyComplete( rc); + } + + return( rc); +} + +/****************************************************************************** +Desc: Returns flag indicating whether or not we can do async writes. +******************************************************************************/ +FLMBOOL XFLMAPI F_FileHdl::CanDoAsync() +{ + return( m_bCanDoAsync); +} + +/****************************************************************************** +Desc: Attempts to lock byte 0 of the file. This method is used to + lock byte 0 of the .lck file to ensure that only one process + has access to a database. +******************************************************************************/ +RCODE XFLMAPI F_FileHdl::Lock( void) +{ + RCODE rc = NE_XFLM_OK; + struct flock LockStruct; + + // Lock first byte in file + + f_memset( &LockStruct, 0, sizeof( LockStruct)); + LockStruct.l_type = F_WRLCK; + LockStruct.l_whence = SEEK_SET; + LockStruct.l_start = 0; + LockStruct.l_len = 1; + + if( fcntl( m_fd, F_SETLK, &LockStruct) == -1) + { + rc = RC_SET( NE_XFLM_IO_FILE_LOCK_ERR); + goto Exit; + } + +Exit: + + return( rc); +} + +/****************************************************************************** +Desc: Attempts to unlock byte 0 of the file. +******************************************************************************/ +RCODE XFLMAPI F_FileHdl::Unlock( void) +{ + RCODE rc = NE_XFLM_OK; + struct flock LockStruct; + + // Lock first byte in file + + f_memset( &LockStruct, 0, sizeof( LockStruct)); + LockStruct.l_type = F_UNLCK; + LockStruct.l_whence = SEEK_SET; + LockStruct.l_start = 0; + LockStruct.l_len = 1; + + if( fcntl( m_fd, F_SETLK, &LockStruct) == -1) + { + rc = RC_SET( NE_XFLM_IO_FILE_UNLOCK_ERR); + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Determines the kernel version of the linux system we are running on +***************************************************************************/ +#ifdef FLM_LINUX +void flmGetLinuxKernelVersion( + FLMUINT * puiMajor, + FLMUINT * puiMinor, + FLMUINT * puiRevision) +{ + int fd = -1; + int iBytesRead; + char szBuffer [80]; + char * pszVer; + FLMUINT uiMajorVer = 0; + FLMUINT uiMinorVer = 0; + FLMUINT uiRevision = 0; + + if( (fd = open( "/proc/version", O_RDONLY, 0600)) == -1) + { + goto Exit; + } + + if( (iBytesRead = read( fd, szBuffer, sizeof( szBuffer))) == -1) + { + goto Exit; + } + if( (pszVer = (char *)f_strstr( + (FLMBYTE *)szBuffer, (FLMBYTE *)"version ")) == NULL) + { + goto Exit; + } + pszVer += 8; + + while( *pszVer >= '0' && *pszVer <= '9') + { + uiMajorVer *= 10; + uiMajorVer += (FLMUINT)(*pszVer - '0'); + pszVer++; + } + + if( *pszVer == '.') + { + pszVer++; + while (*pszVer >= '0' && *pszVer <= '9') + { + uiMinorVer *= 10; + uiMinorVer += (FLMUINT)(*pszVer - '0'); + pszVer++; + } + } + + if( *pszVer == '.') + { + pszVer++; + while (*pszVer >= '0' && *pszVer <= '9') + { + uiRevision *= 10; + uiRevision += (FLMUINT)(*pszVer - '0'); + pszVer++; + } + } + +Exit: + + if( fd != -1) + { + close( fd); + } + + if( puiMajor) + { + *puiMajor = uiMajorVer; + } + + if( puiMinor) + { + *puiMinor = uiMinorVer; + } + + if( puiRevision) + { + *puiRevision = uiRevision; + } +} +#endif + +/*************************************************************************** +Desc: Determines if the linux system we are running on is 2.4 or greater. +***************************************************************************/ +#ifdef FLM_LINUX +FLMUINT flmGetLinuxMaxFileSize( void) +{ +#ifdef FLM_32BIT + return( XFLM_MAXIMUM_FILE_SIZE); +#else + FLMUINT uiMaxFileSize = 0x7FF00000; + + flmAssert( gv_XFlmSysData.uiLinuxMajorVer); + + // Is version 2.4 or greater? + + if( gv_XFlmSysData.uiLinuxMajorVer > 2 || + (gv_XFlmSysData.uiLinuxMajorVer == 2 && + gv_XFlmSysData.uiLinuxMinorVer >= 4)) + { + uiMaxFileSize = XFLM_MAXIMUM_FILE_SIZE; + } + + return( uiMaxFileSize); +#endif +} +#endif + +/*************************************************************************** +Desc: +***************************************************************************/ +#ifdef FLM_LINUX +FINLINE FLMUINT64 flmGetLinuxMemInfoValue( + char * pszMemInfoBuffer, + char * pszTag) +{ + char * pszTmp; + FLMUINT64 ui64Bytes = 0; + + if( (pszTmp = (char *)f_strstr( + (FLMBYTE *)pszMemInfoBuffer, (FLMBYTE *)pszTag)) == NULL) + { + return( 0); + } + + pszTmp += f_strlen( pszTag); + + while( *pszTmp == ASCII_SPACE) + { + pszTmp++; + } + + while( *pszTmp >= '0' && *pszTmp <= '9') + { + ui64Bytes *= 10; + ui64Bytes += (FLMUINT)(*pszTmp - '0'); + pszTmp++; + } + + return( ui64Bytes * 1024); +} +#endif + +/*************************************************************************** +Desc: +***************************************************************************/ +#ifdef FLM_LINUX +void flmGetLinuxMemInfo( + FLMUINT64 * pui64TotalMem, + FLMUINT64 * pui64AvailMem) +{ + int fd = -1; + int iBytesRead; + int iMemInfoBufSize = 4096; + char * pszMemInfoBuf = NULL; + FLMUINT64 ui64TotalMem = 0; + FLMUINT64 ui64AvailMem = 0; + + if( (pszMemInfoBuf = (char *)malloc( iMemInfoBufSize)) == NULL) + { + goto Exit; + } + + if( (fd = open( "/proc/meminfo", O_RDONLY, 0600)) == -1) + { + goto Exit; + } + + if( (iBytesRead = read( fd, pszMemInfoBuf, iMemInfoBufSize - 1)) == -1) + { + goto Exit; + } + + pszMemInfoBuf[ iBytesRead] = 0; + + if( (ui64TotalMem = + flmGetLinuxMemInfoValue( pszMemInfoBuf, "MemTotal:")) != 0) + { + ui64AvailMem = + flmGetLinuxMemInfoValue( pszMemInfoBuf, "MemFree:") + + flmGetLinuxMemInfoValue( pszMemInfoBuf, "Buffers:") + + flmGetLinuxMemInfoValue( pszMemInfoBuf, "Cached:"); + } + +Exit: + + if( pui64TotalMem) + { + *pui64TotalMem = ui64TotalMem; + } + + if( pui64AvailMem) + { + *pui64AvailMem = ui64AvailMem; + } + + if( pszMemInfoBuf) + { + free( pszMemInfoBuf); + } + + if( fd != -1) + { + close( fd); + } +} +#endif + +/**************************************************************************** +Desc: This routine gets the block size for the file system a file belongs to. +****************************************************************************/ +FLMUINT flmGetFSBlockSize( + FLMBYTE * pszFileName) +{ + FLMUINT uiFSBlkSize = 4096; + FLMBYTE * pszTmp = pszFileName + f_strlen( pszFileName) - 1; + FLMBYTE * pszDir; + FLMBYTE ucRestoreByte = 0; + + while( pszTmp != pszFileName && *pszTmp != '/') + { + pszTmp--; + } + + if( *pszTmp == '/') + { + if (pszTmp == pszFileName) + { + pszTmp++; + } + ucRestoreByte = *pszTmp; + *pszTmp = 0; + pszDir = pszFileName; + } + else + { + pszDir = (FLMBYTE *)"."; + } + +#if defined( FLM_SOLARIS) + struct statvfs statfsbuf; + if (statvfs( (char *)pszDir, &statfsbuf) == 0) + { + uiFSBlkSize = (FLMUINT)statfsbuf.f_bsize; + } +#elif defined( FLM_LINUX) || defined( FLM_OSF) + struct statfs statfsbuf; + if (statfs( (char *)pszDir, &statfsbuf) == 0) + { + uiFSBlkSize = (FLMUINT)statfsbuf.f_bsize; + } +#endif + + if( ucRestoreByte) + { + *pszTmp = ucRestoreByte; + } + + return( uiFSBlkSize); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_SOLARIS) && defined( FLM_SPARC) && !defined( FLM_GNUC) +static void sparc_asm_code( void) +{ + asm( ".align 8"); + asm( ".global sparc_atomic_add_32"); + asm( ".type sparc_atomic_add_32, #function"); + asm( "sparc_atomic_add_32:"); + asm( " membar #LoadLoad | #LoadStore | #StoreStore | #StoreLoad"); + asm( " ld [%o0], %l0"); + asm( " add %l0, %o1, %l2"); + asm( " cas [%o0], %l0, %l2"); + asm( " cmp %l0, %l2"); + asm( " bne sparc_atomic_add_32"); + asm( " nop"); + asm( " add %l2, %o1, %o0"); + asm( " membar #LoadLoad | #LoadStore | #StoreStore | #StoreLoad"); + asm( "retl"); + asm( "nop"); + + asm( ".align 8"); + asm( ".global sparc_atomic_xchg_32"); + asm( ".type sparc_atomic_xchg_32, #function"); + asm( "sparc_atomic_xchg_32:"); + asm( " membar #LoadLoad | #LoadStore | #StoreStore | #StoreLoad"); + asm( " ld [%o0], %l0"); + asm( " mov %o1, %l1"); + asm( " cas [%o0], %l0, %l1"); + asm( " cmp %l0, %l1"); + asm( " bne sparc_atomic_xchg_32"); + asm( " nop"); + asm( " mov %l0, %o0"); + asm( " membar #LoadLoad | #LoadStore | #StoreStore | #StoreLoad"); + asm( "retl"); + asm( "nop"); +} +#endif + +#endif // FLM_UNIX + +#if defined( FLM_WATCOM_NLM) + int fposixDummy(void) + { + return( 0); + } +#endif diff --git a/ftk/src/ftkwin.cpp b/ftk/src/ftkwin.cpp new file mode 100644 index 0000000..5b0a652 --- /dev/null +++ b/ftk/src/ftkwin.cpp @@ -0,0 +1,1682 @@ +//------------------------------------------------------------------------------ +// Desc: Contains the methods for the F_FileHdl class on Windows platforms. +// +// Tabs: 3 +// +// Copyright (c) 1999-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: fwin.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +#if defined( FLM_WIN) + +RCODE MapWinErrorToFlaim( + DWORD udErrCode, + RCODE defaultRc); + +FSTATIC RCODE _DeleteFile( + char * path); + +/*************************************************************************** +Desc: Maps WIN errors to IO errors. +***************************************************************************/ +RCODE MapWinErrorToFlaim( + DWORD udErrCode, + RCODE defaultRc) +{ + + // Switch on passed in error code value + + switch( udErrCode) + { + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_OUTOFMEMORY: + return( RC_SET( NE_FLM_MEM)); + + case ERROR_BAD_NETPATH: + case ERROR_BAD_PATHNAME: + case ERROR_DIRECTORY: + case ERROR_FILE_NOT_FOUND: + case ERROR_INVALID_DRIVE: + case ERROR_INVALID_NAME: + case ERROR_NO_NET_OR_BAD_PATH: + case ERROR_PATH_NOT_FOUND: + return( RC_SET( NE_FLM_IO_PATH_NOT_FOUND)); + + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_FILE_EXISTS: + case ERROR_ALREADY_EXISTS: + return( RC_SET( NE_FLM_IO_ACCESS_DENIED)); + + case ERROR_BUFFER_OVERFLOW: + case ERROR_FILENAME_EXCED_RANGE: + return( RC_SET( NE_FLM_IO_PATH_TOO_LONG)); + + case ERROR_DISK_FULL: + case ERROR_HANDLE_DISK_FULL: + return( RC_SET( NE_FLM_IO_DISK_FULL)); + + case ERROR_CURRENT_DIRECTORY: + case ERROR_DIR_NOT_EMPTY: + return( RC_SET( NE_FLM_IO_DIRECTORY_ERR)); + + case ERROR_DIRECT_ACCESS_HANDLE: + case ERROR_INVALID_HANDLE: + case ERROR_INVALID_TARGET_HANDLE: + return( RC_SET( NE_FLM_IO_BAD_FILE_HANDLE)); + + case ERROR_HANDLE_EOF: + return( RC_SET( NE_FLM_IO_END_OF_FILE)); + + case ERROR_OPEN_FAILED: + return( RC_SET( NE_FLM_IO_OPEN_ERR)); + + case ERROR_CANNOT_MAKE: + return( RC_SET( NE_FLM_IO_PATH_CREATE_FAILURE)); + + case ERROR_LOCK_FAILED: + case ERROR_LOCK_VIOLATION: + return( RC_SET( NE_FLM_IO_FILE_LOCK_ERR)); + + case ERROR_NEGATIVE_SEEK: + case ERROR_SEEK: + case ERROR_SEEK_ON_DEVICE: + return( RC_SET( NE_FLM_IO_SEEK_ERR)); + + case ERROR_NO_MORE_FILES: + case ERROR_NO_MORE_SEARCH_HANDLES: + return( RC_SET( NE_FLM_IO_NO_MORE_FILES)); + + case ERROR_TOO_MANY_OPEN_FILES: + return( RC_SET( NE_FLM_IO_TOO_MANY_OPEN_FILES)); + + case NO_ERROR: + return( NE_FLM_OK); + + case ERROR_DISK_CORRUPT: + case ERROR_DISK_OPERATION_FAILED: + case ERROR_FILE_CORRUPT: + case ERROR_FILE_INVALID: + case ERROR_NOT_SAME_DEVICE: + case ERROR_IO_DEVICE: + default: + return( RC_SET( defaultRc)); + + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_FileHdl::F_FileHdl() +{ + m_pNext = NULL; + m_pPrev = NULL; + m_bInList = FALSE; + m_uiAvailTime = 0; + m_bFileOpened = FALSE; + m_bDeleteOnRelease = FALSE; + m_bOpenedReadOnly = FALSE; + m_pszFileName = NULL; + + m_FileHandle = INVALID_HANDLE_VALUE; + m_uiBlockSize = 0; + m_uiBytesPerSector = 0; + m_ui64NotOnSectorBoundMask = 0; + m_ui64GetSectorBoundMask = 0; + m_bDoDirectIO = FALSE; + m_uiExtendSize = 0; +// m_uiMaxAutoExtendSize = gv_XFlmSysData.uiMaxFileSize; + m_pucAlignedBuff = NULL; + m_uiAlignedBuffSize = 0; + m_ui64CurrentPos = 0; + m_bCanDoAsync = FALSE; + m_Overlapped.hEvent = NULL; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_FileHdl::~F_FileHdl() +{ + if( m_bFileOpened) + { + (void)close(); + } + + if (m_pucAlignedBuff) + { + (void)VirtualFree( m_pucAlignedBuff, 0, MEM_RELEASE); + m_pucAlignedBuff = NULL; + m_uiAlignedBuffSize = 0; + } + + if (m_Overlapped.hEvent) + { + CloseHandle( m_Overlapped.hEvent); + } + + if (m_pszFileName) + { + f_free( &m_pszFileName); + } +} + +/*************************************************************************** +Desc: Open or create a file. +***************************************************************************/ +RCODE F_FileHdl::openOrCreate( + const char * pszFileName, + FLMUINT uiAccess, + FLMBOOL bCreateFlag) +{ + RCODE rc = NE_FLM_OK; + char szSaveFileName[ F_PATH_MAX_SIZE]; + DWORD udAccessMode = 0; + DWORD udShareMode = 0; + DWORD udCreateMode = 0; + DWORD udAttrFlags = 0; + DWORD udErrCode; + + m_bDoDirectIO = (uiAccess & FLM_IO_DIRECT) ? TRUE : FALSE; + + /* Save the file name in case we have to create the directory. */ + + if ((bCreateFlag) && (uiAccess & FLM_IO_CREATE_DIR)) + { + f_strcpy( szSaveFileName, pszFileName); + } + + // If doing direct IO, need to get the sector size. + + if (m_bDoDirectIO) + { + if (!m_uiBlockSize) + { + m_bDoDirectIO = FALSE; + } + else + { + if (RC_BAD( rc = gv_pFileSystem->getSectorSize( + pszFileName, &m_uiBytesPerSector))) + { + goto Exit; + } + + m_ui64NotOnSectorBoundMask = m_uiBytesPerSector - 1; + m_ui64GetSectorBoundMask = ~m_ui64NotOnSectorBoundMask; + + // Can't do direct IO if the block size isn't a multiple of + // the sector size. + + if (m_uiBlockSize < m_uiBytesPerSector || + m_uiBlockSize % m_uiBytesPerSector != 0) + { + m_bDoDirectIO = FALSE; + } + } + } + + // Only enable asynchronous writes if direct I/O is enabled. + + if (m_bDoDirectIO) + { + m_bCanDoAsync = TRUE; + } + + // Set up the file characteristics requested by caller. + + if (uiAccess & FLM_IO_SH_DENYRW) + { + udShareMode = 0; + uiAccess &= ~FLM_IO_SH_DENYRW; + } + else if (uiAccess & FLM_IO_SH_DENYWR) + { + udShareMode = FILE_SHARE_READ; + uiAccess &= ~FLM_IO_SH_DENYWR; + } + else if (uiAccess & FLM_IO_SH_DENYNONE) + { + udShareMode = (FILE_SHARE_READ | FILE_SHARE_WRITE); + uiAccess &= ~FLM_IO_SH_DENYNONE; + } + else + { + udShareMode = (FILE_SHARE_READ | FILE_SHARE_WRITE); + } + + // Begin setting the CreateFile flags and fields + + udAttrFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS; + if (m_bDoDirectIO) + { + udAttrFlags |= FILE_FLAG_NO_BUFFERING; + } + + if (m_bCanDoAsync) + { + udAttrFlags |= FILE_FLAG_OVERLAPPED; + } + + if (bCreateFlag) + { + if (uiAccess & FLM_IO_EXCL) + { + udCreateMode = CREATE_NEW; + } + else + { + udCreateMode = CREATE_ALWAYS; + } + } + else + { + udCreateMode = OPEN_EXISTING; + } + + udAccessMode = GENERIC_READ | GENERIC_WRITE; + + if( (!bCreateFlag) && (uiAccess & FLM_IO_RDONLY)) + { + udAccessMode = GENERIC_READ; + } + +Retry_Create: + + // Try to create or open the file + + if( (m_FileHandle = CreateFile( (LPCTSTR)pszFileName, udAccessMode, + udShareMode, NULL, udCreateMode, + udAttrFlags, NULL)) == INVALID_HANDLE_VALUE) + { + udErrCode = GetLastError(); + if ((udErrCode == ERROR_PATH_NOT_FOUND) && (uiAccess & FLM_IO_CREATE_DIR)) + { + char szTemp[ F_PATH_MAX_SIZE]; + char szDirPath[ F_PATH_MAX_SIZE]; + + uiAccess &= ~FLM_IO_CREATE_DIR; + + // Remove the file name for which we are creating the directory. + + if( RC_OK( gv_pFileSystem->pathReduce( szSaveFileName, szDirPath, szTemp))) + { + if( RC_OK( rc = gv_pFileSystem->createDir( szDirPath))) + { + goto Retry_Create; + } + else + { + goto Exit; + } + } + } + + rc = MapWinErrorToFlaim( udErrCode, + (RCODE)(bCreateFlag + ? (RCODE)(m_bDoDirectIO + ? (RCODE)NE_FLM_DIRECT_CREATING_FILE + : (RCODE)NE_FLM_CREATING_FILE) + : (RCODE)(m_bDoDirectIO + ? (RCODE)NE_FLM_DIRECT_OPENING_FILE + : (RCODE)NE_FLM_OPENING_FILE))); + goto Exit; + } + +Exit: + + if( RC_BAD( rc)) + { + m_FileHandle = INVALID_HANDLE_VALUE; + } + + return( rc); +} + +/**************************************************************************** +Desc: Create a file +****************************************************************************/ +RCODE FLMAPI F_FileHdl::create( + const char * pszFileName, + FLMUINT uiIoFlags ) +{ + RCODE rc = NE_FLM_OK; + + flmAssert( m_bFileOpened == FALSE); + + if( m_bDeleteOnRelease) + { + // This file handle had better not been used for another file + // before. Otherwise, we will get a memory leak. + + flmAssert( m_pszFileName == NULL); + + if( RC_BAD( rc = f_alloc( F_PATH_MAX_SIZE, &m_pszFileName))) + { + goto Exit; + } + + f_strcpy( m_pszFileName, pszFileName); + } + + if( RC_BAD( rc = openOrCreate( pszFileName, uiIoFlags, TRUE))) + { + goto Exit; + } + + m_bFileOpened = TRUE; + m_ui64CurrentPos = 0; + m_bOpenedExclusive = (uiIoFlags & FLM_IO_SH_DENYRW) ? TRUE : FALSE; + +Exit: + + if( RC_BAD( rc) && m_bDeleteOnRelease && m_pszFileName) + { + f_free( &m_pszFileName); + } + + return rc; +} + +/**************************************************************************** +Desc: Create a unique file name in the specified directory +****************************************************************************/ +RCODE FLMAPI F_FileHdl::createUnique( + const char * pszDirName, + const char * pszFileExtension, + FLMUINT uiIoFlags) +{ + RCODE rc = NE_FLM_OK; + char * pszTmp; + FLMBOOL bModext = TRUE; + FLMUINT uiBaseTime = 0; + FLMBYTE ucHighByte = 0; + char szFileName[ F_FILENAME_SIZE]; + char szDirPath[ F_PATH_MAX_SIZE]; + char szTmpPath[ F_PATH_MAX_SIZE]; + FLMUINT uiCount; + + szFileName[0] = '\0'; + szTmpPath[0] = '\0'; + flmAssert( m_bFileOpened == FALSE); + + if( m_bDeleteOnRelease) + { + + // This file handle had better not been used for another file + // before. Otherwise, we will get a memory leak. + + flmAssert( m_pszFileName == NULL); + + } + f_strcpy( szDirPath, pszDirName); + + /* + Search backwards replacing trailing spaces with NULLs. + */ + + pszTmp = (char *) szDirPath; + pszTmp += (f_strlen( pszTmp) - 1); + while( pszTmp >= (char *) szDirPath && (*pszTmp == 0x20)) + { + *pszTmp = 0; + pszTmp--; + } + + /* Append a backslash if one isn't already there. */ + + if (pszTmp >= (char *) szDirPath && *pszTmp != '\\') + { + pszTmp++; + *pszTmp++ = '\\'; + } + else + { + pszTmp++; + } + *pszTmp = 0; + + if ((pszFileExtension) && (f_strlen( pszFileExtension) >= 3)) + { + bModext = FALSE; + } + + uiCount = 0; + do + { + gv_pFileSystem->pathCreateUniqueName( &uiBaseTime, szFileName, pszFileExtension, + &ucHighByte, bModext); + + //need to strcpy to the buffer b/c it is uninitialized + f_strcpy( szTmpPath, szDirPath); + gv_pFileSystem->pathAppend( szTmpPath, szFileName); + if( m_pszFileName) + { + f_free( &m_pszFileName); + } + + rc = create( szTmpPath, uiIoFlags | FLM_IO_EXCL); + if (rc == NE_FLM_IO_DISK_FULL) + { + (void)_DeleteFile( szTmpPath); + goto Exit; + } + if ((rc == NE_FLM_IO_PATH_NOT_FOUND) || (rc == NE_FLM_IO_INVALID_PASSWORD)) + { + goto Exit; + } + } while ((rc != NE_FLM_OK) && (uiCount++ < 10)); + + /* Check if the path was created. */ + + if ((uiCount >= 10) && (rc != NE_FLM_OK)) + { + rc = RC_SET( NE_FLM_IO_PATH_CREATE_FAILURE); + goto Exit; + } + m_bFileOpened = TRUE; + m_bOpenedExclusive = (uiIoFlags & FLM_IO_SH_DENYRW) ? TRUE : FALSE; + + // Created file name needs to be returned. + f_strcpy( (char *)pszDirName, szTmpPath); + +Exit: + + if( RC_BAD( rc) && m_pszFileName) + { + f_free( &m_pszFileName); + } + + return( rc); +} + +/**************************************************************************** +Desc: Open a file +****************************************************************************/ +RCODE FLMAPI F_FileHdl::open( + const char * pszFileName, + FLMUINT uiIoFlags) +{ + RCODE rc = NE_FLM_OK; + + flmAssert( m_bFileOpened == FALSE); + + if( m_bDeleteOnRelease) + { + + // This file handle had better not been used for another file + // before. Otherwise, we will get a memory leak. + + flmAssert( m_pszFileName == NULL); + + if( RC_BAD( rc = f_alloc( F_PATH_MAX_SIZE, &m_pszFileName))) + { + goto Exit; + } + + f_strcpy( m_pszFileName, pszFileName); + } + + // Loop on error open conditions. + + for(;;) + { + if( RC_OK( rc = openOrCreate( pszFileName, uiIoFlags, FALSE))) + { + break; + } + + if( rc != NE_FLM_IO_TOO_MANY_OPEN_FILES ) + { + goto Exit; + } + } + + m_bFileOpened = TRUE; + m_ui64CurrentPos = 0; + m_bOpenedReadOnly = (uiIoFlags & FLM_IO_RDONLY) ? TRUE : FALSE; + m_bOpenedExclusive = (uiIoFlags & FLM_IO_SH_DENYRW) ? TRUE : FALSE; + +Exit: + + if( RC_BAD( rc) && m_bDeleteOnRelease && m_pszFileName) + { + f_free( &m_pszFileName); + } + + return rc; +} + +/**************************************************************************** +Desc: Close a file +****************************************************************************/ +RCODE FLMAPI F_FileHdl::close( void) +{ + FLMBOOL bDeleteAllowed = TRUE; + RCODE rc = NE_FLM_OK; + + if( !m_bFileOpened) + { + goto Exit; + } + + if (!CloseHandle( m_FileHandle)) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_CLOSING_FILE); + goto Exit; + } + + m_FileHandle = INVALID_HANDLE_VALUE; + m_bFileOpened = m_bOpenedReadOnly = m_bOpenedExclusive = FALSE; + + if (m_bDeleteOnRelease ) + { + flmAssert( NULL != m_pszFileName ); + + if( bDeleteAllowed) + { + (void)_DeleteFile( m_pszFileName); + } + m_bDeleteOnRelease = FALSE; + f_free( &m_pszFileName); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Flush IO to disk +****************************************************************************/ +RCODE FLMAPI F_FileHdl::flush( void) +{ + RCODE rc = NE_FLM_OK; + + if( !m_bDoDirectIO) + { + if( !FlushFileBuffers( m_FileHandle)) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_FLUSHING_FILE); + } + } + return( rc); +} + +/**************************************************************************** +Desc: Allocate an aligned buffer. +****************************************************************************/ +RCODE F_FileHdl::allocAlignBuffer( void) +{ + RCODE rc = NE_FLM_OK; + + // Allocate at least 64K - this will handle most read and write + // operations and will also be a multiple of the sector size most of + // the time. The calculation below rounds it up to the next sector + // boundary if it is not already on one. + + m_uiAlignedBuffSize = roundToNextSector( 64 * 1024); + if ((m_pucAlignedBuff = (FLMBYTE *)VirtualAlloc( NULL, + (DWORD)m_uiAlignedBuffSize, + MEM_COMMIT, PAGE_READWRITE)) == NULL) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_MEM); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Position and do a single read operation. +****************************************************************************/ +RCODE F_FileHdl::doOneRead( + FLMUINT64 ui64ReadOffset, + FLMUINT uiBytesToRead, + void * pvReadBuffer, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_FLM_OK; + OVERLAPPED * pOverlapped; + LARGE_INTEGER liTmp; + + // Position the file to the specified offset. + + if (!m_bCanDoAsync) + { + liTmp.QuadPart = ui64ReadOffset; + if( !SetFilePointerEx( m_FileHandle, liTmp, NULL, FILE_BEGIN)) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_POSITIONING_IN_FILE); + goto Exit; + } + + pOverlapped = NULL; + } + else + { + if (!m_Overlapped.hEvent) + { + if ((m_Overlapped.hEvent = CreateEvent( NULL, TRUE, + FALSE, NULL)) == NULL) + { + rc = MapWinErrorToFlaim( GetLastError(), + NE_FLM_SETTING_UP_FOR_READ); + goto Exit; + } + } + + pOverlapped = &m_Overlapped; + pOverlapped->Offset = (DWORD)(ui64ReadOffset & 0xFFFFFFFF); + pOverlapped->OffsetHigh = (DWORD)(ui64ReadOffset >> 32); + + if( !ResetEvent( pOverlapped->hEvent)) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_SETTING_UP_FOR_READ); + goto Exit; + } + } + + // Do the read + + if( !ReadFile( m_FileHandle, pvReadBuffer, uiBytesToRead, + puiBytesRead, pOverlapped)) + { + DWORD udErr = GetLastError(); + + if( udErr == ERROR_IO_PENDING && m_bCanDoAsync) + { + if( !GetOverlappedResult( m_FileHandle, + pOverlapped, puiBytesRead, TRUE)) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_READING_FILE); + goto Exit; + } + } + else + { + rc = MapWinErrorToFlaim( udErr, NE_FLM_READING_FILE); + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Read from a file - reads using aligned buffers and offsets - only + sector boundaries +****************************************************************************/ +RCODE F_FileHdl::directRead( + FLMUINT64 ui64ReadOffset, + FLMUINT uiBytesToRead, + void * pvBuffer, + FLMBOOL bBuffHasFullSectors, + FLMUINT * puiBytesReadRV) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiBytesRead; + FLMBYTE * pucReadBuffer; + FLMBYTE * pucDestBuffer; + FLMUINT uiMaxBytesToRead; + FLMBOOL bHitEOF; + + flmAssert( m_bFileOpened); + + if( puiBytesReadRV) + { + *puiBytesReadRV = 0; + } + + if( ui64ReadOffset == FLM_IO_CURRENT_POS) + { + ui64ReadOffset = m_ui64CurrentPos; + } + + // This loop does multiple reads (if necessary) to get all of the + // data. It uses aligned buffers and reads at sector offsets. + + pucDestBuffer = (FLMBYTE *)pvBuffer; + for (;;) + { + + // See if we are using an aligned buffer. If not, allocate + // one (if not already allocated), and use it. + + if ((ui64ReadOffset & m_ui64NotOnSectorBoundMask) || + (((FLMUINT64)pucDestBuffer) & m_ui64NotOnSectorBoundMask) || + (((FLMUINT64)uiBytesToRead & m_ui64NotOnSectorBoundMask) && + (!bBuffHasFullSectors))) + { + if (!m_pucAlignedBuff) + { + if (RC_BAD( rc = allocAlignBuffer())) + { + goto Exit; + } + } + pucReadBuffer = m_pucAlignedBuff; + + // Must read enough bytes to cover all of the sectors that + // contain the data we are trying to read. The value of + // (ui64ReadOffset & m_ui64NotOnSectorBoundMask) will give us the + // number of additional bytes that are in the sector prior to + // the read offset. We then round that up to the next sector + // to get the total number of bytes we are going to read. + + uiMaxBytesToRead = roundToNextSector( uiBytesToRead + + (ui64ReadOffset & m_ui64NotOnSectorBoundMask)); + + // Can't read more than the aligned buffer will hold. + + if (uiMaxBytesToRead > m_uiAlignedBuffSize) + { + uiMaxBytesToRead = m_uiAlignedBuffSize; + } + } + else + { + uiMaxBytesToRead = roundToNextSector( uiBytesToRead); + flmAssert( uiMaxBytesToRead >= uiBytesToRead); + pucReadBuffer = pucDestBuffer; + } + + bHitEOF = FALSE; + if (RC_BAD( rc = doOneRead( truncateToPrevSector( ui64ReadOffset), + uiMaxBytesToRead, pucReadBuffer, &uiBytesRead))) + { + goto Exit; + } + + if( uiBytesRead < uiMaxBytesToRead) + { + bHitEOF = TRUE; + } + + // If the offset we want to read from is not on a sector + // boundary, increment the read buffer pointer to the + // offset where the data we need starts and decrement the + // bytes read by the difference between the start of the + // sector and the actual read offset. + + if (ui64ReadOffset & m_ui64NotOnSectorBoundMask) + { + pucReadBuffer += (ui64ReadOffset & m_ui64NotOnSectorBoundMask); + flmAssert( uiBytesRead >= m_uiBytesPerSector); + uiBytesRead -= (ui64ReadOffset & m_ui64NotOnSectorBoundMask); + } + + // If bytes read is more than we actually need, truncate it back + // so that we only copy what we actually need. + + if( uiBytesRead > uiBytesToRead) + { + uiBytesRead = uiBytesToRead; + } + + uiBytesToRead -= uiBytesRead; + + if( puiBytesReadRV) + { + (*puiBytesReadRV) += uiBytesRead; + } + + m_ui64CurrentPos = ui64ReadOffset + uiBytesRead; + + // If using a different buffer for reading, copy the + // data read into the destination buffer. + + if (pucDestBuffer != pucReadBuffer) + { + f_memcpy( pucDestBuffer, pucReadBuffer, uiBytesRead); + } + + if (!uiBytesToRead) + { + break; + } + + // Still more to read - did we hit EOF above? + + if (bHitEOF) + { + rc = RC_SET( NE_FLM_IO_END_OF_FILE); + break; + } + + pucDestBuffer += uiBytesRead; + ui64ReadOffset += uiBytesRead; + } + +Exit: + return( rc ); +} + +/**************************************************************************** +Desc: Read from a file +****************************************************************************/ +RCODE FLMAPI F_FileHdl::read( + FLMUINT64 ui64ReadOffset, + FLMUINT uiBytesToRead, + void * pvBuffer, + FLMUINT * puiBytesReadRV) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiBytesRead; + + // Do the direct IO call if enabled. + + if (m_bDoDirectIO) + { + rc = directRead( ui64ReadOffset, uiBytesToRead, + pvBuffer, FALSE, puiBytesReadRV); + goto Exit; + } + + // If not doing direct IO, a single read call will do. + + flmAssert( m_bFileOpened); + if( puiBytesReadRV) + { + *puiBytesReadRV = 0; + } + + if( ui64ReadOffset == FLM_IO_CURRENT_POS) + { + ui64ReadOffset = m_ui64CurrentPos; + } + + if( RC_BAD( rc = doOneRead( ui64ReadOffset, uiBytesToRead, + pvBuffer, &uiBytesRead))) + { + goto Exit; + } + + if( puiBytesReadRV) + { + *puiBytesReadRV = uiBytesRead; + } + + m_ui64CurrentPos = ui64ReadOffset + uiBytesRead; + + if (uiBytesRead < uiBytesToRead) + { + rc = RC_SET( NE_FLM_IO_END_OF_FILE); + goto Exit; + } + +Exit: + return( rc ); +} + +/**************************************************************************** +Desc: Sets current position of file. +****************************************************************************/ +RCODE FLMAPI F_FileHdl::seek( + FLMUINT64 ui64Offset, + FLMINT iWhence, + FLMUINT64 * pui64NewOffset) +{ + RCODE rc = NE_FLM_OK; + + switch (iWhence) + { + case FLM_IO_SEEK_CUR: + { + m_ui64CurrentPos += ui64Offset; + break; + } + + case FLM_IO_SEEK_SET: + { + m_ui64CurrentPos = ui64Offset; + break; + } + + case FLM_IO_SEEK_END: + { + if( RC_BAD( rc = size( &m_ui64CurrentPos ))) + { + goto Exit; + } + break; + } + + default: + { + rc = RC_SET_AND_ASSERT( NE_FLM_NOT_IMPLEMENTED); + goto Exit; + } + } + + if( pui64NewOffset) + { + *pui64NewOffset = m_ui64CurrentPos; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Return the size of the file +****************************************************************************/ +RCODE FLMAPI F_FileHdl::size( + FLMUINT64 * pui64Size) +{ + RCODE rc = NE_FLM_OK; + LARGE_INTEGER liTmp; + + if( !GetFileSizeEx( m_FileHandle, &liTmp)) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_GETTING_FILE_SIZE); + goto Exit; + } + + *pui64Size = liTmp.QuadPart; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE FLMAPI F_FileHdl::tell( + FLMUINT64 * pui64Offset) +{ + *pui64Offset = m_ui64CurrentPos; + return( NE_FLM_OK); +} + +/**************************************************************************** +Desc: Truncate the file to the indicated size +WARNING: Direct IO methods are calling this method. Make sure that all changes + to this method work in direct IO mode. +****************************************************************************/ +RCODE FLMAPI F_FileHdl::truncate( + FLMUINT64 ui64Size) +{ + RCODE rc = NE_FLM_OK; + LARGE_INTEGER liTmp; + + flmAssert( m_bFileOpened); + + // Position the file to the nearest sector below the read offset. + + liTmp.QuadPart = ui64Size; + if( !SetFilePointerEx( m_FileHandle, liTmp, NULL, FILE_BEGIN)) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_POSITIONING_IN_FILE); + goto Exit; + } + + // Set the new file size. + + if( !SetEndOfFile( m_FileHandle)) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_TRUNCATING_FILE); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Handles when a file is extended in direct IO mode. May extend the + file some more. Will always call FlushFileBuffers to ensure that + the new file size gets written out. +****************************************************************************/ +RCODE F_FileHdl::extendFile( + FLMUINT64 ui64EndOfLastWrite, + FLMUINT uiMaxBytesToExtend, + FLMBOOL bFlush) +{ + RCODE rc = NE_FLM_OK; + OVERLAPPED * pOverlapped; + FLMUINT uiTotalBytesToExtend; + FLMUINT uiBytesToWrite; + FLMUINT uiBytesWritten; + LARGE_INTEGER liTmp; + + if ((uiTotalBytesToExtend = uiMaxBytesToExtend) != 0) + { + if (ui64EndOfLastWrite > m_uiMaxAutoExtendSize) + { + uiTotalBytesToExtend = 0; + } + else + { + // Don't extend beyond maximum file size. + + if (m_uiMaxAutoExtendSize - ui64EndOfLastWrite < uiTotalBytesToExtend) + { + uiTotalBytesToExtend = m_uiMaxAutoExtendSize - ui64EndOfLastWrite; + } + + // If the extend size is not on a sector boundary, round it down. + + uiTotalBytesToExtend = truncateToPrevSector( uiTotalBytesToExtend); + } + } + + if (uiTotalBytesToExtend) + { + // Allocate an aligned buffer if we haven't already. + + if (!m_pucAlignedBuff) + { + if (RC_BAD( rc = allocAlignBuffer())) + { + goto Exit; + } + } + + f_memset( m_pucAlignedBuff, 0, m_uiAlignedBuffSize); + } + + // Extend the file until we run out of bytes to write. + + while (uiTotalBytesToExtend) + { + if ((uiBytesToWrite = m_uiAlignedBuffSize) > uiTotalBytesToExtend) + { + uiBytesToWrite = uiTotalBytesToExtend; + } + + if (!m_bCanDoAsync) + { + liTmp.QuadPart = ui64EndOfLastWrite; + if( !SetFilePointerEx( m_FileHandle, liTmp, NULL, FILE_BEGIN)) + { + rc = MapWinErrorToFlaim( GetLastError(), + NE_FLM_POSITIONING_IN_FILE); + goto Exit; + } + + pOverlapped = NULL; + } + else + { + pOverlapped = &m_Overlapped; + if (!pOverlapped->hEvent) + { + if ((pOverlapped->hEvent = CreateEvent( NULL, TRUE, + FALSE, NULL)) == NULL) + { + rc = MapWinErrorToFlaim( GetLastError(), + NE_FLM_SETTING_UP_FOR_WRITE); + goto Exit; + } + } + + pOverlapped->Offset = (DWORD)(ui64EndOfLastWrite & 0xFFFFFFFF); + pOverlapped->OffsetHigh = (DWORD)(ui64EndOfLastWrite >> 32); + + if (!ResetEvent( pOverlapped->hEvent)) + { + rc = MapWinErrorToFlaim( GetLastError(), + NE_FLM_SETTING_UP_FOR_WRITE); + goto Exit; + } + } + + // Do the write + + if( !WriteFile( m_FileHandle, m_pucAlignedBuff, + uiBytesToWrite, &uiBytesWritten, pOverlapped)) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_WRITING_FILE); + + // Don't care if it is a disk full error, because + // extending the file is optional work. + + if( rc == NE_FLM_IO_DISK_FULL) + { + rc = NE_FLM_OK; + break; + } + + goto Exit; + } + + // NO more room on disk, but that's OK because we were only + // extending the file beyond where it needed to be. If that + // fails, we will just flush what we have done so far. + + if( uiBytesWritten < uiBytesToWrite) + { + break; + } + + uiTotalBytesToExtend -= uiBytesToWrite; + ui64EndOfLastWrite += uiBytesToWrite; + } + + // Flush the file buffers to ensure that the file size gets written + // out. + + if( bFlush) + { + if( !FlushFileBuffers( m_FileHandle)) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_FLUSHING_FILE); + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Write to a file using direct IO +****************************************************************************/ +RCODE F_FileHdl::directWrite( + FLMUINT64 ui64WriteOffset, + FLMUINT uiBytesToWrite, + const void * pvBuffer, + F_IOBuffer * pBufferObj, + FLMBOOL bBuffHasFullSectors, + FLMBOOL bZeroFill, + FLMUINT * puiBytesWrittenRV) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiBytesRead; + FLMUINT uiBytesWritten; + FLMBYTE * pucWriteBuffer; + FLMBYTE * pucSrcBuffer; + FLMUINT uiMaxBytesToWrite; + FLMUINT64 ui64CurrFileSize; + FLMUINT uiBytesBeingOutput; + OVERLAPPED * pOverlapped; + DWORD udErr; + FLMBOOL bExtendFile = FALSE; + FLMBOOL bDoAsync = (pBufferObj != NULL) + ? TRUE + : FALSE; + FLMBOOL bDidAsync = FALSE; + FLMUINT64 ui64LastWriteOffset; + FLMUINT uiLastWriteSize; + LARGE_INTEGER liTmp; + + flmAssert( m_bFileOpened); + +#ifdef FLM_DEBUG + if (bDoAsync) + { + flmAssert( m_bCanDoAsync); + } +#endif + + if( puiBytesWrittenRV) + { + *puiBytesWrittenRV = 0; + } + + if( ui64WriteOffset == FLM_IO_CURRENT_POS) + { + ui64WriteOffset = m_ui64CurrentPos; + } + + // Determine if the write will extend the file beyond its + // current size. If so, we will need to call FlushFileBuffers + + if( !GetFileSizeEx( m_FileHandle, &liTmp)) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_GETTING_FILE_SIZE); + goto Exit; + } + + ui64CurrFileSize = liTmp.QuadPart; + + if( ui64WriteOffset + uiBytesToWrite > ui64CurrFileSize && + m_uiExtendSize != (FLMUINT)(~0)) + { + bExtendFile = TRUE; + + if( ui64WriteOffset > ui64CurrFileSize) + { + + // Fill in the empty space. + + if (RC_BAD( rc = extendFile( ui64CurrFileSize, + roundToNextSector( ui64WriteOffset - ui64CurrFileSize), FALSE))) + { + goto Exit; + } + } + + // Can't do asynchronous if we are going to extend the file. + + bDoAsync = FALSE; + } + + // This loop is for direct IO - must make sure we use + // aligned buffers. + + pucSrcBuffer = (FLMBYTE *)pvBuffer; + for (;;) + { + + // See if we are using an aligned buffer. If not, allocate + // one (if not already allocated), and use it. + + if ((ui64WriteOffset & m_ui64NotOnSectorBoundMask) || + (((FLMUINT)pucSrcBuffer) & m_ui64NotOnSectorBoundMask) || + ((uiBytesToWrite & m_ui64NotOnSectorBoundMask) && + (!bBuffHasFullSectors))) + { + + // Cannot be using a temporary write buffer if we are doing + // asynchronous writes! + + flmAssert( !bDoAsync || !m_bCanDoAsync); + if (!m_pucAlignedBuff) + { + if (RC_BAD( rc = allocAlignBuffer())) + { + goto Exit; + } + } + pucWriteBuffer = m_pucAlignedBuff; + + // Must write enough bytes to cover all of the sectors that + // contain the data we are trying to write out. The value of + // (ui64WriteOffset & m_ui64NotOnSectorBoundMask) will give us the + // number of additional bytes that are in the sector prior to + // the read offset. We then round to the next sector to get the + // total number of bytes we are going to write. + + uiMaxBytesToWrite = roundToNextSector( uiBytesToWrite + + (ui64WriteOffset & m_ui64NotOnSectorBoundMask)); + + // Can't write more than the aligned buffer will hold. + + if (uiMaxBytesToWrite > m_uiAlignedBuffSize) + { + uiMaxBytesToWrite = m_uiAlignedBuffSize; + uiBytesBeingOutput = uiMaxBytesToWrite - + (ui64WriteOffset & m_ui64NotOnSectorBoundMask); + } + else + { + uiBytesBeingOutput = uiBytesToWrite; + } + + // If the write offset is not on a sector boundary, we must + // read at least the first sector into the buffer. + + if (ui64WriteOffset & m_ui64NotOnSectorBoundMask) + { + + // Read the first sector that is to be written out. + // Read one sector's worth of data - so that we will + // preserve what is already in the sector before + // writing it back out again. + + if (RC_BAD( rc = doOneRead( truncateToPrevSector( ui64WriteOffset), + m_uiBytesPerSector, pucWriteBuffer, &uiBytesRead))) + { + goto Exit; + } + } + + // If we are writing more than one sector, and the last sector's + // worth of data we are writing out is only a partial sector, + // we must read in this sector as well. + + if ((uiMaxBytesToWrite > m_uiBytesPerSector) && + (uiMaxBytesToWrite > uiBytesToWrite) && + (!bBuffHasFullSectors)) + { + + // Read the last sector that is to be written out. + // Read one sector's worth of data - so that we will + // preserve what is already in the sector before + // writing it back out again. + + if (RC_BAD( rc = doOneRead( + (truncateToPrevSector( ui64WriteOffset)) + + (uiMaxBytesToWrite - m_uiBytesPerSector), + m_uiBytesPerSector, + (&pucWriteBuffer [uiMaxBytesToWrite - m_uiBytesPerSector]), + &uiBytesRead))) + { + if (rc == NE_FLM_IO_END_OF_FILE) + { + rc = NE_FLM_OK; + f_memset( &pucWriteBuffer [uiMaxBytesToWrite - m_uiBytesPerSector], + 0, m_uiBytesPerSector); + } + else + { + goto Exit; + } + } + } + + // Finally, copy the data from the source buffer into the + // write buffer. + + f_memcpy( &pucWriteBuffer[ ui64WriteOffset & m_ui64NotOnSectorBoundMask], + pucSrcBuffer, uiBytesBeingOutput); + } + else + { + uiMaxBytesToWrite = roundToNextSector( uiBytesToWrite); + uiBytesBeingOutput = uiBytesToWrite; + pucWriteBuffer = pucSrcBuffer; + if( bZeroFill && uiMaxBytesToWrite > uiBytesToWrite) + { + f_memset( &pucWriteBuffer [uiBytesToWrite], 0, + uiMaxBytesToWrite - uiBytesToWrite); + } + } + + // Position the file to the nearest sector below the write offset. + + ui64LastWriteOffset = truncateToPrevSector( ui64WriteOffset); + if (!m_bCanDoAsync) + { + liTmp.QuadPart = ui64LastWriteOffset; + if( !SetFilePointerEx( m_FileHandle, liTmp, NULL, FILE_BEGIN)) + { + rc = MapWinErrorToFlaim( GetLastError(), + NE_FLM_POSITIONING_IN_FILE); + goto Exit; + } + + pOverlapped = NULL; + } + else + { + if (!pBufferObj) + { + pOverlapped = &m_Overlapped; + } + else + { + pOverlapped = pBufferObj->getOverlapped(); + pBufferObj->setFileHandle( m_FileHandle); + } + + if (!pOverlapped->hEvent) + { + if ((pOverlapped->hEvent = CreateEvent( NULL, TRUE, + FALSE, NULL)) == NULL) + { + rc = MapWinErrorToFlaim( GetLastError(), + NE_FLM_SETTING_UP_FOR_WRITE); + goto Exit; + } + } + + pOverlapped->Offset = (DWORD)(ui64LastWriteOffset & 0xFFFFFFFF); + pOverlapped->OffsetHigh = (DWORD)(ui64LastWriteOffset >> 32); + + if (!ResetEvent( pOverlapped->hEvent)) + { + rc = MapWinErrorToFlaim( GetLastError(), + NE_FLM_SETTING_UP_FOR_WRITE); + goto Exit; + } + } + + // Do the write + + uiLastWriteSize = uiMaxBytesToWrite; + if( !WriteFile( m_FileHandle, (LPVOID)pucWriteBuffer, + (DWORD)uiMaxBytesToWrite, &uiBytesWritten, + pOverlapped)) + { + udErr = GetLastError(); + if (udErr == ERROR_IO_PENDING && m_bCanDoAsync) + { + + // If an async structure was passed in, we better have + // been able to finish in a single write operation. + // Otherwise, we are in deep trouble because we are not + // set up to do multiple async write operations within + // a single call. + + if( bDoAsync) + { + pBufferObj->makePending(); + bDidAsync = TRUE; + break; + } + + if (!GetOverlappedResult( m_FileHandle, pOverlapped, + &uiBytesWritten, TRUE)) + { + rc = MapWinErrorToFlaim( GetLastError(), + NE_FLM_WRITING_FILE); + goto Exit; + } + } + else + { + rc = MapWinErrorToFlaim( udErr, NE_FLM_WRITING_FILE); + goto Exit; + } + } + + if (uiBytesWritten < uiMaxBytesToWrite) + { + rc = RC_SET( NE_FLM_IO_DISK_FULL); + goto Exit; + } + + uiBytesToWrite -= uiBytesBeingOutput; + + if( puiBytesWrittenRV) + { + (*puiBytesWrittenRV) += uiBytesBeingOutput; + } + + m_ui64CurrentPos = ui64WriteOffset + uiBytesBeingOutput; + + if (!uiBytesToWrite) + { + break; + } + + pucSrcBuffer += uiBytesBeingOutput; + ui64WriteOffset += uiBytesBeingOutput; + } + + // See if we extended the file. If so, we may want to extend it some + // more and then also do a flush. + + if (bExtendFile) + { + // NOTE: ui64LastWriteOffset + uiLastWrite is guaranteed to be + // on a sector boundary. + + if (RC_BAD( rc = extendFile( + ui64LastWriteOffset + uiLastWriteSize, + m_uiExtendSize, TRUE))) + { + goto Exit; + } + } + +Exit: + + if( !bDidAsync && pBufferObj) + { + pBufferObj->notifyComplete( rc); + } + + return( rc ); +} + +/**************************************************************************** +Desc: Write to a file +****************************************************************************/ +RCODE FLMAPI F_FileHdl::write( + FLMUINT64 ui64WriteOffset, + FLMUINT uiBytesToWrite, + const void * pvBuffer, + FLMUINT * puiBytesWrittenRV) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiBytesWritten; + OVERLAPPED * pOverlapped; + DWORD udErr; + LARGE_INTEGER liTmp; + + if (m_bDoDirectIO) + { + rc = directWrite( ui64WriteOffset, uiBytesToWrite, pvBuffer, + NULL, FALSE, FALSE, puiBytesWrittenRV); + goto Exit; + } + + // If not doing direct IO, a single write call will do. + + flmAssert( m_bFileOpened); + + if( puiBytesWrittenRV) + { + *puiBytesWrittenRV = 0; + } + + if( ui64WriteOffset == FLM_IO_CURRENT_POS) + { + ui64WriteOffset = m_ui64CurrentPos; + } + + // Position the file. + + if (!m_bCanDoAsync) + { + liTmp.QuadPart = ui64WriteOffset; + if( !SetFilePointerEx( m_FileHandle, liTmp, NULL, FILE_BEGIN)) + { + rc = MapWinErrorToFlaim( GetLastError(), + NE_FLM_POSITIONING_IN_FILE); + goto Exit; + } + + pOverlapped = NULL; + } + else + { + if (!m_Overlapped.hEvent) + { + if ((m_Overlapped.hEvent = CreateEvent( NULL, TRUE, + FALSE, NULL)) == NULL) + { + rc = MapWinErrorToFlaim( GetLastError(), + NE_FLM_SETTING_UP_FOR_WRITE); + goto Exit; + } + } + + pOverlapped = &m_Overlapped; + pOverlapped->Offset = (DWORD)(ui64WriteOffset & 0xFFFFFFFF); + pOverlapped->OffsetHigh = (DWORD)(ui64WriteOffset >> 32); + + if( !ResetEvent( pOverlapped->hEvent)) + { + rc = MapWinErrorToFlaim( GetLastError(), + NE_FLM_SETTING_UP_FOR_WRITE); + goto Exit; + } + } + + if (!WriteFile( m_FileHandle, (LPVOID)pvBuffer, + (DWORD)uiBytesToWrite, &uiBytesWritten, + pOverlapped)) + { + udErr = GetLastError(); + if (udErr == ERROR_IO_PENDING && m_bCanDoAsync) + { + if (!GetOverlappedResult( m_FileHandle, pOverlapped, + &uiBytesWritten, TRUE)) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_WRITING_FILE); + goto Exit; + } + } + else + { + rc = MapWinErrorToFlaim( udErr, NE_FLM_WRITING_FILE); + goto Exit; + } + } + + if( puiBytesWrittenRV) + { + *puiBytesWrittenRV = uiBytesWritten; + } + + m_ui64CurrentPos = ui64WriteOffset + uiBytesWritten; + + if (uiBytesWritten < uiBytesToWrite) + { + rc = RC_SET( NE_FLM_IO_DISK_FULL); + goto Exit; + } + +Exit: + return( rc ); +} + +/**************************************************************************** +Desc: Deletes a file +****************************************************************************/ +FSTATIC RCODE _DeleteFile( + char * pszPath) +{ + RCODE rc = NE_FLM_OK; + + if( DeleteFile( (LPTSTR)pszPath) == FALSE) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_IO_DELETING_FILE); + } + + return rc; +} + +#endif // FLM_WIN + +/**************************************************************************** +Desc: Deletes a file +****************************************************************************/ +#if defined( FLM_WATCOM_NLM) +int gv_ftkwinDummy(void) +{ + return( 0); +} +#endif diff --git a/ftk/src/ftkxml.cpp b/ftk/src/ftkxml.cpp new file mode 100644 index 0000000..41ce3ff --- /dev/null +++ b/ftk/src/ftkxml.cpp @@ -0,0 +1,7184 @@ +//------------------------------------------------------------------------------ +// Desc: XML parser +// +// Tabs: 3 +// +// Copyright (c) 2000-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: fxml.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "ftksys.h" + +// Constants + +#define FLM_XML_BASE_CHAR 0x01 +#define FLM_XML_IDEOGRAPHIC 0x02 +#define FLM_XML_COMBINING_CHAR 0x04 +#define FLM_XML_DIGIT 0x08 +#define FLM_XML_EXTENDER 0x10 +#define FLM_XML_WHITESPACE 0x20 + +// Local typedefs + +typedef struct +{ + char * pszEntity; + FLMUINT uiValue; +} CharEntity; + +// Global data + +static FLMUNICODE gv_puzNamespaceDeclPrefix[] = +{ + FLM_UNICODE_x, + FLM_UNICODE_m, + FLM_UNICODE_l, + FLM_UNICODE_n, + FLM_UNICODE_s, + 0 +}; + +static FLMUNICODE gv_puzXMLPrefix[] = +{ + FLM_UNICODE_x, + FLM_UNICODE_m, + FLM_UNICODE_l, + 0 +}; + +FLMUNICODE gv_puzXMLNSURI[] = +{ + FLM_UNICODE_h, + FLM_UNICODE_t, + FLM_UNICODE_t, + FLM_UNICODE_p, + FLM_UNICODE_COLON, + FLM_UNICODE_FSLASH, + FLM_UNICODE_FSLASH, + FLM_UNICODE_w, + FLM_UNICODE_w, + FLM_UNICODE_w, + FLM_UNICODE_PERIOD, + FLM_UNICODE_w, + FLM_UNICODE_3, + FLM_UNICODE_c, + FLM_UNICODE_PERIOD, + FLM_UNICODE_o, + FLM_UNICODE_r, + FLM_UNICODE_g, + FLM_UNICODE_FSLASH, + FLM_UNICODE_T, + FLM_UNICODE_R, + FLM_UNICODE_FSLASH, + FLM_UNICODE_1, + FLM_UNICODE_9, + FLM_UNICODE_9, + FLM_UNICODE_9, + FLM_UNICODE_FSLASH, + FLM_UNICODE_R, + FLM_UNICODE_E, + FLM_UNICODE_C, + FLM_UNICODE_HYPHEN, + FLM_UNICODE_x, + FLM_UNICODE_m, + FLM_UNICODE_l, + FLM_UNICODE_HYPHEN, + FLM_UNICODE_n, + FLM_UNICODE_a, + FLM_UNICODE_m, + FLM_UNICODE_e, + FLM_UNICODE_s, + FLM_UNICODE_HYPHEN, + FLM_UNICODE_1, + FLM_UNICODE_9, + FLM_UNICODE_9, + FLM_UNICODE_9, + FLM_UNICODE_0, + FLM_UNICODE_1, + FLM_UNICODE_1, + FLM_UNICODE_4, + 0 +}; + +typedef struct +{ + FLMUNICODE uLowChar; + FLMUNICODE uHighChar; + FLMUINT16 ui16Flag; +} CHAR_TBL; + +static CHAR_TBL charTbl[] = +{ + { 0x0041, 0x005A, FLM_XML_BASE_CHAR}, + { 0x0061, 0x007A, FLM_XML_BASE_CHAR}, + { 0x00C0, 0x00D6, FLM_XML_BASE_CHAR}, + { 0x00D8, 0x00F6, FLM_XML_BASE_CHAR}, + { 0x00F8, 0x00FF, FLM_XML_BASE_CHAR}, + { 0x0100, 0x0131, FLM_XML_BASE_CHAR}, + { 0x0134, 0x013E, FLM_XML_BASE_CHAR}, + { 0x0141, 0x0148, FLM_XML_BASE_CHAR}, + { 0x014A, 0x017E, FLM_XML_BASE_CHAR}, + { 0x0180, 0x01C3, FLM_XML_BASE_CHAR}, + { 0x01CD, 0x01F0, FLM_XML_BASE_CHAR}, + { 0x01F4, 0x01F5, FLM_XML_BASE_CHAR}, + { 0x01FA, 0x0217, FLM_XML_BASE_CHAR}, + { 0x0250, 0x02A8, FLM_XML_BASE_CHAR}, + { 0x02BB, 0x02C1, FLM_XML_BASE_CHAR}, + { 0x0386, 0x0386, FLM_XML_BASE_CHAR}, + { 0x0388, 0x038A, FLM_XML_BASE_CHAR}, + { 0x038C, 0x038C, FLM_XML_BASE_CHAR}, + { 0x038E, 0x03A1, FLM_XML_BASE_CHAR}, + { 0x03A3, 0x03CE, FLM_XML_BASE_CHAR}, + { 0x03D0, 0x03D6, FLM_XML_BASE_CHAR}, + { 0x03DA, 0x03DA, FLM_XML_BASE_CHAR}, + { 0x03DC, 0x03DC, FLM_XML_BASE_CHAR}, + { 0x03DE, 0x03DE, FLM_XML_BASE_CHAR}, + { 0x03E0, 0x03E0, FLM_XML_BASE_CHAR}, + { 0x03E2, 0x03F3, FLM_XML_BASE_CHAR}, + { 0x0401, 0x040C, FLM_XML_BASE_CHAR}, + { 0x040E, 0x044F, FLM_XML_BASE_CHAR}, + { 0x0451, 0x045C, FLM_XML_BASE_CHAR}, + { 0x045E, 0x0481, FLM_XML_BASE_CHAR}, + { 0x0490, 0x04C4, FLM_XML_BASE_CHAR}, + { 0x04C7, 0x04C8, FLM_XML_BASE_CHAR}, + { 0x04CB, 0x04CC, FLM_XML_BASE_CHAR}, + { 0x04D0, 0x04EB, FLM_XML_BASE_CHAR}, + { 0x04EE, 0x04F5, FLM_XML_BASE_CHAR}, + { 0x04F8, 0x04F9, FLM_XML_BASE_CHAR}, + { 0x0531, 0x0556, FLM_XML_BASE_CHAR}, + { 0x0559, 0x0559, FLM_XML_BASE_CHAR}, + { 0x0561, 0x0586, FLM_XML_BASE_CHAR}, + { 0x05D0, 0x05EA, FLM_XML_BASE_CHAR}, + { 0x05F0, 0x05F2, FLM_XML_BASE_CHAR}, + { 0x0621, 0x063A, FLM_XML_BASE_CHAR}, + { 0x0641, 0x06B7, FLM_XML_BASE_CHAR}, + { 0x06BA, 0x06BE, FLM_XML_BASE_CHAR}, + { 0x06C0, 0x06CE, FLM_XML_BASE_CHAR}, + { 0x06D0, 0x06D3, FLM_XML_BASE_CHAR}, + { 0x06D5, 0x06D5, FLM_XML_BASE_CHAR}, + { 0x06E5, 0x06E6, FLM_XML_BASE_CHAR}, + { 0x0905, 0x0939, FLM_XML_BASE_CHAR}, + { 0x093D, 0x093D, FLM_XML_BASE_CHAR}, + { 0x0958, 0x0961, FLM_XML_BASE_CHAR}, + { 0x0985, 0x098C, FLM_XML_BASE_CHAR}, + { 0x098F, 0x0990, FLM_XML_BASE_CHAR}, + { 0x0993, 0x09A8, FLM_XML_BASE_CHAR}, + { 0x09AA, 0x09B0, FLM_XML_BASE_CHAR}, + { 0x09B2, 0x09B2, FLM_XML_BASE_CHAR}, + { 0x09B6, 0x09B9, FLM_XML_BASE_CHAR}, + { 0x0061, 0x007A, FLM_XML_BASE_CHAR}, + { 0x09DC, 0x09DD, FLM_XML_BASE_CHAR}, + { 0x09DF, 0x09E1, FLM_XML_BASE_CHAR}, + { 0x09F0, 0x09F1, FLM_XML_BASE_CHAR}, + { 0x0A05, 0x0A0A, FLM_XML_BASE_CHAR}, + { 0x0A0F, 0x0A10, FLM_XML_BASE_CHAR}, + { 0x0A13, 0x0A28, FLM_XML_BASE_CHAR}, + { 0x0A2A, 0x0A30, FLM_XML_BASE_CHAR}, + { 0x0A32, 0x0A33, FLM_XML_BASE_CHAR}, + { 0x0A35, 0x0A36, FLM_XML_BASE_CHAR}, + { 0x0A38, 0x0A39, FLM_XML_BASE_CHAR}, + { 0x0A59, 0x0A5C, FLM_XML_BASE_CHAR}, + { 0x0A5E, 0x0A5E, FLM_XML_BASE_CHAR}, + { 0x0A72, 0x0A74, FLM_XML_BASE_CHAR}, + { 0x0A85, 0x0A8B, FLM_XML_BASE_CHAR}, + { 0x0A8D, 0x0A8D, FLM_XML_BASE_CHAR}, + { 0x0A8F, 0x0A91, FLM_XML_BASE_CHAR}, + { 0x0A93, 0x0AA8, FLM_XML_BASE_CHAR}, + { 0x0AAA, 0x0AB0, FLM_XML_BASE_CHAR}, + { 0x0AB2, 0x0AB3, FLM_XML_BASE_CHAR}, + { 0x0AB5, 0x0AB9, FLM_XML_BASE_CHAR}, + { 0x0ABD, 0x0ABD, FLM_XML_BASE_CHAR}, + { 0x0AE0, 0x0AE0, FLM_XML_BASE_CHAR}, + { 0x0B05, 0x0B0C, FLM_XML_BASE_CHAR}, + { 0x0B0F, 0x0B10, FLM_XML_BASE_CHAR}, + { 0x0B13, 0x0B28, FLM_XML_BASE_CHAR}, + { 0x0B2A, 0x0B30, FLM_XML_BASE_CHAR}, + { 0x0B32, 0x0B33, FLM_XML_BASE_CHAR}, + { 0x0B36, 0x0B39, FLM_XML_BASE_CHAR}, + { 0x0B3D, 0x0B3D, FLM_XML_BASE_CHAR}, + { 0x0B5C, 0x0B5D, FLM_XML_BASE_CHAR}, + { 0x0B5F, 0x0B61, FLM_XML_BASE_CHAR}, + { 0x0B85, 0x0B8A, FLM_XML_BASE_CHAR}, + { 0x0B8E, 0x0B90, FLM_XML_BASE_CHAR}, + { 0x0B92, 0x0B95, FLM_XML_BASE_CHAR}, + { 0x0B99, 0x0B9A, FLM_XML_BASE_CHAR}, + { 0x0B9C, 0x0B9C, FLM_XML_BASE_CHAR}, + { 0x0B9E, 0x0B9F, FLM_XML_BASE_CHAR}, + { 0x0BA3, 0x0BA4, FLM_XML_BASE_CHAR}, + { 0x0BA8, 0x0BAA, FLM_XML_BASE_CHAR}, + { 0x0BAE, 0x0BB5, FLM_XML_BASE_CHAR}, + { 0x0BB7, 0x0BB9, FLM_XML_BASE_CHAR}, + { 0x0C05, 0x0C0C, FLM_XML_BASE_CHAR}, + { 0x0C0E, 0x0C10, FLM_XML_BASE_CHAR}, + { 0x0C12, 0x0C28, FLM_XML_BASE_CHAR}, + { 0x0C2A, 0x0C33, FLM_XML_BASE_CHAR}, + { 0x0C35, 0x0C39, FLM_XML_BASE_CHAR}, + { 0x0C60, 0x0C61, FLM_XML_BASE_CHAR}, + { 0x0C85, 0x0C8C, FLM_XML_BASE_CHAR}, + { 0x0C8E, 0x0C90, FLM_XML_BASE_CHAR}, + { 0x0C92, 0x0CA8, FLM_XML_BASE_CHAR}, + { 0x0CAA, 0x0CB3, FLM_XML_BASE_CHAR}, + { 0x0CB5, 0x0CB9, FLM_XML_BASE_CHAR}, + { 0x0CDE, 0x0CDE, FLM_XML_BASE_CHAR}, + { 0x0CE0, 0x0CE1, FLM_XML_BASE_CHAR}, + { 0x0D05, 0x0D0C, FLM_XML_BASE_CHAR}, + { 0x0D0E, 0x0D10, FLM_XML_BASE_CHAR}, + { 0x0D12, 0x0D28, FLM_XML_BASE_CHAR}, + { 0x0D2A, 0x0D39, FLM_XML_BASE_CHAR}, + { 0x0D60, 0x0D61, FLM_XML_BASE_CHAR}, + { 0x0E01, 0x0E2E, FLM_XML_BASE_CHAR}, + { 0x0E30, 0x0E30, FLM_XML_BASE_CHAR}, + { 0x0E32, 0x0E33, FLM_XML_BASE_CHAR}, + { 0x0E40, 0x0E45, FLM_XML_BASE_CHAR}, + { 0x0E81, 0x0E82, FLM_XML_BASE_CHAR}, + { 0x0E84, 0x0E84, FLM_XML_BASE_CHAR}, + { 0x0E87, 0x0E88, FLM_XML_BASE_CHAR}, + { 0x0E8A, 0x0E8A, FLM_XML_BASE_CHAR}, + { 0x0E8D, 0x0E8D, FLM_XML_BASE_CHAR}, + { 0x0E94, 0x0E97, FLM_XML_BASE_CHAR}, + { 0x0E99, 0x0E9F, FLM_XML_BASE_CHAR}, + { 0x0EA1, 0x0EA3, FLM_XML_BASE_CHAR}, + { 0x0EA5, 0x0EA5, FLM_XML_BASE_CHAR}, + { 0x0EA7, 0x0EA7, FLM_XML_BASE_CHAR}, + { 0x0EAA, 0x0EAB, FLM_XML_BASE_CHAR}, + { 0x0EAD, 0x0EAE, FLM_XML_BASE_CHAR}, + { 0x0EB0, 0x0EB0, FLM_XML_BASE_CHAR}, + { 0x0EB2, 0x0EB3, FLM_XML_BASE_CHAR}, + { 0x0EBD, 0x0EBD, FLM_XML_BASE_CHAR}, + { 0x0EC0, 0x0EC4, FLM_XML_BASE_CHAR}, + { 0x0F40, 0x0F47, FLM_XML_BASE_CHAR}, + { 0x0F49, 0x0F69, FLM_XML_BASE_CHAR}, + { 0x10A0, 0x10C5, FLM_XML_BASE_CHAR}, + { 0x10D0, 0x10F6, FLM_XML_BASE_CHAR}, + { 0x1100, 0x1100, FLM_XML_BASE_CHAR}, + { 0x1102, 0x1103, FLM_XML_BASE_CHAR}, + { 0x1105, 0x1107, FLM_XML_BASE_CHAR}, + { 0x1109, 0x1109, FLM_XML_BASE_CHAR}, + { 0x110B, 0x110C, FLM_XML_BASE_CHAR}, + { 0x110E, 0x1112, FLM_XML_BASE_CHAR}, + { 0x113C, 0x113C, FLM_XML_BASE_CHAR}, + { 0x113E, 0x113E, FLM_XML_BASE_CHAR}, + { 0x1140, 0x1140, FLM_XML_BASE_CHAR}, + { 0x114C, 0x114C, FLM_XML_BASE_CHAR}, + { 0x114E, 0x114E, FLM_XML_BASE_CHAR}, + { 0x1150, 0x1150, FLM_XML_BASE_CHAR}, + { 0x1154, 0x1155, FLM_XML_BASE_CHAR}, + { 0x1159, 0x1159, FLM_XML_BASE_CHAR}, + { 0x115F, 0x1161, FLM_XML_BASE_CHAR}, + { 0x1163, 0x1163, FLM_XML_BASE_CHAR}, + { 0x1165, 0x1165, FLM_XML_BASE_CHAR}, + { 0x1167, 0x1167, FLM_XML_BASE_CHAR}, + { 0x1169, 0x1169, FLM_XML_BASE_CHAR}, + { 0x116D, 0x116E, FLM_XML_BASE_CHAR}, + { 0x1172, 0x1173, FLM_XML_BASE_CHAR}, + { 0x1175, 0x1175, FLM_XML_BASE_CHAR}, + { 0x119E, 0x119E, FLM_XML_BASE_CHAR}, + { 0x11A8, 0x11A8, FLM_XML_BASE_CHAR}, + { 0x11AB, 0x11AB, FLM_XML_BASE_CHAR}, + { 0x11AE, 0x11AF, FLM_XML_BASE_CHAR}, + { 0x11B7, 0x11B8, FLM_XML_BASE_CHAR}, + { 0x11BA, 0x11BA, FLM_XML_BASE_CHAR}, + { 0x11BC, 0x11C2, FLM_XML_BASE_CHAR}, + { 0x11EB, 0x11EB, FLM_XML_BASE_CHAR}, + { 0x11F0, 0x11F0, FLM_XML_BASE_CHAR}, + { 0x11F9, 0x11F9, FLM_XML_BASE_CHAR}, + { 0x1E00, 0x1E9B, FLM_XML_BASE_CHAR}, + { 0x1EA0, 0x1EF9, FLM_XML_BASE_CHAR}, + { 0x1F00, 0x1F15, FLM_XML_BASE_CHAR}, + { 0x1F18, 0x1F1D, FLM_XML_BASE_CHAR}, + { 0x1F20, 0x1F45, FLM_XML_BASE_CHAR}, + { 0x1F48, 0x1F4D, FLM_XML_BASE_CHAR}, + { 0x1F50, 0x1F57, FLM_XML_BASE_CHAR}, + { 0x1F59, 0x1F59, FLM_XML_BASE_CHAR}, + { 0x1F5B, 0x1F5B, FLM_XML_BASE_CHAR}, + { 0x1F5D, 0x1F5D, FLM_XML_BASE_CHAR}, + { 0x1F5F, 0x1F7D, FLM_XML_BASE_CHAR}, + { 0x1F80, 0x1FB4, FLM_XML_BASE_CHAR}, + { 0x1FB6, 0x1FBC, FLM_XML_BASE_CHAR}, + { 0x1FBE, 0x1FBE, FLM_XML_BASE_CHAR}, + { 0x1FC2, 0x1FC4, FLM_XML_BASE_CHAR}, + { 0x1FC6, 0x1FCC, FLM_XML_BASE_CHAR}, + { 0x1FD0, 0x1FD3, FLM_XML_BASE_CHAR}, + { 0x1FD6, 0x1FDB, FLM_XML_BASE_CHAR}, + { 0x1FE0, 0x1FEC, FLM_XML_BASE_CHAR}, + { 0x1FF2, 0x1FF4, FLM_XML_BASE_CHAR}, + { 0x1FF6, 0x1FFC, FLM_XML_BASE_CHAR}, + { 0x2126, 0x2126, FLM_XML_BASE_CHAR}, + { 0x212A, 0x212B, FLM_XML_BASE_CHAR}, + { 0x212E, 0x212E, FLM_XML_BASE_CHAR}, + { 0x2180, 0x2182, FLM_XML_BASE_CHAR}, + { 0x3041, 0x3094, FLM_XML_BASE_CHAR}, + { 0x30A1, 0x30FA, FLM_XML_BASE_CHAR}, + { 0x3105, 0x312C, FLM_XML_BASE_CHAR}, + { 0xAC00, 0xD7A3, FLM_XML_BASE_CHAR}, + + { 0x4E00, 0x9FA5, FLM_XML_IDEOGRAPHIC}, + { 0x3007, 0x3007, FLM_XML_IDEOGRAPHIC}, + { 0x3021, 0x3029, FLM_XML_IDEOGRAPHIC}, + + { 0x0300, 0x0345, FLM_XML_COMBINING_CHAR}, + { 0x0360, 0x0361, FLM_XML_COMBINING_CHAR}, + { 0x0483, 0x0486, FLM_XML_COMBINING_CHAR}, + { 0x0591, 0x05A1, FLM_XML_COMBINING_CHAR}, + { 0x05A3, 0x05B9, FLM_XML_COMBINING_CHAR}, + { 0x05BB, 0x05BD, FLM_XML_COMBINING_CHAR}, + { 0x05BF, 0x05BF, FLM_XML_COMBINING_CHAR}, + { 0x05C1, 0x05C2, FLM_XML_COMBINING_CHAR}, + { 0x05C4, 0x05C4, FLM_XML_COMBINING_CHAR}, + { 0x064B, 0x0652, FLM_XML_COMBINING_CHAR}, + { 0x0670, 0x0670, FLM_XML_COMBINING_CHAR}, + { 0x06D6, 0x06DC, FLM_XML_COMBINING_CHAR}, + { 0x06DD, 0x06DF, FLM_XML_COMBINING_CHAR}, + { 0x06E0, 0x06E4, FLM_XML_COMBINING_CHAR}, + { 0x06E7, 0x06E8, FLM_XML_COMBINING_CHAR}, + { 0x06EA, 0x06ED, FLM_XML_COMBINING_CHAR}, + { 0x0901, 0x0903, FLM_XML_COMBINING_CHAR}, + { 0x093C, 0x093C, FLM_XML_COMBINING_CHAR}, + { 0x093E, 0x094C, FLM_XML_COMBINING_CHAR}, + { 0x094D, 0x094D, FLM_XML_COMBINING_CHAR}, + { 0x0951, 0x0954, FLM_XML_COMBINING_CHAR}, + { 0x0962, 0x0963, FLM_XML_COMBINING_CHAR}, + { 0x0981, 0x0983, FLM_XML_COMBINING_CHAR}, + { 0x09BC, 0x09BC, FLM_XML_COMBINING_CHAR}, + { 0x09BE, 0x09BE, FLM_XML_COMBINING_CHAR}, + { 0x09BF, 0x09BF, FLM_XML_COMBINING_CHAR}, + { 0x09C0, 0x09C4, FLM_XML_COMBINING_CHAR}, + { 0x09C7, 0x09C8, FLM_XML_COMBINING_CHAR}, + { 0x09CB, 0x09CD, FLM_XML_COMBINING_CHAR}, + { 0x09D7, 0x09D7, FLM_XML_COMBINING_CHAR}, + { 0x09E2, 0x09E3, FLM_XML_COMBINING_CHAR}, + { 0x0A02, 0x0A02, FLM_XML_COMBINING_CHAR}, + { 0x0A3C, 0x0A3C, FLM_XML_COMBINING_CHAR}, + { 0x0A3E, 0x0A3E, FLM_XML_COMBINING_CHAR}, + { 0x0A3F, 0x0A3F, FLM_XML_COMBINING_CHAR}, + { 0x0A40, 0x0A42, FLM_XML_COMBINING_CHAR}, + { 0x0A47, 0x0A48, FLM_XML_COMBINING_CHAR}, + { 0x0A4B, 0x0A4D, FLM_XML_COMBINING_CHAR}, + { 0x0A70, 0x0A71, FLM_XML_COMBINING_CHAR}, + { 0x0A81, 0x0A83, FLM_XML_COMBINING_CHAR}, + { 0x0ABC, 0x0ABC, FLM_XML_COMBINING_CHAR}, + { 0x0ABE, 0x0AC5, FLM_XML_COMBINING_CHAR}, + { 0x0AC7, 0x0AC9, FLM_XML_COMBINING_CHAR}, + { 0x0ACB, 0x0ACD, FLM_XML_COMBINING_CHAR}, + { 0x0B01, 0x0B03, FLM_XML_COMBINING_CHAR}, + { 0x0B3C, 0x0B3C, FLM_XML_COMBINING_CHAR}, + { 0x0B3E, 0x0B43, FLM_XML_COMBINING_CHAR}, + { 0x0B47, 0x0B48, FLM_XML_COMBINING_CHAR}, + { 0x0B4B, 0x0B4D, FLM_XML_COMBINING_CHAR}, + { 0x0B56, 0x0B57, FLM_XML_COMBINING_CHAR}, + { 0x0B82, 0x0B83, FLM_XML_COMBINING_CHAR}, + { 0x0BBE, 0x0BC2, FLM_XML_COMBINING_CHAR}, + { 0x0BC6, 0x0BC8, FLM_XML_COMBINING_CHAR}, + { 0x0BCA, 0x0BCD, FLM_XML_COMBINING_CHAR}, + { 0x0BD7, 0x0BD7, FLM_XML_COMBINING_CHAR}, + { 0x0C01, 0x0C03, FLM_XML_COMBINING_CHAR}, + { 0x0C3E, 0x0C44, FLM_XML_COMBINING_CHAR}, + { 0x0C46, 0x0C48, FLM_XML_COMBINING_CHAR}, + { 0x0C4A, 0x0C4D, FLM_XML_COMBINING_CHAR}, + { 0x0C55, 0x0C56, FLM_XML_COMBINING_CHAR}, + { 0x0C82, 0x0C83, FLM_XML_COMBINING_CHAR}, + { 0x0CBE, 0x0CC4, FLM_XML_COMBINING_CHAR}, + { 0x0CC6, 0x0CC8, FLM_XML_COMBINING_CHAR}, + { 0x0CCA, 0x0CCD, FLM_XML_COMBINING_CHAR}, + { 0x0CD5, 0x0CD6, FLM_XML_COMBINING_CHAR}, + { 0x0D02, 0x0D03, FLM_XML_COMBINING_CHAR}, + { 0x0D3E, 0x0D43, FLM_XML_COMBINING_CHAR}, + { 0x0D46, 0x0D48, FLM_XML_COMBINING_CHAR}, + { 0x0D4A, 0x0D4D, FLM_XML_COMBINING_CHAR}, + { 0x0D57, 0x0D57, FLM_XML_COMBINING_CHAR}, + { 0x0E31, 0x0E31, FLM_XML_COMBINING_CHAR}, + { 0x0E34, 0x0E3A, FLM_XML_COMBINING_CHAR}, + { 0x0E47, 0x0E4E, FLM_XML_COMBINING_CHAR}, + { 0x0EB1, 0x0EB1, FLM_XML_COMBINING_CHAR}, + { 0x0EB4, 0x0EB9, FLM_XML_COMBINING_CHAR}, + { 0x0EBB, 0x0EBC, FLM_XML_COMBINING_CHAR}, + { 0x0EC8, 0x0ECD, FLM_XML_COMBINING_CHAR}, + { 0x0F18, 0x0F19, FLM_XML_COMBINING_CHAR}, + { 0x0F35, 0x0F35, FLM_XML_COMBINING_CHAR}, + { 0x0F37, 0x0F37, FLM_XML_COMBINING_CHAR}, + { 0x0F39, 0x0F39, FLM_XML_COMBINING_CHAR}, + { 0x0F3E, 0x0F3E, FLM_XML_COMBINING_CHAR}, + { 0x0F3F, 0x0F3F, FLM_XML_COMBINING_CHAR}, + { 0x0F71, 0x0F84, FLM_XML_COMBINING_CHAR}, + { 0x0F86, 0x0F8B, FLM_XML_COMBINING_CHAR}, + { 0x0F90, 0x0F95, FLM_XML_COMBINING_CHAR}, + { 0x0F97, 0x0F97, FLM_XML_COMBINING_CHAR}, + { 0x0F99, 0x0FAD, FLM_XML_COMBINING_CHAR}, + { 0x0FB1, 0x0FB7, FLM_XML_COMBINING_CHAR}, + { 0x0FB9, 0x0FB9, FLM_XML_COMBINING_CHAR}, + { 0x20D0, 0x20DC, FLM_XML_COMBINING_CHAR}, + { 0x20E1, 0x20E1, FLM_XML_COMBINING_CHAR}, + { 0x302A, 0x302F, FLM_XML_COMBINING_CHAR}, + { 0x3099, 0x3099, FLM_XML_COMBINING_CHAR}, + { 0x309A, 0x309A, FLM_XML_COMBINING_CHAR}, + + { 0x0030, 0x0039, FLM_XML_DIGIT}, + { 0x0660, 0x0669, FLM_XML_DIGIT}, + { 0x06F0, 0x06F9, FLM_XML_DIGIT}, + { 0x0966, 0x096F, FLM_XML_DIGIT}, + { 0x09E6, 0x09EF, FLM_XML_DIGIT}, + { 0x0A66, 0x0A6F, FLM_XML_DIGIT}, + { 0x0AE6, 0x0AEF, FLM_XML_DIGIT}, + { 0x0B66, 0x0B6F, FLM_XML_DIGIT}, + { 0x0BE7, 0x0BEF, FLM_XML_DIGIT}, + { 0x0C66, 0x0C6F, FLM_XML_DIGIT}, + { 0x0CE6, 0x0CEF, FLM_XML_DIGIT}, + { 0x0D66, 0x0D6F, FLM_XML_DIGIT}, + { 0x0E50, 0x0E59, FLM_XML_DIGIT}, + { 0x0ED0, 0x0ED9, FLM_XML_DIGIT}, + { 0x0F20, 0x0F29, FLM_XML_DIGIT}, + + { 0x00B7, 0x00B7, FLM_XML_EXTENDER}, + { 0x02D0, 0x02D0, FLM_XML_EXTENDER}, + { 0x02D1, 0x02D1, FLM_XML_EXTENDER}, + { 0x0387, 0x0387, FLM_XML_EXTENDER}, + { 0x0640, 0x0640, FLM_XML_EXTENDER}, + { 0x0E46, 0x0E46, FLM_XML_EXTENDER}, + { 0x0EC6, 0x0EC6, FLM_XML_EXTENDER}, + { 0x3005, 0x3005, FLM_XML_EXTENDER}, + { 0x3031, 0x3035, FLM_XML_EXTENDER}, + { 0x309D, 0x309E, FLM_XML_EXTENDER}, + { 0x30FC, 0x30FE, FLM_XML_EXTENDER}, + + { 0x0009, 0x0009, FLM_XML_WHITESPACE}, + { 0x000A, 0x000A, FLM_XML_WHITESPACE}, + { 0x000D, 0x000D, FLM_XML_WHITESPACE}, + { 0x0020, 0x0020, FLM_XML_WHITESPACE}, + { 0, 0, 0} +}; + +FSTATIC RCODE exportUniValue( + IF_OStream * pOStream, + FLMUNICODE * puzStr, + FLMUINT uiStrChars, + FLMBOOL bEncodeSpecialChars, + FLMUINT uiIndentCount); + +/**************************************************************************** +Desc: +****************************************************************************/ +F_XML::F_XML() +{ + m_pCharTable = NULL; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_XML::~F_XML() +{ + if( m_pCharTable) + { + f_free( &m_pCharTable); + } +} + +/**************************************************************************** +Desc: Sets a character's type flag in the character lookup table +****************************************************************************/ +void F_XML::setCharFlag( + FLMUNICODE uLowChar, + FLMUNICODE uHighChar, + FLMUINT16 ui16Flag) +{ + FLMUINT uiLoop; + + flmAssert( uLowChar <= uHighChar); + + for( uiLoop = (FLMUINT)uLowChar; uiLoop <= (FLMUINT)uHighChar; uiLoop++) + { + m_pCharTable[ uiLoop].ucFlags |= (FLMBYTE)ui16Flag; + } +} + +/**************************************************************************** +Desc: Builds a character lookup table +****************************************************************************/ +RCODE FLMAPI F_XML::setup( void) +{ + RCODE rc = NE_FLM_OK; + + if( m_pCharTable) + { + f_free( &m_pCharTable); + } + + if( RC_BAD( rc = f_calloc( sizeof( XMLCHAR) * 0xFFFF, &m_pCharTable))) + { + goto Exit; + } + + for (FLMUINT uiLoop = 0; charTbl[uiLoop].ui16Flag; uiLoop++) + { + setCharFlag( charTbl[uiLoop].uLowChar, + charTbl[uiLoop].uHighChar, + charTbl[uiLoop].ui16Flag); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Returns TRUE if the character is a valid XML PubID character +****************************************************************************/ +FLMBOOL FLMAPI F_XML::isPubidChar( + FLMUNICODE uChar) +{ + if( uChar == FLM_UNICODE_SPACE || + uChar == FLM_UNICODE_LINEFEED || + (uChar >= FLM_UNICODE_a && uChar <= FLM_UNICODE_z) || + (uChar >= FLM_UNICODE_A && uChar <= FLM_UNICODE_Z) || + (uChar >= FLM_UNICODE_0 && uChar <= FLM_UNICODE_9) || + uChar == FLM_UNICODE_HYPHEN || + uChar == FLM_UNICODE_APOS || + uChar == FLM_UNICODE_LPAREN || + uChar == FLM_UNICODE_RPAREN || + uChar == FLM_UNICODE_PLUS || + uChar == FLM_UNICODE_COMMA || + uChar == FLM_UNICODE_PERIOD || + uChar == FLM_UNICODE_FSLASH || + uChar == FLM_UNICODE_COLON || + uChar == FLM_UNICODE_EQ || + uChar == FLM_UNICODE_QUEST || + uChar == FLM_UNICODE_SEMI || + uChar == FLM_UNICODE_BANG || + uChar == FLM_UNICODE_ASTERISK || + uChar == FLM_UNICODE_POUND || + uChar == FLM_UNICODE_ATSIGN || + uChar == FLM_UNICODE_DOLLAR || + uChar == FLM_UNICODE_UNDERSCORE || + uChar == FLM_UNICODE_PERCENT) + { + return( TRUE); + } + + return( FALSE); +} + +/**************************************************************************** +Desc: Returns TRUE if the character is a single or double quote character +****************************************************************************/ +FLMBOOL FLMAPI F_XML::isQuoteChar( + FLMUNICODE uChar) +{ + if( uChar == FLM_UNICODE_QUOTE || uChar == FLM_UNICODE_APOS) + { + return( TRUE); + } + + return( FALSE); +} + +/**************************************************************************** +Desc: Returns TRUE if the character is a whitespace character +****************************************************************************/ +FLMBOOL FLMAPI F_XML::isWhitespace( + FLMUNICODE uChar) +{ + if( (m_pCharTable[ uChar].ucFlags & FLM_XML_WHITESPACE) != 0) + { + return( TRUE); + } + + return( FALSE); +} + +/**************************************************************************** +Desc: Returns TRUE if the character is an extender character +****************************************************************************/ +FLMBOOL FLMAPI F_XML::isExtender( + FLMUNICODE uChar) +{ + if( (m_pCharTable[ uChar].ucFlags & FLM_XML_EXTENDER) != 0) + { + return( TRUE); + } + + return( FALSE); +} + +/**************************************************************************** +Desc: Returns TRUE if the character is a combining character +****************************************************************************/ +FLMBOOL FLMAPI F_XML::isCombiningChar( + FLMUNICODE uChar) +{ + if( (m_pCharTable[ uChar].ucFlags & FLM_XML_COMBINING_CHAR) != 0) + { + return( TRUE); + } + + return( FALSE); +} + +/**************************************************************************** +Desc: Returns TRUE if the character is a valid XML naming character +****************************************************************************/ +FLMBOOL FLMAPI F_XML::isNCNameChar( + FLMUNICODE uChar) +{ + if( isLetter( uChar) || + isDigit( uChar) || + uChar == FLM_UNICODE_PERIOD || + uChar == FLM_UNICODE_HYPHEN || + uChar == FLM_UNICODE_UNDERSCORE || + isCombiningChar( uChar) || isExtender( uChar)) + { + return( TRUE); + } + + return( FALSE); +} + +/**************************************************************************** +Desc: Returns TRUE if the character is a valid XML naming character +****************************************************************************/ +FLMBOOL FLMAPI F_XML::isNameChar( + FLMUNICODE uChar) +{ + if( isNCNameChar( uChar) || + uChar == FLM_UNICODE_COLON) + { + return( TRUE); + } + + return( FALSE); +} + +/**************************************************************************** +Desc: Returns TRUE if the character is an ideographic character +****************************************************************************/ +FLMBOOL FLMAPI F_XML::isIdeographic( + FLMUNICODE uChar) +{ + if( (m_pCharTable[ uChar].ucFlags & FLM_XML_IDEOGRAPHIC) != 0) + { + return( TRUE); + } + + return( FALSE); +} + +/**************************************************************************** +Desc: Returns TRUE if the character is a base character +****************************************************************************/ +FLMBOOL FLMAPI F_XML::isBaseChar( + FLMUNICODE uChar) +{ + if( (m_pCharTable[ uChar].ucFlags & FLM_XML_BASE_CHAR) != 0) + { + return( TRUE); + } + + return( FALSE); +} + +/**************************************************************************** +Desc: Returns TRUE if the character is a digit +****************************************************************************/ +FLMBOOL FLMAPI F_XML::isDigit( + FLMUNICODE uChar) +{ + if( (m_pCharTable[ uChar].ucFlags & FLM_XML_DIGIT) != 0) + { + return( TRUE); + } + + return( FALSE); +} + +/**************************************************************************** +Desc: Returns TRUE if the character is a letter +****************************************************************************/ +FLMBOOL FLMAPI F_XML::isLetter( + FLMUNICODE uChar) +{ + if( isBaseChar( uChar) || isIdeographic( uChar)) + { + return( TRUE); + } + + return( FALSE); +} + +/**************************************************************************** +Desc: Returns TRUE if the name is a valid XML name +****************************************************************************/ +FLMBOOL FLMAPI F_XML::isNameValid( + FLMUNICODE * puzName, + FLMBYTE * pszName) +{ + FLMBOOL bValid = FALSE; + + if( puzName) + { + FLMUNICODE * puzTmp; + + if( !isLetter( *puzName) && *puzName != FLM_UNICODE_UNDERSCORE && + *puzName != FLM_UNICODE_COLON) + { + goto Exit; + } + + puzTmp = &puzName[ 1]; + while( *puzTmp) + { + if( !isNameChar( *puzTmp)) + { + goto Exit; + } + puzTmp++; + } + } + + if( pszName) + { + FLMBYTE * pszTmp; + + if( !isLetter( *pszName) && *pszName != FLM_UNICODE_UNDERSCORE && + *pszName != FLM_UNICODE_COLON) + { + goto Exit; + } + + pszTmp = &pszName[ 1]; + while( *pszTmp) + { + if( !isNameChar( *pszTmp)) + { + goto Exit; + } + pszTmp++; + } + } + + bValid = TRUE; + +Exit: + + return( bValid); +} + +/**************************************************************************** +Desc: Constructor +****************************************************************************/ +F_XMLParser::F_XMLParser() +{ + m_uiValBufSize = 0; + m_pucValBuf = NULL; + m_bSetup = FALSE; + m_fnStatus = NULL; + m_pvCallbackData = NULL; + m_tmpPool.poolInit( 4096); + m_attrPool.poolInit( 4096); + m_puzCurrLineBuf = NULL; + m_uiCurrLineBufMaxChars = 0; + reset(); +} + +/**************************************************************************** +Desc: Destructor +****************************************************************************/ +F_XMLParser::~F_XMLParser() +{ + reset(); + + if( m_pucValBuf) + { + f_free( &m_pucValBuf); + } + if (m_puzCurrLineBuf) + { + f_free( &m_puzCurrLineBuf); + } +} + +/**************************************************************************** +Desc: Resets member variables so the object can be reused +****************************************************************************/ +void FLMAPI F_XMLParser::reset( void) +{ + m_uiCurrLineNum = 0; + m_uiCurrLineNumChars = 0; + m_uiCurrLineOffset = 0; + m_ucUngetByte = 0; + m_uiCurrLineFilePos = 0; + m_uiCurrLineBytes = 0; + m_pStream = NULL; + m_uiFlags = 0; + m_eXMLEncoding = FLM_XML_USASCII_ENCODING; + f_memset( &m_importStats, 0, sizeof( FLM_IMPORT_STATS)); + + popNamespaces( getNamespaceCount()); + m_tmpPool.poolReset( NULL); + resetAttrList(); +} + +/**************************************************************************** +Desc: Initializes the object (allocates buffers, etc.) +****************************************************************************/ +RCODE FLMAPI F_XMLParser::setup( void) +{ + RCODE rc = NE_FLM_OK; + + flmAssert( !m_bSetup); + + if( RC_BAD( rc = resizeValBuffer( 2048))) + { + goto Exit; + } + + if( RC_BAD( rc = F_XML::setup())) + { + goto Exit; + } + + m_bSetup = TRUE; + +Exit: + + if( RC_BAD( rc)) + { + if( m_pucValBuf) + { + f_free( &m_pucValBuf); + m_pucValBuf = NULL; + } + } + + return( rc); +} + +/**************************************************************************** +Desc: Reads data from the input stream and builds a FLAIM record +****************************************************************************/ +RCODE FLMAPI F_XMLParser::import( + IF_IStream * pStream, + FLMUINT uiFlags, + IF_DOMNode * pNodeToLinkTo, + eNodeInsertLoc eInsertLoc, + IF_DOMNode ** ppNewNode, + FLM_IMPORT_STATS * pImportStats) +{ + RCODE rc = NE_FLM_OK; + + // Reset the state of the parser + + reset(); + + // Set up namespace support. Un-prefixed names (NULL prefix) are + // not bound to a namespace (NULL URI). The 'xml' namespace prefix + // is, by definition, bound to 'http://www.w3.org/XML/1998/namespace' + + if( RC_BAD( rc = pushNamespace( NULL, NULL))) + { + goto Exit; + } + + if( RC_BAD( rc = pushNamespace( + gv_puzXMLPrefix, gv_puzXMLNSURI))) + { + goto Exit; + } + + m_pStream = pStream; + m_uiFlags = uiFlags; + + if( RC_BAD( rc = processProlog())) + { + goto Exit; + } + + if( RC_BAD( rc = processElement( pNodeToLinkTo, eInsertLoc, ppNewNode))) + { + goto Exit; + } + + // Call the status hook one last time + + m_importStats.uiDocuments++; + if( m_fnStatus) + { + m_fnStatus( XML_STATS, + (void *)&m_importStats, NULL, NULL, m_pvCallbackData); + } + + // Tally and return the import stats + + if( pImportStats) + { + pImportStats->uiChars += m_importStats.uiChars; + pImportStats->uiAttributes += m_importStats.uiAttributes; + pImportStats->uiElements += m_importStats.uiElements; + pImportStats->uiText += m_importStats.uiText; + pImportStats->uiDocuments += m_importStats.uiDocuments; + } + +Exit: + + if( RC_BAD( rc) && pImportStats) + { + pImportStats->uiErrLineNum = m_importStats.uiErrLineNum + ? m_importStats.uiErrLineNum + : m_uiCurrLineNum; + + pImportStats->uiErrLineOffset = m_importStats.uiErrLineOffset + ? m_importStats.uiErrLineOffset + : m_uiCurrLineOffset; + + pImportStats->eErrorType = ( XMLParseError)( m_importStats.eErrorType); + + pImportStats->uiErrLineFilePos = m_importStats.uiErrLineFilePos; + pImportStats->uiErrLineBytes = m_importStats.uiErrLineBytes; + pImportStats->eXMLEncoding = m_importStats.eXMLEncoding; + } + + return( rc); +} + +/**************************************************************************** +Desc: Process an XML prolog +****************************************************************************/ +RCODE F_XMLParser::processProlog( void) +{ + RCODE rc = NE_FLM_OK; + + if( RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + + if (lineHasToken( "createNode( m_pDb, DATA_NODE, 0, +// FLM_LAST_CHILD, &pData))) +// { +// setErrInfo( m_uiCurrLineNum, +// m_uiCurrLineOffset, +// XML_ERR_CREATING_DATA_NODE, +// m_uiCurrLineFilePos, +// m_uiCurrLineBytes); +// goto Exit; +// } + + if( RC_BAD( rc = pParent->getDataType( &parentDataType))) + { + goto Exit; + } + + switch( parentDataType) + { + case FLM_TEXT_TYPE: + { +// if( RC_BAD( rc = pData->setUnicode( m_pDb, puzTextStart))) +// { +// goto Exit; +// } + + m_importStats.uiText++; + if( m_fnStatus && (m_importStats.uiText % 50) == 0) + { + m_fnStatus( XML_STATS, + (void *)&m_importStats, NULL, NULL, m_pvCallbackData); + } + + break; + } + + case FLM_NUMBER_TYPE: + { + FLMUINT64 ui64Val; + FLMBOOL bNeg; + + if( RC_BAD( rc = unicodeToNumber64( puzTextStart, &ui64Val, &bNeg))) + { + goto Exit; + } + + if( !bNeg) + { +// if( RC_BAD( rc = pData->setUINT64( m_pDb, ui64Val))) +// { +// goto Exit; +// } + } + else + { +// if( RC_BAD( rc = pData->setINT64( m_pDb, -((FLMINT64)ui64Val)))) +// { +// goto Exit; +// } + } + break; + } + + case FLM_BINARY_TYPE: + { +// if( RC_BAD( rc = pData->setBinary( m_pDb, pucValue, uiValueLen))) +// { +// goto Exit; +// } + break; + } + + default: + { + rc = RC_SET_AND_ASSERT( NE_FLM_NOT_IMPLEMENTED); + goto Exit; + } + } + +Exit: + + if( pData) + { + pData->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Processes an XML element +****************************************************************************/ +RCODE F_XMLParser::processElement( + IF_DOMNode * pNodeToLinkTo, + eNodeInsertLoc eInsertLoc, + IF_DOMNode ** ppNewNode) +{ + RCODE rc = NE_FLM_OK; + FLMBOOL bHasContent; + FLMBOOL bFlushedValue = FALSE; + FLMUINT uiChars; + FLMUINT uiOffset = 0; + FLMUNICODE uChar; + IF_DOMNode * pElement = NULL; + F_XMLNamespace * pNamespace = NULL; + FLMUNICODE * puzPrefix; + FLMUNICODE * puzLocal; + FLMUINT uiStartNSCount = getNamespaceCount(); + FLMUINT uiTmp; + FLMUINT uiWhitespaceStartOffset = 0; + FLMBOOL bNamespaceDecl; + FLMUINT uiSavedLineNum = 0; + FLMUINT uiSavedOffset = 0; + FLMUINT uiSavedFilePos = 0; + FLMUINT uiSavedLineBytes = 0; + eFlmDataType elmDataType; + FLMUINT uiElmNameId; + + if( RC_BAD( rc = processSTag( pNodeToLinkTo, eInsertLoc, + &bHasContent, &pElement))) + { + goto Exit; + } + + if (ppNewNode) + { + *ppNewNode = pElement; + (*ppNewNode)->AddRef(); + } + + if( !bHasContent) + { + goto Exit; + } + + if( RC_BAD( rc = pElement->getNameId( &uiElmNameId))) + { + goto Exit; + } + + if( RC_BAD( rc = pElement->getDataType( &elmDataType))) + { + goto Exit; + } + + for( ;;) + { + if ((uChar = getChar()) == 0) + { + uChar = ASCII_NEWLINE; + if (RC_BAD( rc = getLine())) + { + goto Exit; + } + } + + if( uChar == FLM_UNICODE_LT) + { + if( uiWhitespaceStartOffset) + { + // Set the offset to where the whitespace would + // have started. + + flmAssert( uiWhitespaceStartOffset <= uiOffset); + uiOffset = uiWhitespaceStartOffset; + uiWhitespaceStartOffset = 0; + } + + if( uiOffset) + { + // Flush the value + + if( pElement) + { + if( elmDataType == FLM_TEXT_TYPE || + elmDataType == FLM_NUMBER_TYPE) + { + if( uiOffset + 1 >= m_uiValBufSize) + { + if( RC_BAD( rc = resizeValBuffer( uiOffset + 2))) + { + goto Exit; + } + } + + m_pucValBuf[ uiOffset] = 0; + m_pucValBuf[ uiOffset + 1] = 0; + } + + if( RC_BAD( rc = flushElementValue( + pElement, m_pucValBuf, uiOffset))) + { + goto Exit; + } + } + + bFlushedValue = TRUE; + uiOffset = 0; + } + + // Preserve start location for error handling if necessary + + uiSavedLineNum = m_uiCurrLineNum; + uiSavedOffset = m_uiCurrLineOffset; + uiSavedFilePos = m_uiCurrLineFilePos; + uiSavedLineBytes = m_uiCurrLineBytes; + + if (lineHasToken( "?")) + { + if( RC_BAD( rc = processPI( pElement, + uiSavedLineNum, + uiSavedOffset, + uiSavedFilePos, + uiSavedLineBytes))) + { + goto Exit; + } + } + else if (lineHasToken( "!--")) + { + if( RC_BAD( rc = processComment( pElement, + uiSavedLineNum, + uiSavedOffset, + uiSavedFilePos, + uiSavedLineBytes))) + { + goto Exit; + } + } + else if (lineHasToken( "![CDATA[")) + { + if( RC_BAD( rc = processCDATA( pElement, + uiSavedLineNum, + uiSavedOffset, + uiSavedFilePos, + uiSavedLineBytes))) + { + goto Exit; + } + } + else if (lineHasToken( "/")) + { + break; + } + else if( isNameChar( peekChar())) + { + + // Unget the "<" - because processElement expect to see + // "= m_uiValBufSize) + { + if( RC_BAD( rc = resizeValBuffer( ~((FLMUINT)0)))) + { + goto Exit; + } + } + } + } + else + { + if( elmDataType != FLM_NODATA_TYPE) + { + if( m_uiFlags & FLM_XML_COMPRESS_WHITESPACE_FLAG) + { + if( isWhitespace( uChar)) + { + // If uiOffset is zero, this is still leading + // white space, and we should ignore it. + // Otherwise, we need to keep track of where + // whitespace began. + + if( !uiOffset) + { + uChar = 0; + } + else + { + uiWhitespaceStartOffset = uiOffset; + } + } + else + { + + // Last character is not whitespace. + + uiWhitespaceStartOffset = 0; + } + } + + if( uChar) + { + *((FLMUNICODE *)(&m_pucValBuf[ uiOffset])) = uChar; + uiOffset += sizeof( FLMUNICODE); + if( uiOffset >= m_uiValBufSize) + { + if( RC_BAD( rc = resizeValBuffer( ~((FLMUINT)0)))) + { + goto Exit; + } + } + } + } + } + } + + flmAssert( !uiOffset); + + + uiSavedOffset = m_uiCurrLineOffset; + if( RC_BAD( rc = getQualifiedName( &uiChars, + &puzPrefix, &puzLocal, &bNamespaceDecl, NULL))) + { + goto Exit; + } + + // Validate that the end tag matches the start tag + + if( pElement) + { + + // Element names cannot be "xmlns" or begin with "xmlns:" + + if (bNamespaceDecl) + { + setErrInfo( m_uiCurrLineNum, + uiSavedOffset, + XML_ERR_XMLNS_IN_ELEMENT_NAME, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + if( puzPrefix) + { + FLMUINT uiPrefixId1 = 0; + FLMUINT uiPrefixId2 = 0; + +// if( RC_BAD( rc = m_pDb->m_pDict->getPrefixId( +// m_pDb, puzPrefix, &uiPrefixId1))) +// { +// goto Exit; +// } + +// if( RC_BAD( rc = pElement->getPrefixId( m_pDb, &uiPrefixId2))) +// { +// goto Exit; +// } + + if( uiPrefixId1 != uiPrefixId2) + { + setErrInfo( m_uiCurrLineNum, + uiSavedOffset, + XML_ERR_ELEMENT_NAME_MISMATCH, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + } + else + { + if( RC_BAD( rc = pElement->getPrefixId( &uiTmp))) + { + goto Exit; + } + + if( uiTmp) + { + setErrInfo( m_uiCurrLineNum, + uiSavedOffset, + XML_ERR_ELEMENT_NAME_MISMATCH, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + } + + if( RC_BAD( rc = findNamespace( puzPrefix, &pNamespace))) + { + if( rc == NE_FLM_NOT_FOUND) + { + setErrInfo( m_uiCurrLineNum, + uiSavedOffset, + XML_ERR_PREFIX_NOT_DEFINED, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + } + goto Exit; + } + +// if( RC_BAD( rc = m_pDb->getElementNameId( +// pNamespace->getURIPtr(), puzLocal, &uiTmp))) +// { +// if( rc == NE_FLM_NOT_FOUND) +// { +// setErrInfo( m_uiCurrLineNum, +// uiSavedOffset, +// XML_ERR_ELEMENT_NAME_MISMATCH, +// m_uiCurrLineFilePos, +// m_uiCurrLineBytes); +// rc = RC_SET( NE_FLM_INVALID_XML); +// } +// goto Exit; +// } + + if( uiElmNameId != uiTmp) + { + setErrInfo( m_uiCurrLineNum, + uiSavedOffset, + XML_ERR_ELEMENT_NAME_MISMATCH, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + } + + // Skip any whitespace after the name + + if( RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + + // Get the ending ">" + + if( getChar() != FLM_UNICODE_GT) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + XML_ERR_EXPECTING_GT, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + +Exit: + + if( pNamespace) + { + pNamespace->Release(); + } + + popNamespaces( getNamespaceCount() - uiStartNSCount); + + if( pElement) + { + pElement->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Processes an XML STag +****************************************************************************/ +RCODE F_XMLParser::processSTag( + IF_DOMNode * pNodeToLinkTo, + eNodeInsertLoc eInsertLoc, + FLMBOOL * pbHasContent, + IF_DOMNode ** ppElement) +{ + RCODE rc = NE_FLM_OK; + FLMUNICODE uChar; + FLMUINT uiChars; + IF_DOMNode * pElement = NULL; + F_XMLNamespace * pNamespace = NULL; + FLMUNICODE * puzTmpPrefix; + FLMUNICODE * puzPrefix = NULL; + FLMUNICODE * puzTmpLocal; + FLMUNICODE * puzLocal = NULL; + FLMUINT uiNameId = 0; + FLMUINT uiAllocSize; + void * pvMark = m_tmpPool.poolMark(); + FLMBOOL bNamespaceDecl; + FLMUINT uiSavedLineNum; + FLMUINT uiSavedOffset; + FLMUINT uiSavedFilePos; + FLMUINT uiSavedLineBytes; + + *pbHasContent = FALSE; + + if( getChar() != FLM_UNICODE_LT) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + XML_ERR_EXPECTING_ELEMENT_LT, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + + uiSavedLineNum = m_uiCurrLineNum; + uiSavedOffset = m_uiCurrLineOffset; + uiSavedFilePos = m_uiCurrLineFilePos; + uiSavedLineBytes = m_uiCurrLineBytes; + if( RC_BAD( rc = getQualifiedName( &uiChars, &puzTmpPrefix, &puzTmpLocal, + &bNamespaceDecl, NULL))) + { + goto Exit; + } + + // Element names cannot be "xmlns" or begin with "xmlns:" + + if (bNamespaceDecl) + { + setErrInfo( uiSavedLineNum, + uiSavedOffset, + XML_ERR_XMLNS_IN_ELEMENT_NAME, + uiSavedFilePos, + uiSavedLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + + uiAllocSize = (f_unilen( puzTmpLocal) + 1) * sizeof( FLMUNICODE); + if( RC_BAD( rc = m_tmpPool.poolAlloc( uiAllocSize, (void **)&puzLocal))) + { + goto Exit; + } + f_unicpy( puzLocal, puzTmpLocal); + + if( puzTmpPrefix) + { + + // Need to save the prefix, because as parsing + // continues, the scratch buffer will be overwritten + + uiAllocSize = (f_unilen( puzTmpPrefix) + 1) * sizeof( FLMUNICODE); + if( RC_BAD( rc = m_tmpPool.poolAlloc( uiAllocSize, (void **)&puzPrefix))) + { + goto Exit; + } + f_unicpy( puzPrefix, puzTmpPrefix); + } + + if( RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + + // Read the attributes + + resetAttrList(); + + uChar = peekChar(); + if( uChar != FLM_UNICODE_GT && uChar != FLM_UNICODE_FSLASH) + { + if( RC_BAD( rc = processAttributeList())) + { + goto Exit; + } + } + + // Find or create the element's name ID + + if( RC_BAD( rc = findNamespace( puzPrefix, &pNamespace))) + { + if( rc == NE_FLM_NOT_FOUND) + { + setErrInfo( uiSavedLineNum, + uiSavedOffset, + XML_ERR_PREFIX_NOT_DEFINED, + uiSavedFilePos, + uiSavedLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + } + goto Exit; + } + +// if( RC_BAD( rc = m_pDb->getElementNameId( +// pNamespace->getURIPtr(), puzLocal, &uiNameId))) +// { +// if( rc != NE_FLM_NOT_FOUND) +// { +// goto Exit; +// } +// +// if( !(m_uiFlags & FLM_XML_EXTEND_DICT_FLAG) || +// (pNamespace->getURIPtr() && +// f_unicmp( pNamespace->getURIPtr(), gv_uzXFLAIMNamespace) == 0)) +// { +// rc = RC_SET( NE_FLM_UNDEFINED_ELEMENT_NAME); +// goto Exit; +// } +// +// // Automatically extend the schema +// +// uiNameId = 0; +// if( RC_BAD( rc = m_pDb->createElementDef( +// pNamespace->getURIPtr(), +// puzLocal, FLM_TEXT_TYPE, &uiNameId))) +// { +// goto Exit; +// } +// } + + // Create the element node + + if( pNodeToLinkTo) + { + if( RC_BAD( rc = pNodeToLinkTo->createNode( ELEMENT_NODE, + uiNameId, eInsertLoc, &pElement))) + { + setErrInfo( uiSavedLineNum, + uiSavedOffset, + XML_ERR_CREATING_ELEMENT_NODE, + uiSavedFilePos, + uiSavedLineBytes); + goto Exit; + } + } + else + { +// if( RC_BAD( rc = m_pDb->createRootElement( uiNameId, &pElement))) +// { +// setErrInfo( uiSavedLineNum, +// uiSavedOffset, +// XML_ERR_CREATING_ROOT_ELEMENT, +// uiSavedFilePos, +// uiSavedLineBytes); +// goto Exit; +// } + } + + if( RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + + // Need to end with ">" or "/>" + + uChar = getChar(); + if( uChar == FLM_UNICODE_GT) + { + *pbHasContent = TRUE; + } + else if( uChar == FLM_UNICODE_FSLASH) + { + if( getChar() != FLM_UNICODE_GT) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + XML_ERR_EXPECTING_GT, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + } + else + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + XML_ERR_EXPECTING_GT, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + + // Set the element's prefix + + if( RC_BAD( rc = addAttributesToElement( pElement))) + { + goto Exit; + } + + if( puzPrefix) + { + if( RC_BAD( rc = pElement->setPrefix( puzPrefix))) + { + goto Exit; + } + } + + if( ppElement) + { + *ppElement = pElement; + pElement = NULL; + } + + m_importStats.uiElements++; + if( m_fnStatus && (m_importStats.uiElements % 50) == 0) + { + m_fnStatus( XML_STATS, + (void *)&m_importStats, NULL, NULL, m_pvCallbackData); + } + +Exit: + + if( pElement) + { + pElement->Release(); + } + + if( pNamespace) + { + pNamespace->Release(); + } + + m_tmpPool.poolReset( pvMark); + return( rc); +} + +/**************************************************************************** +Desc: Processes an element's attributes +****************************************************************************/ +RCODE F_XMLParser::processAttributeList( void) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiChars; + FLMUNICODE * puzLocal; + FLMUNICODE * puzPrefix; + XML_ATTR * pAttr = NULL; + FLMBOOL bFoundDefaultNamespace = FALSE; + FLMUINT uiNamespaceCount = 0; + FLMBOOL bNamespaceDecl; + FLMBOOL bDefaultNamespaceDecl; + FLMUINT uiSavedLineNum; + FLMUINT uiSavedOffset; + FLMUINT uiSavedFilePos; + FLMUINT uiSavedLineBytes; + + for( ;;) + { + if( RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + if( !isNameChar( peekChar())) + { + break; + } + + uiSavedLineNum = m_uiCurrLineNum; + uiSavedOffset = m_uiCurrLineOffset; + uiSavedFilePos = m_uiCurrLineFilePos; + uiSavedLineBytes = m_uiCurrLineBytes; + if( RC_BAD( rc = getQualifiedName( &uiChars, + &puzPrefix, &puzLocal, &bNamespaceDecl, + &bDefaultNamespaceDecl))) + { + goto Exit; + } + + if( RC_BAD( rc = allocAttribute( &pAttr))) + { + goto Exit; + } + pAttr->uiLineNum = uiSavedLineNum; + pAttr->uiLineOffset = uiSavedOffset; + pAttr->uiLineFilePos = uiSavedFilePos; + pAttr->uiLineBytes = uiSavedLineBytes; + + if( RC_BAD( rc = setPrefix( pAttr, puzPrefix))) + { + goto Exit; + } + + if( RC_BAD( rc = setLocalName( pAttr, puzLocal))) + { + goto Exit; + } + if (bNamespaceDecl) + { + if (bDefaultNamespaceDecl) + { + pAttr->uiFlags |= F_DEFAULT_NS_DECL; + } + else + { + pAttr->uiFlags |= F_PREFIXED_NS_DECL; + } + } + + if( RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + + // Attribute name must be followed by an "=" + + if( getChar() != FLM_UNICODE_EQ) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + XML_ERR_EXPECTING_EQ, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + + if( RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + + pAttr->uiValueLineNum = m_uiCurrLineNum; + pAttr->uiValueLineOffset = m_uiCurrLineOffset; + if( RC_BAD( rc = processAttValue( pAttr))) + { + goto Exit; + } + + m_importStats.uiAttributes++; + if( m_fnStatus && (m_importStats.uiAttributes % 50) == 0) + { + m_fnStatus( XML_STATS, + (void *)&m_importStats, NULL, NULL, m_pvCallbackData); + } + } + + // Push any namespace declarations onto the stack + + for( pAttr = m_pFirstAttr; pAttr; pAttr = pAttr->pNext) + { + // Duplicate namespace declarations are not allowed within a single element. + // So, multiple default namespace declarations or multiple uses of the same + // prefix in will result in a syntax error. + + if( pAttr->uiFlags & F_DEFAULT_NS_DECL) + { + // Default namespace declaration + + if( bFoundDefaultNamespace) + { + setErrInfo( pAttr->uiLineNum, + pAttr->uiLineOffset, + XML_ERR_MULTIPLE_XMLNS_DECLS, + pAttr->uiLineFilePos, + pAttr->uiLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + + if( !pAttr->puzVal || *pAttr->puzVal == 0) + { + // No namespace + + if( RC_BAD( rc = pushNamespace( NULL, NULL))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pushNamespace( NULL, pAttr->puzVal))) + { + goto Exit; + } + } + + uiNamespaceCount++; + bFoundDefaultNamespace = TRUE; + } + else if( pAttr->uiFlags & F_PREFIXED_NS_DECL) + { + // Check for a unique prefix within current element + + if( RC_OK( rc = findNamespace( &pAttr->puzLocalName [6], + NULL, uiNamespaceCount))) + { + setErrInfo( pAttr->uiLineNum, + pAttr->uiLineOffset, + XML_ERR_MULTIPLE_PREFIX_DECLS, + pAttr->uiLineFilePos, + pAttr->uiLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + else if( rc != NE_FLM_NOT_FOUND) + { + goto Exit; + } + else + { + rc = NE_FLM_OK; + } + + if( RC_BAD( rc = pushNamespace( + &pAttr->puzLocalName [6], pAttr->puzVal))) + { + goto Exit; + } + + uiNamespaceCount++; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Processes an XML declaration +****************************************************************************/ +RCODE F_XMLParser::processXMLDecl( void) +{ + RCODE rc = NE_FLM_OK; + FLMUNICODE uChar; + + // Have already eaten the "")) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + XML_ERR_EXPECTING_QUEST_GT, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Processes an XML document type declaration +****************************************************************************/ +RCODE F_XMLParser::processDocTypeDecl( void) +{ + RCODE rc = NE_FLM_OK; + FLMUNICODE uChar; + + // Have already eaten the "' + + if( RC_BAD( rc = skipWhitespace( TRUE))) + { + goto Exit; + } + } + + // Get the system ID + + if (RC_BAD( rc = getSystemLiteral())) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Processes a notation declaration +****************************************************************************/ +RCODE F_XMLParser::processNotationDecl( void) +{ + RCODE rc = NE_FLM_OK; + + // Have already eaten up the "#PCDATA" + + for( ;;) + { + if( RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + + uChar = getChar(); + if( uChar == FLM_UNICODE_RPAREN) + { + break; + } + else if( uChar == FLM_UNICODE_PIPE) + { + if( RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + + if( RC_BAD( rc = getName( NULL))) + { + goto Exit; + } + + bExpectingAsterisk = TRUE; + } + else + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + XML_ERR_EXPECTING_RPAREN_OR_PIPE, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + } + + if( bExpectingAsterisk) + { + if( getChar() != FLM_UNICODE_ASTERISK) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + XML_ERR_EXPECTING_ASTERISK, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Processes child content +****************************************************************************/ +RCODE F_XMLParser::processChildContent( void) +{ + RCODE rc = NE_FLM_OK; + FLMUNICODE uChar; + FLMUINT uiItemCount = 0; + FLMUINT uiDelimCount = 0; + FLMBOOL bChoice = FALSE; + FLMBOOL bSeq = FALSE; + + // Have eaten up the "(" + + for( ;;) + { + if( RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + + uChar = getChar(); + if( uChar == FLM_UNICODE_LPAREN) + { + if( RC_BAD( rc = processChildContent())) + { + goto Exit; + } + + uiItemCount++; + } + else if (uChar == FLM_UNICODE_RPAREN) + { + if( !uiItemCount || (uiItemCount - 1) != uiDelimCount) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + XML_ERR_EMPTY_CONTENT_INVALID, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + + break; + } + else if (uChar == FLM_UNICODE_PIPE) + { + if( bSeq) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + XML_ERR_CANNOT_MIX_CHOICE_AND_SEQ, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + bChoice = TRUE; + uiDelimCount++; + } + else if (uChar == FLM_UNICODE_COMMA) + { + if (bChoice) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + XML_ERR_CANNOT_MIX_CHOICE_AND_SEQ, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + bSeq = TRUE; + uiDelimCount++; + } + else + { + ungetChar(); + if( RC_BAD( rc = getName( NULL))) + { + goto Exit; + } + uiItemCount++; + + uChar = peekChar(); + if (uChar == FLM_UNICODE_QUEST || + uChar == FLM_UNICODE_ASTERISK || + uChar == FLM_UNICODE_PLUS) + { + + // Eat up a "?", "*", or "+" + + (void)getChar(); + } + } + } + + uChar = peekChar(); + if( uChar == FLM_UNICODE_QUEST || + uChar == FLM_UNICODE_ASTERISK || + uChar == FLM_UNICODE_PLUS) + { + // Eat up a "?", "*", or "+" + + (void)getChar(); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Processes a misc. declaration +****************************************************************************/ +RCODE F_XMLParser::processMisc( void) +{ + RCODE rc = NE_FLM_OK; + + for( ;;) + { + if( RC_BAD( rc = skipWhitespace( FALSE))) + { + if( rc == NE_FLM_IO_END_OF_FILE || rc == NE_FLM_EOF_HIT) + { + rc = NE_FLM_OK; + } + + goto Exit; + } + + if (lineHasToken( "")) + { + break; + } + + if ((uChar = getChar()) == 0) + { + if (RC_BAD( rc = getLine())) + { + goto Exit; + } + uChar = ASCII_NEWLINE; + } + + *((FLMUNICODE *)(&m_pucValBuf[ uiOffset])) = uChar; + uiOffset += sizeof( FLMUNICODE); + + if( uiOffset >= uiMaxOffset) + { + if (RC_BAD( rc = resizeValBuffer( ~((FLMUINT)0)))) + { + goto Exit; + } + uiMaxOffset = m_uiValBufSize; + } + } + + if( pParent) + { + if( RC_BAD( rc = pParent->createNode( COMMENT_NODE, 0, + FLM_LAST_CHILD, &pComment))) + { + setErrInfo( uiSavedLineNum, + uiSavedOffset, + XML_ERR_CREATING_COMMENT_NODE, + uiSavedFilePos, + uiSavedLineBytes); + goto Exit; + } + + *((FLMUNICODE *)(&m_pucValBuf[ uiOffset])) = 0; + + if( RC_BAD( rc = pComment->setUnicode( (FLMUNICODE *)m_pucValBuf))) + { + goto Exit; + } + + pComment->Release(); + pComment = NULL; + } + +Exit: + + if( pComment) + { + pComment->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Processes a CDATA tag +****************************************************************************/ +RCODE F_XMLParser::processCDATA( + IF_DOMNode * pParent, + FLMUINT uiSavedLineNum, + FLMUINT uiSavedOffset, + FLMUINT uiSavedFilePos, + FLMUINT uiSavedLineBytes) +{ + RCODE rc = NE_FLM_OK; + FLMUNICODE uChar; + FLMUINT uiOffset = 0; + IF_DOMNode * pCData = NULL; + + // Have already eaten up the "")) + { + break; + } + if ((uChar = getChar()) == 0) + { + if (RC_BAD( rc = getLine())) + { + goto Exit; + } + uChar = ASCII_NEWLINE; + } + + *((FLMUNICODE *)(&m_pucValBuf[ uiOffset])) = uChar; + uiOffset += sizeof( FLMUNICODE); + + if( uiOffset >= m_uiValBufSize) + { + if( RC_BAD( rc = resizeValBuffer( ~((FLMUINT)0)))) + { + goto Exit; + } + } + } + + if( pParent) + { + if( RC_BAD( rc = pParent->createNode( CDATA_SECTION_NODE, 0, + FLM_LAST_CHILD, &pCData))) + { + setErrInfo( uiSavedLineNum, + uiSavedOffset, + XML_ERR_CREATING_CDATA_NODE, + uiSavedFilePos, + uiSavedLineBytes); + goto Exit; + } + + *((FLMUNICODE *)(&m_pucValBuf[ uiOffset])) = 0; + if( RC_BAD( rc = pCData->setUnicode( (FLMUNICODE *)m_pucValBuf))) + { + goto Exit; + } + + pCData->Release(); + pCData = NULL; + } + +Exit: + + if( pCData) + { + pCData->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Skips any whitespace characters in the input stream +****************************************************************************/ +RCODE F_XMLParser::skipWhitespace( + FLMBOOL bRequired) +{ + RCODE rc = NE_FLM_OK; + FLMUNICODE uChar; + FLMUINT uiCount = 0; + + for( ;;) + { + if ((uChar = getChar()) == 0) + { + uiCount++; + if (RC_BAD( rc = getLine())) + { + goto Exit; + } + continue; + } + + if( !isWhitespace( uChar)) + { + ungetChar(); + break; + } + uiCount++; + } + + if( !uiCount && bRequired) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + XML_ERR_EXPECTING_WHITESPACE, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_XMLParser::resizeValBuffer( + FLMUINT uiSize) +{ + RCODE rc = NE_FLM_OK; + + if( uiSize == m_uiValBufSize) + { + goto Exit; + } + + if( uiSize == ~((FLMUINT)0)) + { + uiSize = m_uiValBufSize + 2048; + } + + if( m_pucValBuf) + { + if( uiSize) + { + if( RC_BAD( rc = f_realloc( uiSize, &m_pucValBuf))) + { + goto Exit; + } + } + else + { + f_free( &m_pucValBuf); + m_pucValBuf = NULL; + } + } + else + { + flmAssert( !m_pucValBuf); + + if( RC_BAD( rc = f_alloc( uiSize, &m_pucValBuf))) + { + goto Exit; + } + } + + m_uiValBufSize = uiSize; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_XMLParser::getBinaryVal( + FLMUINT * puiLength) +{ + RCODE rc = NE_FLM_OK; + FLMUNICODE uChar; + FLMUNICODE uChar2; + FLMUINT uiOffset = 0; + FLMBOOL bHavePreamble; + + flmAssert( *puiLength == 0); + + for( ;;) + { + bHavePreamble = FALSE; + + if( RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + +Retry: + + uChar = getChar(); + if( !f_isHexChar( uChar)) + { + if( uChar != FLM_UNICODE_LT) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + XML_ERR_EXPECTING_HEX_DIGIT, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + ungetChar(); + break; + } + + uChar2 = getChar(); + if( uChar == FLM_UNICODE_0 && + (uChar2 == FLM_UNICODE_X || uChar2 == FLM_UNICODE_x)) + { + if( bHavePreamble) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + XML_ERR_EXPECTING_HEX_DIGIT, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + + bHavePreamble = TRUE; + goto Retry; + } + + if( !f_isHexChar( uChar2)) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + XML_ERR_EXPECTING_HEX_DIGIT, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + + if( uiOffset >= m_uiValBufSize) + { + if( RC_BAD( rc = resizeValBuffer( ~((FLMUINT)0)))) + { + goto Exit; + } + } + + m_pucValBuf[ uiOffset++] = + (f_getHexVal( uChar) << 4) | f_getHexVal( uChar2); + + if( RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + + uChar = getChar(); + if( uChar != FLM_UNICODE_COMMA) + { + ungetChar(); + } + } + + *puiLength = uiOffset; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Constructor +****************************************************************************/ +F_XMLNamespaceMgr::F_XMLNamespaceMgr() +{ + m_pFirstNamespace = NULL; + m_uiNamespaceCount = 0; +} + +/**************************************************************************** +Desc: Destructor +****************************************************************************/ +F_XMLNamespaceMgr::~F_XMLNamespaceMgr() +{ + popNamespaces( m_uiNamespaceCount); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_XMLNamespaceMgr::popNamespaces( + FLMUINT uiCount) +{ + F_XMLNamespace * pTmpNamespace; + + flmAssert( uiCount <= m_uiNamespaceCount); + + while( uiCount && m_pFirstNamespace) + { + pTmpNamespace = m_pFirstNamespace; + m_pFirstNamespace = m_pFirstNamespace->m_pNext; + pTmpNamespace->m_pNext = NULL; + pTmpNamespace->Release(); + m_uiNamespaceCount--; + uiCount--; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_XMLNamespaceMgr::findNamespace( + FLMUNICODE * puzPrefix, + F_XMLNamespace ** ppNamespace, + FLMUINT uiMaxSearchSize) +{ + RCODE rc = NE_FLM_OK; + F_XMLNamespace * pTmpNamespace = m_pFirstNamespace; + + while( pTmpNamespace) + { + if( !uiMaxSearchSize) + { + pTmpNamespace = NULL; + break; + } + + if( !puzPrefix && !pTmpNamespace->m_puzPrefix) + { + break; + } + else if( puzPrefix && pTmpNamespace->m_puzPrefix) + { + if( f_unicmp( puzPrefix, pTmpNamespace->m_puzPrefix) == 0) + { + break; + } + } + + pTmpNamespace = pTmpNamespace->m_pNext; + uiMaxSearchSize--; + } + + if( !pTmpNamespace) + { + rc = RC_SET( NE_FLM_NOT_FOUND); + goto Exit; + } + + if( ppNamespace) + { + if( *ppNamespace) + { + (*ppNamespace)->Release(); + } + + pTmpNamespace->AddRef(); + *ppNamespace = pTmpNamespace; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_XMLNamespaceMgr::pushNamespace( + FLMUNICODE * puzPrefix, + FLMUNICODE * puzNamespaceURI) +{ + RCODE rc = NE_FLM_OK; + F_XMLNamespace * pNewNamespace = NULL; + + if( (pNewNamespace = f_new F_XMLNamespace) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewNamespace->setPrefix( puzPrefix))) + { + goto Exit; + } + + if( RC_BAD( rc = pNewNamespace->setURI( puzNamespaceURI))) + { + goto Exit; + } + + pNewNamespace->m_pNext = m_pFirstNamespace; + m_pFirstNamespace = pNewNamespace; + pNewNamespace = NULL; + m_uiNamespaceCount++; + +Exit: + + if( pNewNamespace) + { + pNewNamespace->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_XMLNamespaceMgr::pushNamespace( + F_XMLNamespace * pNamespace) +{ + flmAssert( m_pFirstNamespace != pNamespace && + !pNamespace->m_pNext); + + pNamespace->AddRef(); + pNamespace->m_pNext = m_pFirstNamespace; + m_pFirstNamespace = pNamespace; + m_uiNamespaceCount++; + + return( NE_FLM_OK); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_XMLNamespace::setPrefix( + FLMUNICODE * puzPrefix) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiLen; + + if( m_puzPrefix) + { + f_free( &m_puzPrefix); + } + + if( puzPrefix) + { + uiLen = f_unilen( puzPrefix); + if( RC_BAD( rc = f_alloc( sizeof( FLMUNICODE) * (uiLen + 1), + &m_puzPrefix))) + { + goto Exit; + } + + f_unicpy( m_puzPrefix, puzPrefix); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_XMLNamespace::setURI( + FLMUNICODE * puzURI) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiLen; + + if( m_puzURI) + { + f_free( &m_puzURI); + } + + if( puzURI) + { + uiLen = f_unilen( puzURI); + if( RC_BAD( rc = f_alloc( + sizeof( FLMUNICODE) * (uiLen + 1), &m_puzURI))) + { + goto Exit; + } + + f_unicpy( m_puzURI, puzURI); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_XMLNamespace::setup( + FLMUNICODE * puzPrefix, + FLMUNICODE * puzURI, + F_XMLNamespace * pNext) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiLen; + + flmAssert( !m_puzPrefix); + flmAssert( !m_puzURI); + flmAssert( !m_pNext); + + if( puzPrefix) + { + uiLen = f_unilen( puzPrefix); + if( RC_BAD( rc = f_alloc( sizeof( FLMUNICODE) * (uiLen + 1), + &m_puzPrefix))) + { + goto Exit; + } + + f_unicpy( m_puzPrefix, puzPrefix); + } + + if( puzURI) + { + uiLen = f_unilen( puzURI); + if( RC_BAD( rc = f_alloc( sizeof( FLMUNICODE) * (uiLen + 1), + &m_puzURI))) + { + goto Exit; + } + + f_unicpy( m_puzURI, puzURI); + } + + m_pNext = pNext; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#if 0 +RCODE F_XMLParser::addAttributesToElement( + IF_DOMNode * pElement) +{ + RCODE rc = NE_FLM_OK; + XML_ATTR * pAttr; + F_XMLNamespace * pNamespace = NULL; + IF_DOMNode * pTmpNode = NULL; + FLMUINT uiNameId; + eFlmDataType attrDataType; + + // Make sure any prefixes (e.g., xmlns:xxxx) are added to the database + // before they are used - in case they are used by the attributes + // themselves. + + for( pAttr = m_pFirstAttr; pAttr; pAttr = pAttr->pNext) + { + if( pAttr->uiFlags & F_PREFIXED_NS_DECL) + { + FLMUINT uiPrefixId; + + // Create the prefix (stored in &puzLocalName [6]) if it doesn't + // already exist + +// if( RC_BAD( rc = m_pDb->m_pDict->getPrefixId( m_pDb, +// &pAttr->puzLocalName [6], &uiPrefixId))) +// { +// if( rc != NE_FLM_NOT_FOUND) +// { +// goto Exit; +// } +// +// uiPrefixId = 0; +// if( RC_BAD( rc = m_pDb->createPrefixDef( TRUE, +// &pAttr->puzLocalName [6], &uiPrefixId))) +// { +// goto Exit; +// } +// } + } + } + + // Add the attributes to the element + // + // NOTE: The XML namespace specification states that the names + // of all unqualified attributes are assigned to the + // appropriate per-element-type partition. This means that + // the combination of the attribute name with the parent + // element's type and namespace name is used to uniquely + // identify each unqualified attribute. + // + // For sake of clarity and useability, however, the parser + // deviates from the namespace specification. Each unprefixed + // attribute encountered by the parser will inherit the + // namespace of the parent element, even if the namespace + // is a default namespace. + + for( pAttr = m_pFirstAttr; pAttr; pAttr = pAttr->pNext) + { + if( pAttr->uiFlags & F_DEFAULT_NS_DECL) + { + // Add the namespace declaration to the element + +// if( RC_BAD( rc = pElement->createAttribute( ATTR_XMLNS_TAG, +// &pTmpNode))) +// { +// goto Exit; +// } +// +// if( RC_BAD( rc = pTmpNode->setUnicode( pAttr->puzVal))) +// { +// goto Exit; +// } + +// if( RC_BAD( rc = pTmpNode->addModeFlags( m_pDb, FDOM_READ_ONLY))) +// { +// goto Exit; +// } + } + else if( pAttr->uiFlags & F_PREFIXED_NS_DECL) + { + // Find the attribute definition + +// if( RC_BAD( rc = m_pDb->getAttributeNameId( +// NULL, pAttr->puzLocalName, &uiNameId))) +// { +// if( rc != NE_FLM_NOT_FOUND) +// { +// goto Exit; +// } +// +// if( !(m_uiFlags & FLM_XML_EXTEND_DICT_FLAG)) +// { +// rc = RC_SET( NE_FLM_UNDEFINED_ATTRIBUTE_NAME); +// goto Exit; +// } +// +// uiNameId = 0; +// if( RC_BAD( rc = m_pDb->createAttributeDef( +// NULL, pAttr->puzLocalName, FLM_TEXT_TYPE, &uiNameId, +// &pTmpNode))) +// { +// goto Exit; +// } +// } + + // Add the namespace declaration to the element + +// if( RC_BAD( rc = pElement->createAttribute( m_pDb, uiNameId, +// &pTmpNode))) +// { +// goto Exit; +// } +// +// if( RC_BAD( rc = pTmpNode->setUnicode( m_pDb, pAttr->puzVal))) +// { +// goto Exit; +// } +// +// if( RC_BAD( rc = pTmpNode->addModeFlags( m_pDb, FDOM_READ_ONLY))) +// { +// goto Exit; +// } + } + else + { + if( pAttr->puzPrefix) + { + if( RC_BAD( rc = findNamespace( pAttr->puzPrefix, &pNamespace))) + { + if( rc == NE_FLM_NOT_FOUND) + { + setErrInfo( pAttr->uiLineNum, pAttr->uiLineOffset, + XML_ERR_PREFIX_NOT_DEFINED, pAttr->uiLineFilePos, + pAttr->uiLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + } + + goto Exit; + } + } + else + { + if( pNamespace) + { + pNamespace->Release(); + } + pNamespace = NULL; + } + +// if( RC_BAD( rc = m_pDb->getAttributeNameId( +// pNamespace ? pNamespace->getURIPtr() : NULL, +// pAttr->puzLocalName, &uiNameId))) +// { +// if( rc != NE_FLM_NOT_FOUND) +// { +// goto Exit; +// } +// +// if( !(m_uiFlags & FLM_XML_EXTEND_DICT_FLAG) || +// (pNamespace && +// f_unicmp( pNamespace->getURIPtr(), gv_uzXFLAIMNamespace) == 0)) +// { +// rc = RC_SET( NE_FLM_UNDEFINED_ATTRIBUTE_NAME); +// goto Exit; +// } +// +// uiNameId = 0; +// if( RC_BAD( rc = m_pDb->createAttributeDef( +// pNamespace ? pNamespace->getURIPtr() : NULL, +// pAttr->puzLocalName, FLM_TEXT_TYPE, &uiNameId))) +// { +// goto Exit; +// } +// } + +// if( RC_BAD( rc = pElement->createAttribute( m_pDb, uiNameId, +// &pTmpNode))) +// { +// goto Exit; +// } +// +// if (pAttr->puzPrefix) +// { +// if( RC_BAD( rc = pTmpNode->setPrefix( m_pDb, pAttr->puzPrefix))) +// { +// if( rc == NE_FLM_NOT_FOUND) +// { +// setErrInfo( pAttr->uiLineNum, +// pAttr->uiLineOffset, +// XML_ERR_PREFIX_NOT_DEFINED, +// pAttr->uiLineFilePos, +// pAttr->uiLineBytes); +// rc = RC_SET( NE_FLM_INVALID_XML); +// } +// goto Exit; +// } +// } +// + if( RC_BAD( rc = pTmpNode->getDataType( &attrDataType))) + { + goto Exit; + } + + switch( attrDataType) + { + case FLM_TEXT_TYPE: + { + if( RC_BAD( rc = pTmpNode->setUnicode( pAttr->puzVal))) + { + goto Exit; + } + break; + } + + case FLM_NUMBER_TYPE: + { + FLMUINT64 ui64Val; + FLMBOOL bNeg; + + if( RC_BAD( rc = unicodeToNumber64( + pAttr->puzVal, &ui64Val, &bNeg))) + { + goto Exit; + } + + if( !bNeg) + { + if( RC_BAD( rc = pTmpNode->setUINT64( ui64Val))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pTmpNode->setINT64( -((FLMINT64)ui64Val)))) + { + goto Exit; + } + } + + break; + } + + case FLM_BINARY_TYPE: + { + FLMBOOL bHavePreamble; + FLMUNICODE * puzStr = pAttr->puzVal; + FLMUINT uiOffset = 0; + + // Convert the Unicode value to binary + + while( puzStr && *puzStr) + { + bHavePreamble = FALSE; + + while( isWhitespace( *puzStr)) + { + puzStr++; + } + + Retry: + + if( !f_isHexChar( *puzStr)) + { + break; + } + + if( *puzStr == FLM_UNICODE_0 && + (puzStr[ 1] == FLM_UNICODE_X || puzStr[ 1] == FLM_UNICODE_x)) + { + if( bHavePreamble) + { + setErrInfo( pAttr->uiValueLineNum, + pAttr->uiValueLineOffset, + XML_ERR_INVALID_BINARY_ATTR_VALUE, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + + bHavePreamble = TRUE; + puzStr += 2; + goto Retry; + } + + if( !f_isHexChar( puzStr[ 1])) + { + setErrInfo( pAttr->uiValueLineNum, + pAttr->uiValueLineOffset, + XML_ERR_INVALID_BINARY_ATTR_VALUE, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_FLM_INVALID_XML); + goto Exit; + } + + if( uiOffset >= m_uiValBufSize) + { + if( RC_BAD( rc = resizeValBuffer( ~((FLMUINT)0)))) + { + goto Exit; + } + } + + m_pucValBuf[ uiOffset++] = + (f_getHexVal( *puzStr) << 4) | f_getHexVal( puzStr[ 1]); + + puzStr += 2; + + while( isWhitespace( *puzStr)) + { + puzStr++; + } + + if( *puzStr == FLM_UNICODE_COMMA) + { + puzStr++; + } + } + + if( RC_BAD( rc = pTmpNode->setBinary( m_pucValBuf, uiOffset))) + { + goto Exit; + } + break; + } + + default: + { + rc = RC_SET_AND_ASSERT( NE_FLM_NOT_IMPLEMENTED); + goto Exit; + } + } + } + } + +Exit: + + if( pTmpNode) + { + pTmpNode->Release(); + } + + if( pNamespace) + { + pNamespace->Release(); + } + + return( rc); +} +#endif + +// Some forward declarations + +class F_Element; +class F_Attribute; + +/***************************************************************************** +Desc: Keeps track of an attribute that we are going to output +*****************************************************************************/ +class F_Attribute : public F_Base +{ +public: + F_Attribute( + F_Element * pElement) + { + m_uiTmpSpaceSize = sizeof( m_uzTmpSpace); + m_puzName = &m_uzTmpSpace [0]; + reset( pElement); + } + + ~F_Attribute(); + + FINLINE void reset( + F_Element * pElement) + { + m_uiNameChars = 0; + m_bIsNamespaceDecl = FALSE; + m_bDefaultNamespaceDecl = FALSE; + m_uiNamespaceChars = 0; + m_uiValueChars = 0; + m_uiPrefixChars = 0; + m_pElement = pElement; + } + + RCODE allocNameSpace( void); + +// RCODE setupAttribute( +// IF_Db * pDb, +// IF_DOMNode * pNode); + + RCODE setPrefix( void); + + RCODE outputAttr( + IF_OStream * pOStream); + + FINLINE F_Attribute * getNext( void) + { + return( m_pNext); + } + +private: + FLMUNICODE m_uzTmpSpace [150]; + FLMUINT m_uiTmpSpaceSize; + FLMBOOL m_bIsNamespaceDecl; + FLMBOOL m_bDefaultNamespaceDecl; + FLMUNICODE * m_puzName; + FLMUINT m_uiNameChars; + FLMUNICODE * m_puzNamespace; + FLMUINT m_uiNamespaceChars; + FLMUNICODE * m_puzValue; + FLMUINT m_uiValueChars; + FLMUNICODE * m_puzPrefix; + FLMUINT m_uiPrefixChars; + F_Element * m_pElement; + F_Attribute * m_pNext; + +friend class F_Element; +}; + +/***************************************************************************** +Desc: Destructor for F_Attribute class. +*****************************************************************************/ +F_Attribute::~F_Attribute() +{ + if (m_puzName != &m_uzTmpSpace [0]) + { + f_free( &m_puzName); + } +} + +/***************************************************************************** +Desc: Keeps track of an element that we are going to output +*****************************************************************************/ +class F_Element : public F_Base +{ +public: + F_Element( + F_Element * pParentElement, + F_Attribute ** ppAvailAttrs, + FLMUINT * puiNextPrefixNum) + { + m_uiTmpSpaceSize = sizeof( m_uzTmpSpace); + m_puzName = &m_uzTmpSpace [0]; + m_pFirstAttr = NULL; + m_pLastAttr = NULL; + m_pNext = NULL; + m_uiIndentCount = 0; + m_bIsDocumentRoot = FALSE; + reset( pParentElement, ppAvailAttrs, puiNextPrefixNum); + } + + ~F_Element() + { + F_Attribute * pAttr; + F_Attribute * pTmpAttr; + + // Delete all of the attributes + + pAttr = m_pFirstAttr; + while (pAttr) + { + pTmpAttr = pAttr; + pAttr = pAttr->m_pNext; + delete pTmpAttr; + } + + if (m_puzName != &m_uzTmpSpace [0]) + { + f_free( &m_puzName); + } + } + + FINLINE void reset( + F_Element * pParentElement, + F_Attribute ** ppAvailAttrs, + FLMUINT * puiNextPrefixNum) + { + m_uiNameChars = 0; + m_uiNamespaceChars = 0; + m_uiPrefixChars = 0; + m_pParentElement = pParentElement; + m_puiNextPrefixNum = puiNextPrefixNum; + m_ppAvailAttrs = ppAvailAttrs; + m_pNext = NULL; + m_uiIndentCount = 0; + m_bIsDocumentRoot = FALSE; + } + + FINLINE void setIndentCount( + FLMUINT uiIndentCount) + { + m_uiIndentCount = uiIndentCount; + } + + FINLINE void setDocumentRoot( + FLMBOOL bIsDocumentRoot) + { + m_bIsDocumentRoot = bIsDocumentRoot; + } + + RCODE allocAttr( + F_Attribute ** ppAttr); + + FINLINE void makeAttrAvail( + F_Attribute * pAttr) + { + pAttr->m_pNext = *m_ppAvailAttrs; + *m_ppAvailAttrs = pAttr; + } + + FINLINE void makeAllAttrsAvail( void) + { + if (m_pFirstAttr) + { + m_pLastAttr->m_pNext = *m_ppAvailAttrs; + *m_ppAvailAttrs = m_pFirstAttr; + m_pFirstAttr = NULL; + m_pLastAttr = NULL; + } + } + +// RCODE saveAttribute( +// IF_Db * pDb, +// IF_DOMNode * pNode); + + RCODE allocNameSpace( void); + +// RCODE setupElement( +// IF_Db * pDb, +// IF_DOMNode * pNode); + + RCODE addNamespaceDecl( + FLMUNICODE * puzPrefix, + FLMUINT uiPrefixChars, + FLMUNICODE * puzNamespace, + FLMUINT uiNamespaceChars, + F_Attribute ** ppAttr); + + void genPrefix( + FLMUNICODE * puzPrefix, + FLMUINT * puiPrefixChars); + + RCODE findPrefix( + FLMUNICODE * puzNamespace, + FLMUINT uiNamespaceChars, + FLMBOOL bForElement, + FLMUNICODE ** ppuzPrefix, + FLMUINT * puiPrefixChars); + + FINLINE RCODE setPrefix( void) + { + return( findPrefix( m_puzNamespace, m_uiNamespaceChars, TRUE, + &m_puzPrefix, &m_uiPrefixChars)); + } + + FINLINE F_Element * getParentElement( void) + { + return( m_pParentElement); + } + + RCODE outputElem( + IF_OStream * pOStream, + FLMBOOL bStartOfElement, + FLMBOOL bEndOfElement, + FLMBOOL bAddNewLine); + +// RCODE outputLocalData( +// IF_OStream * pOStream, +// IF_DOMNode * pDbNode, +// IF_Db * ifpDb, +// eExportFormatType eFormatType, +// FLMUINT uiIndentCount); + + FINLINE F_Element * getNext( void) + { + return( m_pNext); + } + + FINLINE void makeAvail( + F_Element ** ppAvailElements) + { + m_pNext = *ppAvailElements; + *ppAvailElements = this; + } + +private: + FLMUNICODE m_uzTmpSpace [100]; + FLMUINT m_uiTmpSpaceSize; + FLMUNICODE * m_puzName; + FLMUINT m_uiNameChars; + FLMUNICODE * m_puzNamespace; + FLMUINT m_uiNamespaceChars; + FLMUNICODE * m_puzPrefix; + FLMUINT m_uiPrefixChars; + F_Attribute * m_pFirstAttr; + F_Attribute * m_pLastAttr; + F_Element * m_pParentElement; + F_Element * m_pNext; + FLMUINT * m_puiNextPrefixNum; + F_Attribute ** m_ppAvailAttrs; + FLMBOOL m_bIsDocumentRoot; + FLMUINT m_uiIndentCount; + +friend class F_Attribute; +}; + +/***************************************************************************** +Desc: Allocate space to hold the name, namespace, and value for an attribute. +*****************************************************************************/ +RCODE F_Attribute::allocNameSpace( void) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiSpaceNeeded; + FLMUNICODE * puzTmp; + + uiSpaceNeeded = (m_uiNameChars + + m_uiNamespaceChars + + m_uiValueChars + 3) * sizeof( FLMUNICODE); + if (uiSpaceNeeded > m_uiTmpSpaceSize) + { + if (RC_BAD( rc = f_alloc( uiSpaceNeeded, &puzTmp))) + { + goto Exit; + } + if (m_puzName != &m_uzTmpSpace [0]) + { + f_free( &m_puzName); + } + m_puzName = puzTmp; + m_uiTmpSpaceSize = uiSpaceNeeded; + } + m_puzNamespace = &m_puzName [m_uiNameChars + 1]; + m_puzValue = &m_puzNamespace [m_uiNamespaceChars + 1]; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Setup an attribute with its namespace, etc. +*****************************************************************************/ +#if 0 +RCODE F_Attribute::setupAttribute( + IF_Db * pDb, + IF_DOMNode * pNode + ) +{ + RCODE rc = NE_FLM_OK; + + // Determine if the attribute is a namespace declaration + + if (RC_BAD( rc = pNode->isNamespaceDecl( pDb, &m_bIsNamespaceDecl))) + { + goto Exit; + } + + // Get the length of the name of the attribute + + if (RC_BAD( rc = pNode->getLocalName( pDb, (FLMUNICODE *)NULL, + 0, &m_uiNameChars))) + { + goto Exit; + } + + // If it is a namespace declaration, no need to get the namespace URI, + // we already know what it is, and we will output it with an xmlns prefix + // Otherwise, we need to get the namespace so we can determine a prefix, + // if any. If the namespace is the same namespace as the enclosing + // element, we do not need to output a prefix. + + if (!m_bIsNamespaceDecl) + { + + // Get the number of characters in the namespace of the attribute + + if (RC_BAD( rc = pNode->getNamespaceURI( pDb, (FLMUNICODE *)NULL, + 0, &m_uiNamespaceChars))) + { + goto Exit; + } + } + + // Get the number of characters in the attribute's value. + + if (RC_BAD( rc = pNode->getUnicodeChars( pDb, &m_uiValueChars))) + { + goto Exit; + } + + // Allocate space for the name, namespace, and value + + if (RC_BAD( rc = allocNameSpace())) + { + goto Exit; + } + + // Get the attribute name. + + if (RC_BAD( rc = pNode->getLocalName( pDb, m_puzName, + (m_uiNameChars + 1) * sizeof( FLMUNICODE), + &m_uiNameChars))) + { + goto Exit; + } + + // Get the namespace, if necessary + + if (m_uiNamespaceChars) + { + if (RC_BAD( rc = pNode->getNamespaceURI( pDb, m_puzNamespace, + (m_uiNamespaceChars + 1) * sizeof( FLMUNICODE), + &m_uiNamespaceChars))) + { + goto Exit; + } + } + + // Get the value, if any + + if (m_uiValueChars) + { + if (RC_BAD( rc = pNode->getUnicode( pDb, m_puzValue, + (m_uiValueChars + 1) * sizeof( FLMUNICODE), + 0, m_uiValueChars, &m_uiValueChars))) + { + goto Exit; + } + } + + // If it is a namespace declaration, the local name must either be + // "xmlns" or begin with "xmlns:" + + if (m_bIsNamespaceDecl) + { + + // Make sure name is "xmlns" or begins with "xmlns:" + + if (m_uiNameChars != 5 && m_uiNameChars <= 6) + { + rc = RC_SET_AND_ASSERT( NE_FLM_INVALID_NAMESPACE_DECL); + goto Exit; + } + + if (!isXMLNS( m_puzName)) + { + rc = RC_SET_AND_ASSERT( NE_FLM_INVALID_NAMESPACE_DECL); + goto Exit; + } + else if (m_uiNameChars == 5) + { + m_bDefaultNamespaceDecl = TRUE; + } + else if (m_puzName [5] != ':') + { + rc = RC_SET_AND_ASSERT( NE_FLM_INVALID_NAMESPACE_DECL); + goto Exit; + } + } + +Exit: + + return( rc); +} +#endif + +/***************************************************************************** +Desc: Set the prefix for an attribute. +*****************************************************************************/ +#if 0 +RCODE F_Attribute::setPrefix( void) +{ + RCODE rc = NE_FLM_OK; + + // If this is a namespace declaration, there should be no prefix in the name. + + if (m_bIsNamespaceDecl) + { + flmAssert( !m_uiPrefixChars); + } + + // Only need to set a prefix on an attribute if it has a namespace + // Otherwise, leave it alone - no prefix. + + else if (m_uiNamespaceChars) + { + + + // See if we can find a namespace declaration in either + // this element's attributes, or any of its parent element + // attributes. + + if (RC_BAD( rc = m_pElement->findPrefix( m_puzNamespace, + m_uiNamespaceChars, FALSE, + &m_puzPrefix, &m_uiPrefixChars))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} +#endif + +/***************************************************************************** +Desc: Export a unicode string to the string buffer - as UTF8. +*****************************************************************************/ +#if 0 +FSTATIC RCODE exportUniValue( + IF_OStream * pOStream, + FLMUNICODE * puzStr, + FLMUINT uiStrChars, + FLMBOOL bEncodeSpecialChars, + FLMUINT uiIndentCount + ) +{ + RCODE rc = NE_FLM_OK; + FLMBYTE ucTmp [4]; + FLMUINT uiLen; + FLMUINT uiCharOffset = 0; + FLMUNICODE uzChar; + FLMBOOL bIndent = FALSE; + FLMUINT uiICount = 0; + + while ( *puzStr && uiCharOffset < uiStrChars) + { + uzChar = *puzStr; + + // Handle encoding of special characters + + if (bEncodeSpecialChars) + { + if (uzChar == '<') + { + if (RC_BAD( rc = pOStream->write( (void *)"<", 4))) + { + goto Exit; + } + } + else if (uzChar == '>') + { + if (RC_BAD( rc = pOStream->write( (void *)">", 4))) + { + goto Exit; + } + } + else if (uzChar == '&') + { + if (RC_BAD( rc = pOStream->write( (void *)"&", 5))) + { + goto Exit; + } + } + else if (uzChar == '\'') + { + if (RC_BAD( rc = pOStream->write( (void *)"'", 6))) + { + goto Exit; + } + } + else if (uzChar == '"') + { + if (RC_BAD( rc = pOStream->write( (void *)""", 6))) + { + goto Exit; + } + } + else + { + goto Normal_Encoding; + } + } + else + { + +Normal_Encoding: + + // Output the character as UTF8. + + if (uzChar <= 0x007F) + { + // New Line char found. Need to indent. + if( uzChar == ASCII_NEWLINE) + { + bIndent = TRUE; + } + ucTmp [0] = (FLMBYTE)uzChar; + uiLen = 1; + } + else if (*puzStr <= 0x07FF) + { + ucTmp [0] = (FLMBYTE)(0xC0 | (FLMBYTE)(uzChar >> 6)); + ucTmp [1] = (FLMBYTE)(0x80 | (FLMBYTE)(uzChar & 0x003F)); + uiLen = 2; + } + else + { + ucTmp [0] = (FLMBYTE)(0xE0 | (FLMBYTE)(uzChar >> 12)); + ucTmp [1] = (FLMBYTE)(0x80 | (FLMBYTE)((uzChar & 0x0FC0) >> 6)); + ucTmp [2] = (FLMBYTE)(0x80 | (FLMBYTE)(uzChar & 0x003F)); + uiLen = 3; + } + if (RC_BAD( rc = pOStream->write( (void *)&ucTmp[0], uiLen))) + { + goto Exit; + } + + if( bIndent && uiIndentCount) + { + for( uiICount = uiIndentCount; uiICount; uiICount--) + { + if (RC_BAD( rc = pOStream->write( (void *)"\t", 1))) + { + goto Exit; + } + } + bIndent = FALSE; + } + } + puzStr++; + uiCharOffset++; + } + +Exit: + + return( rc); +} +#endif + +/***************************************************************************** +Desc: Output an attribute to the string buffer. +*****************************************************************************/ +#if 0 +RCODE F_Attribute::outputAttr( + IF_OStream * pOStream) +{ + RCODE rc = NE_FLM_OK; + + if (RC_BAD( rc = pOStream->write( (void *)" ", 1))) + { + goto Exit; + } + if (m_uiPrefixChars) + { + if (RC_BAD( rc = exportUniValue( pOStream, m_puzPrefix, m_uiPrefixChars, FALSE, 0))) + { + goto Exit; + } + if (RC_BAD( rc = pOStream->write( (void *)":", 1))) + { + goto Exit; + } + } + + if (RC_BAD( rc = exportUniValue( pOStream, m_puzName, m_uiNameChars, FALSE, 0))) + { + goto Exit; + } + + if (RC_BAD( rc = pOStream->write( (void *)"=\"", 2))) + { + goto Exit; + } + + if (RC_BAD( rc = exportUniValue( pOStream, m_puzValue, m_uiValueChars, TRUE, 0))) + { + goto Exit; + } + + if (RC_BAD( rc = pOStream->write( (void *)"\"", 1))) + { + goto Exit; + } + +Exit: + + return( rc); +} +#endif + +/***************************************************************************** +Desc: Allocate a new attribute. +*****************************************************************************/ +#if 0 +RCODE F_Element::allocAttr( + F_Attribute ** ppAttr + ) +{ + RCODE rc = NE_FLM_OK; + + if ((*ppAttr = *m_ppAvailAttrs) != NULL) + { + *m_ppAvailAttrs = (*ppAttr)->m_pNext; + (*ppAttr)->reset( this); + } + else + { + if ((*ppAttr = f_new F_Attribute( this)) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + } + +Exit: + + return( rc); +} +#endif + +/***************************************************************************** +Desc: Save an attribute in an element. Put at end of list. +*****************************************************************************/ +#if 0 +RCODE F_Element::saveAttribute( + IF_Db * pDb, + IF_DOMNode * pNode + ) +{ + RCODE rc = NE_FLM_OK; + F_Attribute * pAttr = NULL; + + if (RC_BAD( rc = allocAttr( &pAttr))) + { + goto Exit; + } + + // Set up the attribute + + if (RC_BAD( rc = pAttr->setupAttribute( pDb, pNode))) + { + goto Exit; + } + + // Put attribute at end of list of attributes. + + pAttr->m_pNext = NULL; + if (m_pLastAttr) + { + m_pLastAttr->m_pNext = pAttr; + } + else + { + m_pFirstAttr = pAttr; + } + m_pLastAttr = pAttr; + + // Set pAttr to NULL so it won't be made available at exit. + + pAttr = NULL; + +Exit: + + if (pAttr) + { + makeAttrAvail( pAttr); + } + + return( rc); +} +#endif + +/***************************************************************************** +Desc: Allocate space for the element's name and namespace. +*****************************************************************************/ +#if 0 +RCODE F_Element::allocNameSpace( void) +{ + RCODE rc = NE_FLM_OK; + FLMUINT uiSpaceNeeded; + FLMUNICODE * puzTmp; + + uiSpaceNeeded = (m_uiNameChars + m_uiNamespaceChars + 2) * sizeof( FLMUNICODE); + + // Allocate space for the name and namespace + + if (uiSpaceNeeded > m_uiTmpSpaceSize) + { + + if (RC_BAD( rc = f_alloc( uiSpaceNeeded, &puzTmp))) + { + goto Exit; + } + if (m_puzName != &m_uzTmpSpace [0]) + { + f_free( &m_puzName); + } + m_puzName = puzTmp; + m_uiTmpSpaceSize = uiSpaceNeeded; + } + m_puzNamespace = &m_puzName [m_uiNameChars + 1]; + +Exit: + + return( rc); +} +#endif + +/***************************************************************************** +Desc: Setup an element with its namespace, etc. +*****************************************************************************/ +#if 0 +RCODE F_Element::setupElement( + IF_Db * pDb, + IF_DOMNode * pNode + ) +{ + RCODE rc = NE_FLM_OK; + IF_DOMNode * pAttrNode = NULL; + F_Attribute * pAttr; + + // Get the length of the name of the element + + if (RC_BAD( rc = pNode->getLocalName( pDb, (FLMUNICODE *)NULL, + 0, &m_uiNameChars))) + { + goto Exit; + } + + // Get the number of characters in the namespace of the element + + if (RC_BAD( rc = pNode->getNamespaceURI( pDb, (FLMUNICODE *)NULL, + 0, &m_uiNamespaceChars))) + { + goto Exit; + } + + if (RC_BAD( rc = allocNameSpace())) + { + goto Exit; + } + + // Get the element name. + + if (RC_BAD( rc = pNode->getLocalName( pDb, m_puzName, + (m_uiNameChars + 1) * sizeof( FLMUNICODE), + &m_uiNameChars))) + { + goto Exit; + } + + // Get the namespace, if necessary + + if (m_uiNamespaceChars) + { + if (RC_BAD( rc = pNode->getNamespaceURI( pDb, m_puzNamespace, + (m_uiNamespaceChars + 1) * sizeof( FLMUNICODE), + &m_uiNamespaceChars))) + { + goto Exit; + } + } + + // See if the node has any attributes. + + for (;;) + { + rc = (RCODE)(pAttrNode + ? pAttrNode->getNextSibling( pDb, &pAttrNode) + : pNode->getFirstAttribute( pDb, &pAttrNode)); + if (RC_BAD( rc)) + { + if (rc == NE_FLM_DOM_NODE_NOT_FOUND) + { + rc = NE_FLM_OK; + break; + } + else + { + goto Exit; + } + } + if (RC_BAD( rc = saveAttribute( pDb, pAttrNode))) + { + goto Exit; + } + } + + // Get the prefix for the element + + if (RC_BAD( rc = setPrefix())) + { + goto Exit; + } + + // Set the prefix for every attribute + + pAttr = m_pFirstAttr; + while (pAttr) + { + if (RC_BAD( rc = pAttr->setPrefix())) + { + goto Exit; + } + pAttr = pAttr->m_pNext; + } + +Exit: + + if (pAttrNode) + { + pAttrNode->Release(); + } + + return( rc); +} +#endif + +/***************************************************************************** +Desc: Add an attribute that is a namespace to an element. +*****************************************************************************/ +#if 0 +RCODE F_Element::addNamespaceDecl( + FLMUNICODE * puzPrefix, + FLMUINT uiPrefixChars, + FLMUNICODE * puzNamespace, + FLMUINT uiNamespaceChars, + F_Attribute ** ppAttr + ) +{ + RCODE rc = NE_FLM_OK; + F_Attribute * pAttr = NULL; + + // If uiPrefixChars is zero, we are being asked to create a default + // namespace. But that can only be output once in the element, + // so make sure it is not already declared. If it is, do nothing. + + if (!uiPrefixChars) + { + pAttr = m_pFirstAttr; + while (pAttr && !pAttr->m_bDefaultNamespaceDecl) + { + pAttr = pAttr->m_pNext; + } + if (pAttr) + { + goto Exit; + } + } + + if (RC_BAD( rc = allocAttr( &pAttr))) + { + goto Exit; + } + pAttr->m_bIsNamespaceDecl = TRUE; + + // name will be "xmlns:" or, in the case of no namespace, "xmlns" + + if (!uiPrefixChars) + { + + // "xmlns" - but make sure not already declared. + + pAttr->m_uiNameChars = 5; + pAttr->m_bDefaultNamespaceDecl = TRUE; + } + else + { + + // "xmlns:" + + pAttr->m_uiNameChars = uiPrefixChars + 6; + } + pAttr->m_uiNamespaceChars = 0; + pAttr->m_uiValueChars = uiNamespaceChars; + + if (RC_BAD( rc = pAttr->allocNameSpace())) + { + goto Exit; + } + + // Always output "xmlns" as the first part of the name + + f_memcpy( pAttr->m_puzName, gv_puzNamespaceDeclPrefix, 5 * sizeof( FLMUNICODE)); + if (uiPrefixChars) + { + pAttr->m_puzName [5] = ':'; + f_memcpy( &pAttr->m_puzName [6], puzPrefix, uiPrefixChars * sizeof( FLMUNICODE)); + pAttr->m_puzName [6 + uiPrefixChars] = 0; + } + else + { + pAttr->m_puzName [5] = 0; + } + if (uiNamespaceChars) + { + f_memcpy( pAttr->m_puzValue, puzNamespace, + uiNamespaceChars * sizeof( FLMUNICODE)); + } + pAttr->m_puzValue [pAttr->m_uiValueChars] = 0; + + // Put new namespace decl at front of list of attributes. + + if ((pAttr->m_pNext = m_pFirstAttr) == NULL) + { + m_pLastAttr = pAttr; + } + m_pFirstAttr = pAttr; + *ppAttr = pAttr; + + // Set pAttr to NULL so that it won't be made available at exit. + + pAttr = NULL; + +Exit: + + if (pAttr) + { + makeAttrAvail( pAttr); + } + return( rc); +} +#endif + +/***************************************************************************** +Desc: Generate a random prefix, ensure that it is not defined anywhere + in the path. +*****************************************************************************/ +#if 0 +void F_Element::genPrefix( + FLMUNICODE * puzPrefix, + FLMUINT * puiPrefixChars + ) +{ + FLMUINT uiTmp; + FLMUINT uiPrefixChars; + FLMUNICODE * puzTmp; + F_Attribute * pAttr; + F_Element * pElement; + + puzPrefix [0] = 'p'; + puzPrefix [1] = 'r'; + puzPrefix [2] = 'f'; + puzPrefix [3] = 'x'; + for (;;) + { + + // Append the number in reverse digit order - it really doesn't matter + // because we're just trying to generate a unique prefix number. + + puzTmp = &puzPrefix [4]; + uiPrefixChars = 4; + uiTmp = *m_puiNextPrefixNum; + do + { + *puzTmp++ = (FLMUNICODE)((uiTmp % 10) + '0'); + uiPrefixChars++; + uiTmp /= 10; + } while (uiTmp); + + // See if the prefix is defined. + + pAttr = m_pFirstAttr; + pElement = this; + while (pAttr) + { + if (pAttr->m_bIsNamespaceDecl && + pAttr->m_uiNameChars > 6 && + pAttr->m_uiNameChars - 6 == uiPrefixChars && + f_memcmp( puzPrefix, &pAttr->m_puzName [6], + uiPrefixChars * sizeof( FLMUNICODE)) == 0) + { + break; + } + if ((pAttr = pAttr->m_pNext) == NULL) + { + pElement = pElement->m_pParentElement; + while (pElement && !pElement->m_pFirstAttr) + { + pElement = pElement->m_pParentElement; + } + if (!pElement) + { + break; + } + pAttr = pElement->m_pFirstAttr; + } + } + + // If the prefix was not defined, we can use it. + + if (!pAttr) + { + break; + } + (*m_puiNextPrefixNum)++; + } + puzPrefix [uiPrefixChars] = 0; + *puiPrefixChars = uiPrefixChars; +} +#endif + +/***************************************************************************** +Desc: Find a prefix for a namespace +*****************************************************************************/ +#if 0 +RCODE F_Element::findPrefix( + FLMUNICODE * puzNamespace, + FLMUINT uiNamespaceChars, + FLMBOOL bForElement, + FLMUNICODE ** ppuzPrefix, + FLMUINT * puiPrefixChars) +{ + RCODE rc = NE_FLM_OK; + F_Attribute * pAttr = m_pFirstAttr; + F_Element * pElement = this; + FLMUNICODE uzPrefix [50]; + FLMUINT uiPrefixChars; + + for (;;) + { + if ( pAttr) + { + if (pAttr->m_bIsNamespaceDecl && + uiNamespaceChars == pAttr->m_uiValueChars && + (!uiNamespaceChars || + f_memcmp( puzNamespace, pAttr->m_puzValue, + uiNamespaceChars * sizeof( FLMUNICODE)) == 0)) + { + + // Don't set the prefix if it is the default namespace. + + if (!pAttr->m_bDefaultNamespaceDecl) + { + // Prefix comes after the "xmlns:" + + *ppuzPrefix = &pAttr->m_puzName [6]; + *puiPrefixChars = pAttr->m_uiNameChars - 6; + goto Exit; + } + + // Default namespace is only OK for elements, + // but not attributes. We don't want to count + // attributes as having been "found" if they matched + // the default namespace. This routine is only called + // for attributes if the attribute namepace is non-empty. + + else if (bForElement) + { + goto Exit; + } + } + pAttr = pAttr->m_pNext; + } + if ( !pAttr) + { + pElement = pElement->m_pParentElement; + while (pElement && !pElement->m_pFirstAttr) + { + pElement = pElement->m_pParentElement; + } + if (!pElement) + { + break; + } + pAttr = pElement->m_pFirstAttr; + } + } + + // If namespaces is empty, the only declaration that is legal is + // a default namespace declaration. + + if (!uiNamespaceChars) + { + if (RC_BAD( rc = addNamespaceDecl( NULL, 0, NULL, 0, &pAttr))) + { + goto Exit; + } + } + else + { + + // Manufacture a prefix that is not used in the hierarchy yet. + + genPrefix( uzPrefix, &uiPrefixChars); + if (RC_BAD( rc = addNamespaceDecl( uzPrefix, uiPrefixChars, puzNamespace, + uiNamespaceChars, &pAttr))) + { + goto Exit; + } + + *ppuzPrefix = &pAttr->m_puzName [6]; + *puiPrefixChars = pAttr->m_uiNameChars - 6; + } + +Exit: + + return( rc); +} +#endif + +/***************************************************************************** +Desc: Output the element name, with its attributes - this marks the + beginning of the element. +*****************************************************************************/ +#if 0 +RCODE F_Element::outputElem( + IF_OStream * pOStream, + FLMBOOL bStartOfElement, + FLMBOOL bEndOfElement, + FLMBOOL bAddNewLine) +{ + RCODE rc = NE_FLM_OK; + F_Attribute * pAttr; + F_Attribute * pPrevAttr; + FLMUINT uiIndentCount = 0; + FLMBOOL bEndNode; + + bEndNode = ( m_bIsDocumentRoot && !bStartOfElement); + if( bAddNewLine && ( !m_bIsDocumentRoot || bEndNode)) + { + if (RC_BAD( rc = pOStream->write( (void *)"\n", 1))) + { + goto Exit; + } + for( uiIndentCount = 0; uiIndentCount < m_uiIndentCount; uiIndentCount++) + { + if (RC_BAD( rc = pOStream->write( (void *)"\t", 1))) + { + goto Exit; + } + } + } + + // Output the element name + if ( bStartOfElement) + { + + if (RC_BAD( rc = pOStream->write( (void *)"<", 1))) + { + goto Exit; + } + } + else + { + + if (RC_BAD( rc = pOStream->write( (void *)"write( (void *)":", 1))) + { + goto Exit; + } + } + + if (RC_BAD( rc = exportUniValue( pOStream, m_puzName, m_uiNameChars, FALSE, 0))) + { + goto Exit; + } + + if (bStartOfElement) + { + + // Output the attributes. As we go, remove any attributes that are + // not namespace declarations. They are not needed after this. + + pPrevAttr = NULL; + pAttr = m_pFirstAttr; + while (pAttr) + { + if (RC_BAD( rc = pAttr->outputAttr( pOStream))) + { + goto Exit; + } + + if (!pAttr->m_bIsNamespaceDecl) + { + if (pPrevAttr) + { + pPrevAttr->m_pNext = pAttr->m_pNext; + makeAttrAvail( pAttr); + pAttr = pPrevAttr->m_pNext; + } + else + { + m_pFirstAttr = pAttr->m_pNext; + makeAttrAvail( pAttr); + pAttr = m_pFirstAttr; + } + + // See if we deleted the last attribute in the list. + + if (!pAttr) + { + m_pLastAttr = pPrevAttr; + } + } + else + { + pPrevAttr = pAttr; + pAttr = pAttr->m_pNext; + } + } + } + + // Close out the element + if (RC_BAD( rc = (RCODE)(bStartOfElement && bEndOfElement + ? pOStream->write( (void *)"/>", 2) + : pOStream->write( (void *)">", 1)))) + { + goto Exit; + } + + if ( bAddNewLine && bEndNode) + { + if (RC_BAD( rc = pOStream->write( (void *)"\n", 1))) + { + goto Exit; + } + } + + +Exit: + + return( rc); +} +#endif + +/***************************************************************************** +Desc: Output Data that is contained on an element node. +*****************************************************************************/ +#if 0 +RCODE F_Element::outputLocalData( + IF_OStream * pOStream, + IF_DOMNode * pDbNode, + IF_Db * ifpDb, + eExportFormatType eFormatType, + FLMUINT uiIndentCount) +{ + RCODE rc = NE_FLM_OK; + FLMUNICODE uzTmpData [150]; + FLMUNICODE * puzData = &uzTmpData [0]; + FLMUINT uiDataBufSize = sizeof( uzTmpData); + FLMUINT uiChars; + + if (RC_BAD( rc = pDbNode->getUnicodeChars( ifpDb, &uiChars))) + { + goto Exit; + } + + + if (uiDataBufSize < (uiChars + 1) * sizeof( FLMUNICODE)) + { + FLMUNICODE * puzNew; + + if (RC_BAD( rc = f_alloc( (uiChars + 1) * sizeof( FLMUNICODE), + &puzNew))) + { + goto Exit; + } + if (puzData != &uzTmpData [0]) + { + f_free( &puzData); + } + puzData = puzNew; + uiDataBufSize = (uiChars + 1) * sizeof( FLMUNICODE); + } + if (RC_BAD( rc = pDbNode->getUnicode( ifpDb, puzData, + uiDataBufSize, 0, uiChars, &uiChars))) + { + goto Exit; + } + + // Output the value. + if (RC_BAD( rc = exportUniValue( pOStream, puzData, uiChars, TRUE, + eFormatType >= FLM_EXPORT_INDENT_DATA ? uiIndentCount : 0))) + { + goto Exit; + } + +Exit: + + return ( rc); +} +#endif + +/***************************************************************************** +Desc: Outputs a UTF8 stream of XML, starting at the specified node. Node and + all of its descendant nodes are output. +*****************************************************************************/ +#if 0 +RCODE XFLMAPI F_Db::exportXML( + IF_DOMNode * pStartNode, + IF_OStream * pOStream, + eExportFormatType eFormatType) +{ + RCODE rc = NE_FLM_OK; + F_Element * pAvailElements = NULL; + F_Element * pTmpElement; + F_Attribute * pAvailAttrs = NULL; + F_Attribute * pTmpAttr; + FLMUNICODE uzTmpData [150]; + FLMUNICODE * puzData = &uzTmpData [0]; + FLMUINT uiDataBufSize = sizeof( uzTmpData); + IF_DOMNode * pDbNode = NULL; + eDomNodeType ePrevNodeType; + F_Element * pCurrElement = NULL; + FLMUINT uiNextPrefixNum = 0; + FLMBOOL bStartOfDocument = TRUE; + FLMBOOL bShouldFormat = FALSE; + FLMBOOL bIsDataLocal = FALSE; + FLMUINT uiIndentCount = 0; + FLMUINT uiICount = 0; + + // This routine should only be called if the node type is element node. + + flmAssert( pStartNode->getNodeType() == ELEMENT_NODE); + + ePrevNodeType = ELEMENT_NODE; + pDbNode = pStartNode; + pDbNode->AddRef(); + + for (;;) + { + // Output the current node, depending on its type. + + if( pDbNode->getNodeType() == ELEMENT_NODE) + { + if (pAvailElements) + { + pTmpElement = pAvailElements; + pAvailElements = pAvailElements->getNext(); + pTmpElement->reset( pCurrElement, &pAvailAttrs, &uiNextPrefixNum); + } + else + { + if ((pTmpElement = f_new F_Element( pCurrElement, &pAvailAttrs, + &uiNextPrefixNum)) == NULL) + { + rc = RC_SET( NE_FLM_MEM); + goto Exit; + } + } + + pCurrElement = pTmpElement; + + if (RC_BAD( rc = pCurrElement->setupElement( (IF_Db *)this, pDbNode))) + { + goto Exit; + } + + if( eFormatType >= FLM_EXPORT_INDENT) + { + pCurrElement->setIndentCount(uiIndentCount); + } + + if( pDbNode == pStartNode) + { + pCurrElement->setDocumentRoot( TRUE); + } + + // Only want a New Line and tabs for Element if: + // 1) New Line format is indicated + // 2) Previous Element Was NOT Data + bShouldFormat = ( (eFormatType >= FLM_EXPORT_NEW_LINE) && + (ePrevNodeType != DATA_NODE)) + ? TRUE + : FALSE; + + if( RC_BAD( rc = pDbNode->isDataLocalToNode( (IF_Db *)this, + &bIsDataLocal))) + { + goto Exit; + } + + if( bIsDataLocal) + { + if( RC_BAD( rc = pCurrElement->outputElem( pOStream, + TRUE, FALSE, bShouldFormat))) + { + goto Exit; + } + + pCurrElement->outputLocalData( pOStream, + pDbNode, + (IF_Db *)this, + eFormatType, + uiIndentCount); + + } + + if( RC_OK( rc = pDbNode->getFirstChild( (IF_Db *)this, &pDbNode))) + { + if( !bIsDataLocal && RC_BAD( rc = pCurrElement->outputElem( + pOStream, TRUE, FALSE, bShouldFormat))) + { + goto Exit; + } + + bStartOfDocument = FALSE; + uiIndentCount++; + ePrevNodeType = ELEMENT_NODE; + continue; + } + + if( rc != NE_FLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + // Write out the "/>" for the element, because it had no + // child nodes. + + if( bIsDataLocal) + { + if( RC_BAD( rc = pCurrElement->outputElem( pOStream, + FALSE, TRUE, bShouldFormat))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pCurrElement->outputElem( pOStream, + TRUE, TRUE, bShouldFormat))) + { + goto Exit; + } + } + + // We are now done with this element + + ePrevNodeType = ELEMENT_NODE; + pTmpElement = pCurrElement; + pCurrElement = pCurrElement->getParentElement(); + pTmpElement->makeAvail( &pAvailElements); + + if( !pCurrElement) + { + break; + } + +Get_Element_Sibling: + + // See if we have a sibling. Go up tree until we find + // a node that has a sibling. + + for( ;;) + { + if( RC_OK( rc = pDbNode->getNextSibling( (IF_Db *)this, &pDbNode))) + { + break; + } + + if( rc != NE_FLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + // Need to close previous element + + if( uiIndentCount) + { + uiIndentCount--; + } + + if( RC_BAD( rc = pCurrElement->outputElem( pOStream, FALSE, TRUE, + eFormatType >= FLM_EXPORT_NEW_LINE + ? TRUE + : FALSE))) + { + goto Exit; + } + + if( RC_BAD( rc = pDbNode->getParentNode( (IF_Db *)this, &pDbNode))) + { + if( rc == NE_FLM_DOM_NODE_NOT_FOUND) + { + // There should be a parent node at this point! + + rc = RC_SET_AND_ASSERT( NE_FLM_DATA_ERROR); + } + + goto Exit; + } + + pTmpElement = pCurrElement; + pCurrElement = pCurrElement->getParentElement(); + pTmpElement->makeAvail( &pAvailElements); + + if( !pCurrElement) + { + pDbNode->Release(); + pDbNode = NULL; + goto Exit; + } + } + } + else + { + // Only output data, comment, and cdata nodes. + + if( pDbNode->getNodeType() == DATA_NODE || + pDbNode->getNodeType() == COMMENT_NODE || + pDbNode->getNodeType() == CDATA_SECTION_NODE) + { + FLMUINT uiChars; + + if( RC_BAD( rc = pDbNode->getUnicodeChars( (IF_Db *)this, + &uiChars))) + { + goto Exit; + } + + if( uiDataBufSize < (uiChars + 1) * sizeof( FLMUNICODE)) + { + FLMUNICODE * puzNew; + + if( RC_BAD( rc = f_alloc( (uiChars + 1) * sizeof( FLMUNICODE), + &puzNew))) + { + goto Exit; + } + + if( puzData != &uzTmpData [0]) + { + f_free( &puzData); + } + + puzData = puzNew; + uiDataBufSize = (uiChars + 1) * sizeof( FLMUNICODE); + } + + if( RC_BAD( rc = pDbNode->getUnicode( (IF_Db *)this, puzData, + uiDataBufSize, 0, uiChars, &uiChars))) + { + goto Exit; + } + + if( pDbNode->getNodeType() == DATA_NODE) + { + // Output the value + + if (RC_BAD( rc = exportUniValue( pOStream, puzData, uiChars, + TRUE, eFormatType >= FLM_EXPORT_INDENT_DATA + ? uiIndentCount + : 0))) + { + goto Exit; + } + + ePrevNodeType = DATA_NODE; + } + else if( pDbNode->getNodeType() == COMMENT_NODE) + { + //If Comment Node follows Data Node do not add new line + + if( eFormatType >= FLM_EXPORT_INDENT_DATA && + ePrevNodeType != DATA_NODE) + { + if (RC_BAD( rc = pOStream->write( (void *)"\n", 1))) + { + goto Exit; + } + + for( uiICount = 0; uiICount < uiIndentCount; uiICount++) + { + if (RC_BAD( rc = pOStream->write( (void *)"\t", 1))) + { + goto Exit; + } + } + } + + // Output the beginning of a comment + + if (RC_BAD( rc = pOStream->write( (void *)"", 3))) + { + goto Exit; + } + + ePrevNodeType = COMMENT_NODE; + } + else + { + // Output the beginning of a cdata section + + if( RC_BAD( rc = pOStream->write( (void *)"write( (void *)"]]>", 3))) + { + goto Exit; + } + + ePrevNodeType = CDATA_SECTION_NODE; + } + } + + // Have a data node, or comment node probably + // In any case, see if there are any sibling nodes. + // If not, go back to enclosing element node. + + if( RC_OK( rc = pDbNode->getNextSibling( (IF_Db *)this, &pDbNode))) + { + continue; + } + + if( rc != NE_FLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + // Go back up to enclosing element + + if( RC_BAD( rc = pDbNode->getParentNode( (IF_Db *)this, &pDbNode))) + { + // There better be a parent node or we have a corruption! + + if( rc == NE_FLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET( NE_FLM_DATA_ERROR); + } + + goto Exit; + } + + // Parent node better be an element + + if( pDbNode->getNodeType() != ELEMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_FLM_DATA_ERROR); + goto Exit; + } + + // If we were traversing the attributes of an element, + // we need to now go back and get its child nodes. + + // Write out the for the element + + if( RC_BAD( rc = pCurrElement->outputElem( pOStream, + FALSE, TRUE, FALSE))) + { + goto Exit; + } + + // We are now done with this element + + if( uiIndentCount) + { + uiIndentCount--; + } + + ePrevNodeType = ELEMENT_NODE; + pTmpElement = pCurrElement; + pCurrElement = pCurrElement->getParentElement(); + pTmpElement->makeAvail( &pAvailElements); + + if( !pCurrElement) + { + break; + } + + goto Get_Element_Sibling; + } + } + +Exit: + + if( puzData != &uzTmpData [0]) + { + f_free( &puzData); + } + + while( pCurrElement) + { + pTmpElement = pCurrElement; + pCurrElement = pCurrElement->getParentElement(); + delete pTmpElement; + } + + while( pAvailElements) + { + pTmpElement = pAvailElements; + pAvailElements = pAvailElements->getNext(); + delete pTmpElement; + } + + while( pAvailAttrs) + { + pTmpAttr = pAvailAttrs; + pAvailAttrs = pAvailAttrs->getNext(); + delete pTmpAttr; + } + + if( pDbNode) + { + pDbNode->Release(); + } + + return( rc); +} +#endif